Merge m-c to autoland, a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 13 Mar 2017 16:40:18 -0700
changeset 395518 6b1862025a25525ce9ebcd044afa91269d1d4ee5
parent 395517 4bc69004c3f66ea17dcc06e1c9e1a5659d27d82d (current diff)
parent 395461 6d38ad302429c98115c354d643e81987ecec5d3c (diff)
child 395519 41c24766c4ce0e2f98ae6a0a3b2a7d294e6a4544
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.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: Hikpu6ARByb
gfx/webrender_traits/build.rs
gfx/webrender_traits/src/stacking_context.rs
gfx/webrender_traits/src/types.rs
media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
third_party/rust/serde_codegen/.cargo-checksum.json
third_party/rust/serde_codegen/.cargo-ok
third_party/rust/serde_codegen/Cargo.toml
third_party/rust/serde_codegen/src/bound.rs
third_party/rust/serde_codegen/src/de.rs
third_party/rust/serde_codegen/src/lib.rs
third_party/rust/serde_codegen/src/ser.rs
third_party/rust/servo-dwrote/.cargo-checksum.json
third_party/rust/servo-dwrote/.cargo-ok
third_party/rust/servo-dwrote/.gitignore
third_party/rust/servo-dwrote/Cargo.toml
third_party/rust/servo-dwrote/README.md
third_party/rust/servo-dwrote/build.rs
third_party/rust/servo-dwrote/src/bitmap_render_target.rs
third_party/rust/servo-dwrote/src/com_helpers.rs
third_party/rust/servo-dwrote/src/comptr.rs
third_party/rust/servo-dwrote/src/font.rs
third_party/rust/servo-dwrote/src/font_collection.rs
third_party/rust/servo-dwrote/src/font_face.rs
third_party/rust/servo-dwrote/src/font_family.rs
third_party/rust/servo-dwrote/src/font_file.rs
third_party/rust/servo-dwrote/src/font_file_loader_impl.rs
third_party/rust/servo-dwrote/src/gdi_interop.rs
third_party/rust/servo-dwrote/src/glyph_run_analysis.rs
third_party/rust/servo-dwrote/src/helpers.rs
third_party/rust/servo-dwrote/src/lib.rs
third_party/rust/servo-dwrote/src/rendering_params.rs
third_party/rust/servo-dwrote/src/test.rs
third_party/rust/servo-dwrote/src/types.rs
third_party/rust/syn-0.10.8/.cargo-checksum.json
third_party/rust/syn-0.10.8/.cargo-ok
third_party/rust/syn-0.10.8/Cargo.toml
third_party/rust/syn-0.10.8/src/aster/generics.rs
third_party/rust/syn-0.10.8/src/aster/ident.rs
third_party/rust/syn-0.10.8/src/aster/invoke.rs
third_party/rust/syn-0.10.8/src/aster/lifetime.rs
third_party/rust/syn-0.10.8/src/aster/mod.rs
third_party/rust/syn-0.10.8/src/aster/path.rs
third_party/rust/syn-0.10.8/src/aster/qpath.rs
third_party/rust/syn-0.10.8/src/aster/ty.rs
third_party/rust/syn-0.10.8/src/aster/ty_param.rs
third_party/rust/syn-0.10.8/src/aster/where_predicate.rs
third_party/rust/syn-0.10.8/src/attr.rs
third_party/rust/syn-0.10.8/src/constant.rs
third_party/rust/syn-0.10.8/src/data.rs
third_party/rust/syn-0.10.8/src/escape.rs
third_party/rust/syn-0.10.8/src/expr.rs
third_party/rust/syn-0.10.8/src/generics.rs
third_party/rust/syn-0.10.8/src/helper.rs
third_party/rust/syn-0.10.8/src/ident.rs
third_party/rust/syn-0.10.8/src/item.rs
third_party/rust/syn-0.10.8/src/krate.rs
third_party/rust/syn-0.10.8/src/lib.rs
third_party/rust/syn-0.10.8/src/lit.rs
third_party/rust/syn-0.10.8/src/mac.rs
third_party/rust/syn-0.10.8/src/macro_input.rs
third_party/rust/syn-0.10.8/src/nom.rs
third_party/rust/syn-0.10.8/src/op.rs
third_party/rust/syn-0.10.8/src/registry.rs
third_party/rust/syn-0.10.8/src/space.rs
third_party/rust/syn-0.10.8/src/ty.rs
third_party/rust/syn-0.10.8/src/visit.rs
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -1129,20 +1129,22 @@ nsContextMenu.prototype = {
       };
       mm.addMessageListener("ContextMenu:Canvas:ToBlobURL:Result", onMessage);
     });
   },
 
   // Change current window to the URL of the image, video, or audio.
   viewMedia: function(e) {
     let referrerURI = gContextMenuContentData.documentURIObject;
+    let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
     if (this.onCanvas) {
       this._canvasToBlobURL(this.target).then(function(blobURL) {
         openUILink(blobURL, e, { disallowInheritPrincipal: true,
-                                 referrerURI: referrerURI });
+                                 referrerURI: referrerURI,
+                                 originPrincipal: systemPrincipal});
       }, Cu.reportError);
     }
     else {
       urlSecurityCheck(this.mediaURL,
                        this.browser.contentPrincipal,
                        Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
       openUILink(this.mediaURL, e, { disallowInheritPrincipal: true,
                                      referrerURI: referrerURI });
--- a/browser/base/content/test/general/browser_selectpopup.js
+++ b/browser/base/content/test/general/browser_selectpopup.js
@@ -589,22 +589,21 @@ function* performLargePopupTests(win) {
 
   scrollPos = selectPopup.scrollBox.scrollTop;
   EventUtils.synthesizeMouseAtPoint(popupRect.left + 20, popupRect.bottom + 25, { type: "mouseup" }, win);
   is(selectPopup.scrollBox.scrollTop, scrollPos, "scroll position at mouseup from option should not change");
 
   EventUtils.synthesizeMouseAtPoint(popupRect.left + 20, popupRect.bottom + 20, { type: "mousemove" }, win);
   is(selectPopup.scrollBox.scrollTop, scrollPos, "scroll position at mousemove after mouseup should not change");
 
-
   yield hideSelectPopup(selectPopup, "escape", win);
 
   let positions = [
     "margin-top: 300px;",
-    "position: fixed; bottom: 100px;",
+    "position: fixed; bottom: 200px;",
     "width: 100%; height: 9999px;"
   ];
 
   let position;
   while (positions.length) {
     yield openSelectPopup(selectPopup, "key", "select", win);
 
     let rect = selectPopup.getBoundingClientRect();
@@ -639,18 +638,63 @@ function* performLargePopupTests(win) {
     let contentPainted = BrowserTestUtils.contentPainted(browser);
     yield ContentTask.spawn(browser, position, function*(contentPosition) {
       let select = content.document.getElementById("one");
       select.setAttribute("style", contentPosition || "");
       select.getBoundingClientRect();
     });
     yield contentPainted;
   }
+
+  if (navigator.platform.indexOf("Mac") == 0) {
+    yield ContentTask.spawn(browser, null, function*() {
+      let doc = content.document;
+      doc.body.style = "padding-top: 400px;"
+
+      let select = doc.getElementById("one");
+      select.options[41].selected = true;
+      select.focus();
+    });
+
+    yield openSelectPopup(selectPopup, "key", "select", win);
+
+    ok(selectPopup.getBoundingClientRect().top > browser.getBoundingClientRect().top,
+       "select popup appears over selected item");
+
+    yield hideSelectPopup(selectPopup, "escape", win);
+  }
 }
 
+// This test checks select elements with a large number of options to ensure that
+// the popup appears within the browser area.
+add_task(function* test_large_popup() {
+  const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
+
+  yield* performLargePopupTests(window);
+
+  yield BrowserTestUtils.removeTab(tab);
+});
+
+// This test checks the same as the previous test but in a new smaller window.
+add_task(function* test_large_popup_in_small_window() {
+  let newwin = yield BrowserTestUtils.openNewBrowserWindow({ width: 400, height: 400 });
+
+  const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
+  let browserLoadedPromise = BrowserTestUtils.browserLoaded(newwin.gBrowser.selectedBrowser);
+  yield BrowserTestUtils.loadURI(newwin.gBrowser.selectedBrowser, pageUrl);
+  yield browserLoadedPromise;
+
+  newwin.gBrowser.selectedBrowser.focus();
+
+  yield* performLargePopupTests(newwin);
+
+  yield BrowserTestUtils.closeWindow(newwin);
+});
+
 function* performSelectSearchTests(win) {
   let browser = win.gBrowser.selectedBrowser;
   yield ContentTask.spawn(browser, null, function*() {
     let doc = content.document;
     let select = doc.getElementById("one");
 
     for (var i = 0; i < 40; i++) {
       select.add(new content.Option("Test" + i));
@@ -713,43 +757,16 @@ add_task(function* test_select_search() 
 
   yield performSelectSearchTests(window);
 
   yield BrowserTestUtils.removeTab(tab);
 
   yield SpecialPowers.popPrefEnv();
 });
 
-// This test checks select elements with a large number of options to ensure that
-// the popup appears within the browser area.
-add_task(function* test_large_popup() {
-  const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
-  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
-
-  yield* performLargePopupTests(window);
-
-  yield BrowserTestUtils.removeTab(tab);
-});
-
-// This test checks the same as the previous test but in a new smaller window.
-add_task(function* test_large_popup_in_small_window() {
-  let newwin = yield BrowserTestUtils.openNewBrowserWindow({ width: 400, height: 400 });
-
-  const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
-  let browserLoadedPromise = BrowserTestUtils.browserLoaded(newwin.gBrowser.selectedBrowser);
-  yield BrowserTestUtils.loadURI(newwin.gBrowser.selectedBrowser, pageUrl);
-  yield browserLoadedPromise;
-
-  newwin.gBrowser.selectedBrowser.focus();
-
-  yield* performLargePopupTests(newwin);
-
-  yield BrowserTestUtils.closeWindow(newwin);
-});
-
 // This test checks that a mousemove event is fired correctly at the menu and
 // not at the browser, ensuring that any mouse capture has been cleared.
 add_task(function* test_mousemove_correcttarget() {
   const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
 
   let selectPopup = document.getElementById("ContentSelectDropdown").menupopup;
 
--- a/devtools/server/actors/layout.js
+++ b/devtools/server/actors/layout.js
@@ -93,16 +93,20 @@ var LayoutActor = ActorClassWithSpec(lay
    *
    * @param  {Node|NodeActor} rootNode
    *         The root node to start iterating at.
    * @return {Array} An array of GridActor objects.
    */
   getGrids: function (rootNode) {
     let grids = [];
 
+    if (!rootNode) {
+      return grids;
+    }
+
     let treeWalker = this.walker.getDocumentWalker(rootNode);
     while (treeWalker.nextNode()) {
       let currentNode = treeWalker.currentNode;
 
       if (currentNode.getGridFragments && currentNode.getGridFragments().length > 0) {
         let gridActor = new GridActor(this, currentNode);
         grids.push(gridActor);
       }
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -1193,18 +1193,26 @@ TimeoutManager::Resume()
 }
 
 void
 TimeoutManager::Freeze()
 {
   MOZ_LOG(gLog, LogLevel::Debug,
           ("Freeze(TimeoutManager=%p)\n", this));
 
+  DebugOnly<bool> _seenDummyTimeout = false;
+
   TimeStamp now = TimeStamp::Now();
   ForEachUnorderedTimeout([&](Timeout* aTimeout) {
+    if (!aTimeout->mWindow) {
+      NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
+      _seenDummyTimeout = true;
+      return;
+    }
+
     // Save the current remaining time for this timeout.  We will
     // re-apply it when the window is Thaw()'d.  This effectively
     // shifts timers to the right as if time does not pass while
     // the window is frozen.
     TimeDuration delta(0);
     if (aTimeout->When() > now) {
       delta = aTimeout->When() - now;
     }
--- a/dom/canvas/test/reftest/reftest.list
+++ b/dom/canvas/test/reftest/reftest.list
@@ -44,75 +44,75 @@ skip-if(Android) == webgl-capturestream-
 skip-if(Android)                                                                                                        == webgl-color-test.html?frame=1&__&________&_______&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                                                        == webgl-color-test.html?frame=1&aa&________&_______&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                                                        == webgl-color-test.html?frame=1&__&preserve&_______&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                                                        == webgl-color-test.html?frame=1&aa&preserve&_______&_____ wrapper.html?colors-no-alpha.png
 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)                                                                                                        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
+fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android)                                       fails-if(webrender) == webgl-color-test.html?frame=1&__&________&_______&alpha wrapper.html?colors-non-premult.png
+fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android)                                       fails-if(webrender) == 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)                                       fails-if(webrender) == 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)                                       fails-if(webrender) == 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)                                                                                                        == 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)                                                                                                        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
+fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android)                                       fails-if(webrender) == webgl-color-test.html?frame=6&__&________&_______&alpha wrapper.html?colors-non-premult.png
+fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android)                                       fails-if(webrender) == 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)                                       fails-if(webrender) == 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)                                       fails-if(webrender) == 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)                                                                 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) 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
+fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android) pref(webgl.force-layers-readback,true) fails-if(webrender) == 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) fails-if(webrender) == 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) fails-if(webrender) == 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) fails-if(webrender) == 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) == 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) 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
+fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android) pref(webgl.force-layers-readback,true) fails-if(webrender) == 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) fails-if(webrender) == 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) fails-if(webrender) == 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) fails-if(webrender) == 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
 
 # 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,17 +152,17 @@ 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 fails-if(webrender) == drawFocusIfNeeded.html drawFocusIfNeeded-ref.html
+pref(canvas.focusring.enabled,true) skip-if(cocoaWidget) skip-if(winWidget) needs-focus == 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
 
 # Canvas Filter Reftests
--- 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.
-fails-if(webrender) == bug1228601-video-rotation-90.html bug1228601-video-rotated-ref.html
+== bug1228601-video-rotation-90.html bug1228601-video-rotated-ref.html
--- a/dom/media/platforms/wmf/DXVA2Manager.cpp
+++ b/dom/media/platforms/wmf/DXVA2Manager.cpp
@@ -878,17 +878,16 @@ D3D11DXVA2Manager::CreateOutputSample(Re
 
 HRESULT
 D3D11DXVA2Manager::CopyToImage(IMFSample* aVideoSample,
                                const nsIntRect& aRegion,
                                Image** aOutImage)
 {
   NS_ENSURE_TRUE(aVideoSample, E_POINTER);
   NS_ENSURE_TRUE(aOutImage, E_POINTER);
-  NS_ENSURE_TRUE(mSyncObject, E_FAIL);
   MOZ_ASSERT(mTextureClientAllocator);
 
   RefPtr<D3D11ShareHandleImage> image =
     new D3D11ShareHandleImage(gfx::IntSize(mWidth, mHeight), aRegion);
   bool ok = image->AllocateTexture(mTextureClientAllocator, mDevice);
   NS_ENSURE_TRUE(ok, E_FAIL);
 
   RefPtr<TextureClient> client = image->GetTextureClient(ImageBridgeChild::GetSingleton().get());
@@ -898,16 +897,18 @@ D3D11DXVA2Manager::CopyToImage(IMFSample
   HRESULT hr;
   RefPtr<ID3D11Texture2D> texture = image->GetTexture();
 
   texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
 
   if (mutex) {
     hr = mutex->AcquireSync(0, 2000);
     NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+  } else {
+    NS_ENSURE_TRUE(mSyncObject, E_FAIL);
   }
 
   if (client && client->GetFormat() == SurfaceFormat::NV12) {
     // Our video frame is stored in a non-sharable ID3D11Texture2D. We need
     // to create a copy of that frame as a sharable resource, save its share
     // handle, and put that handle into the rendering pipeline.
 
     RefPtr<IMFMediaBuffer> buffer;
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -568,16 +568,18 @@ private:
   int mChannel;
   MOZ_INIT_OUTSIDE_CTOR TrackID mTrackID;
   bool mStarted;
 
   nsString mDeviceName;
   nsCString mDeviceUUID;
 
   int32_t mSampleFrequency;
+  uint64_t mTotalFrames;
+  uint64_t mLastLogFrames;
   int32_t mPlayoutDelay;
 
   NullTransport *mNullTransport;
 
   nsTArray<int16_t> mInputBuffer;
   // mSkipProcessing is true if none of the processing passes are enabled,
   // because of prefs or constraints. This allows simply copying the audio into
   // the MSG, skipping resampling and the whole webrtc.org code.
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -35,16 +35,21 @@ namespace mozilla {
 #ifdef LOG
 #undef LOG
 #endif
 
 extern LogModule* GetMediaManagerLog();
 #define LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg)
 #define LOG_FRAMES(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Verbose, msg)
 
+LogModule* AudioLogModule() {
+  static mozilla::LazyLogModule log("AudioLatency");
+  return static_cast<LogModule*>(log);
+}
+
 /**
  * Webrtc microphone source source.
  */
 NS_IMPL_ISUPPORTS0(MediaEngineWebRTCMicrophoneSource)
 NS_IMPL_ISUPPORTS0(MediaEngineWebRTCAudioCaptureSource)
 
 // XXX temp until MSG supports registration
 StaticRefPtr<AudioOutputObserver> gFarendObserver;
@@ -193,16 +198,18 @@ MediaEngineWebRTCMicrophoneSource::Media
   , mVoiceEngine(aVoiceEnginePtr)
   , mAudioInput(aAudioInput)
   , mMonitor("WebRTCMic.Monitor")
   , mCapIndex(aIndex)
   , mChannel(-1)
   , mTrackID(TRACK_NONE)
   , mStarted(false)
   , mSampleFrequency(MediaEngine::DEFAULT_SAMPLE_RATE)
+  , mTotalFrames(0)
+  , mLastLogFrames(0)
   , mPlayoutDelay(0)
   , mNullTransport(nullptr)
   , mSkipProcessing(false)
 {
   MOZ_ASSERT(aVoiceEnginePtr);
   MOZ_ASSERT(aAudioInput);
   mDeviceName.Assign(NS_ConvertUTF8toUTF16(name));
   mDeviceUUID.Assign(uuid);
@@ -573,16 +580,26 @@ void
 MediaEngineWebRTCMicrophoneSource::InsertInGraph(const T* aBuffer,
                                                  size_t aFrames,
                                                  uint32_t aChannels)
 {
   if (mState != kStarted) {
     return;
   }
 
+  if (MOZ_LOG_TEST(AudioLogModule(), LogLevel::Debug)) {
+    mTotalFrames += aFrames;
+    if (mTotalFrames > mLastLogFrames + mSampleFrequency) { // ~ 1 second
+      MOZ_LOG(AudioLogModule(), LogLevel::Debug,
+              ("%p: Inserting %" PRIuSIZE " samples into graph, total frames = %" PRIu64,
+               (void*)this, aFrames, mTotalFrames));
+      mLastLogFrames = mTotalFrames;
+    }
+  }
+
   size_t len = mSources.Length();
   for (size_t i = 0; i < len; i++) {
     if (!mSources[i]) {
       continue;
     }
     RefPtr<SharedBuffer> buffer =
       SharedBuffer::Create(aFrames * aChannels * sizeof(T));
     PodCopy(static_cast<T*>(buffer->Data()),
--- a/dom/plugins/test/reftest/reftest.list
+++ b/dom/plugins/test/reftest/reftest.list
@@ -1,27 +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) fails-if(webrender) == plugin-alpha-zindex.html div-alpha-zindex.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) 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) 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-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
 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
+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
 # 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) 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
+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
 random-if(!haveTestPlugin) == plugin-transform-1.html plugin-transform-1-ref.html
 fails-if(!haveTestPlugin) == 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/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -74,9 +74,9 @@ there is another crate in m-c called moz
 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: 501e3d79c8a3019762bd8bd2d00eecf7811a84de
+Latest Commit: e30fb2914928c0e596d8632ed234647c0fd1492e
--- a/gfx/layers/CompositorTypes.h
+++ b/gfx/layers/CompositorTypes.h
@@ -64,20 +64,22 @@ enum class TextureFlags : uint32_t {
   // to it again.
   IMMEDIATE_UPLOAD   = 1 << 10,
   // The texture is part of a component-alpha pair
   COMPONENT_ALPHA    = 1 << 11,
   // The texture is being allocated for a compositor that no longer exists.
   // This flag is only used in the parent process.
   INVALID_COMPOSITOR = 1 << 12,
   // The texture was created by converting from YCBCR to RGB
-  RGB_FROM_YCBCR = 1 << 13,
+  RGB_FROM_YCBCR     = 1 << 13,
+  // The texture is used for snapshot.
+  SNAPSHOT           = 1 << 14,
 
   // OR union of all valid bits
-  ALL_BITS           = (1 << 14) - 1,
+  ALL_BITS           = (1 << 15) - 1,
   // the default flags
   DEFAULT = NO_FLAGS
 };
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TextureFlags)
 
 static inline bool
 TextureRequiresLocking(TextureFlags aFlags)
 {
--- a/gfx/layers/basic/TextureHostBasic.cpp
+++ b/gfx/layers/basic/TextureHostBasic.cpp
@@ -12,22 +12,23 @@ using namespace mozilla::gl;
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace layers {
 
 already_AddRefed<TextureHost>
 CreateTextureHostBasic(const SurfaceDescriptor& aDesc,
                        ISurfaceAllocator* aDeallocator,
+                       LayersBackend aBackend,
                        TextureFlags aFlags)
 {
 #ifdef XP_MACOSX
   if (aDesc.type() == SurfaceDescriptor::TSurfaceDescriptorMacIOSurface) {
     const SurfaceDescriptorMacIOSurface& desc =
       aDesc.get_SurfaceDescriptorMacIOSurface();
     return MakeAndAddRef<MacIOSurfaceTextureHostBasic>(aFlags, desc);
   }
 #endif
-  return CreateBackendIndependentTextureHost(aDesc, aDeallocator, aFlags);
+  return CreateBackendIndependentTextureHost(aDesc, aDeallocator, aBackend, aFlags);
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/composite/CompositableHost.h
+++ b/gfx/layers/composite/CompositableHost.h
@@ -38,16 +38,17 @@ class DataSourceSurface;
 namespace layers {
 
 class Layer;
 class LayerComposite;
 class Compositor;
 class ThebesBufferData;
 class TiledContentHost;
 class CompositableParentManager;
+class WebRenderImageHost;
 struct EffectChain;
 
 struct ImageCompositeNotificationInfo {
   base::ProcessId mImageBridgeProcessId;
   ImageCompositeNotification mNotification;
 };
 
 struct AsyncCompositableRef
@@ -143,16 +144,17 @@ public:
   {
     return mCompositor;
   }
 
   Layer* GetLayer() const { return mLayer; }
   void SetLayer(Layer* aLayer) { mLayer = aLayer; }
 
   virtual TiledContentHost* AsTiledContentHost() { return nullptr; }
+  virtual WebRenderImageHost* AsWebRenderImageHost() { return nullptr; }
 
   typedef uint32_t AttachFlags;
   static const AttachFlags NO_FLAGS = 0;
   static const AttachFlags ALLOW_REATTACH = 1;
   static const AttachFlags KEEP_ATTACHED = 2;
   static const AttachFlags FORCE_DETACH = 2;
 
   virtual void Attach(Layer* aLayer,
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -4,27 +4,29 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "TextureHost.h"
 
 #include "CompositableHost.h"           // for CompositableHost
 #include "LayerScope.h"
 #include "LayersLogging.h"              // for AppendToString
 #include "mozilla/gfx/2D.h"             // for DataSourceSurface, Factory
+#include "mozilla/gfx/gfxVars.h"
 #include "mozilla/ipc/Shmem.h"          // for Shmem
 #include "mozilla/layers/CompositableTransactionParent.h" // for CompositableParentManager
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor, etc
 #include "mozilla/layers/TextureHostBasic.h"
 #include "mozilla/layers/TextureHostOGL.h"  // for TextureHostOGL
 #include "mozilla/layers/ImageDataSerializer.h"
 #include "mozilla/layers/TextureClient.h"
 #include "mozilla/layers/GPUVideoTextureHost.h"
+#include "mozilla/layers/WebRenderTextureHost.h"
 #include "nsAString.h"
 #include "mozilla/RefPtr.h"                   // for nsRefPtr
 #include "nsPrintfCString.h"            // for nsPrintfCString
 #include "mozilla/layers/PTextureParent.h"
 #include "mozilla/Unused.h"
 #include <limits>
 #include "../opengl/CompositorOGL.h"
 #include "gfxPrefs.h"
@@ -155,94 +157,115 @@ void
 TextureHost::SetLastFwdTransactionId(uint64_t aTransactionId)
 {
   MOZ_ASSERT(mFwdTransactionId <= aTransactionId);
   mFwdTransactionId = aTransactionId;
 }
 
 // implemented in TextureHostOGL.cpp
 already_AddRefed<TextureHost> CreateTextureHostOGL(const SurfaceDescriptor& aDesc,
-                                               ISurfaceAllocator* aDeallocator,
-                                               TextureFlags aFlags);
+                                                   ISurfaceAllocator* aDeallocator,
+                                                   LayersBackend aBackend,
+                                                   TextureFlags aFlags);
 
 // implemented in TextureHostBasic.cpp
 already_AddRefed<TextureHost> CreateTextureHostBasic(const SurfaceDescriptor& aDesc,
-                                                 ISurfaceAllocator* aDeallocator,
-                                                 TextureFlags aFlags);
+                                                     ISurfaceAllocator* aDeallocator,
+                                                     LayersBackend aBackend,
+                                                     TextureFlags aFlags);
 
 // implemented in TextureD3D11.cpp
 already_AddRefed<TextureHost> CreateTextureHostD3D11(const SurfaceDescriptor& aDesc,
-                                                 ISurfaceAllocator* aDeallocator,
-                                                 TextureFlags aFlags);
+                                                     ISurfaceAllocator* aDeallocator,
+                                                     LayersBackend aBackend,
+                                                     TextureFlags aFlags);
 
 already_AddRefed<TextureHost>
 TextureHost::Create(const SurfaceDescriptor& aDesc,
                     ISurfaceAllocator* aDeallocator,
                     LayersBackend aBackend,
                     TextureFlags aFlags)
 {
   switch (aDesc.type()) {
     case SurfaceDescriptor::TSurfaceDescriptorBuffer:
     case SurfaceDescriptor::TSurfaceDescriptorDIB:
     case SurfaceDescriptor::TSurfaceDescriptorFileMapping:
     case SurfaceDescriptor::TSurfaceDescriptorGPUVideo:
-      return CreateBackendIndependentTextureHost(aDesc, aDeallocator, aFlags);
+      return CreateBackendIndependentTextureHost(aDesc, aDeallocator, aBackend, aFlags);
 
     case SurfaceDescriptor::TEGLImageDescriptor:
     case SurfaceDescriptor::TSurfaceTextureDescriptor:
     case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture:
-      return CreateTextureHostOGL(aDesc, aDeallocator, aFlags);
+      return CreateTextureHostOGL(aDesc, aDeallocator, aBackend, aFlags);
 
     case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface:
       if (aBackend == LayersBackend::LAYERS_OPENGL ||
           aBackend == LayersBackend::LAYERS_WR) {
-        return CreateTextureHostOGL(aDesc, aDeallocator, aFlags);
+        return CreateTextureHostOGL(aDesc, aDeallocator, aBackend, aFlags);
       } else {
-        return CreateTextureHostBasic(aDesc, aDeallocator, aFlags);
+        return CreateTextureHostBasic(aDesc, aDeallocator, aBackend, aFlags);
       }
 
 #ifdef MOZ_X11
     case SurfaceDescriptor::TSurfaceDescriptorX11: {
       const SurfaceDescriptorX11& desc = aDesc.get_SurfaceDescriptorX11();
       return MakeAndAddRef<X11TextureHost>(aFlags, desc);
     }
 #endif
 
 #ifdef XP_WIN
     case SurfaceDescriptor::TSurfaceDescriptorD3D10:
     case SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr:
-      return CreateTextureHostD3D11(aDesc, aDeallocator, aFlags);
+      return CreateTextureHostD3D11(aDesc, aDeallocator, aBackend, aFlags);
 #endif
     default:
       MOZ_CRASH("GFX: Unsupported Surface type host");
   }
 }
 
+bool WrapWithWebRenderTextureHost(LayersBackend aBackend,
+                                  TextureFlags aFlags)
+{
+  if (!gfxVars::UseWebRender() ||
+      (aFlags & TextureFlags::SNAPSHOT) ||
+      (aBackend != LayersBackend::LAYERS_WR)) {
+    return false;
+  }
+  return true;
+}
+
 already_AddRefed<TextureHost>
 CreateBackendIndependentTextureHost(const SurfaceDescriptor& aDesc,
                                     ISurfaceAllocator* aDeallocator,
+                                    LayersBackend aBackend,
                                     TextureFlags aFlags)
 {
   RefPtr<TextureHost> result;
   switch (aDesc.type()) {
     case SurfaceDescriptor::TSurfaceDescriptorBuffer: {
       const SurfaceDescriptorBuffer& bufferDesc = aDesc.get_SurfaceDescriptorBuffer();
       const MemoryOrShmem& data = bufferDesc.data();
       switch (data.type()) {
         case MemoryOrShmem::TShmem: {
           result = new ShmemTextureHost(data.get_Shmem(),
                                         bufferDesc.desc(),
                                         aDeallocator,
                                         aFlags);
+          if (WrapWithWebRenderTextureHost(aBackend, aFlags)) {
+            result = new WebRenderTextureHost(aFlags, result);
+          }
           break;
         }
         case MemoryOrShmem::Tuintptr_t: {
           result = new MemoryTextureHost(reinterpret_cast<uint8_t*>(data.get_uintptr_t()),
                                          bufferDesc.desc(),
                                          aFlags);
+          if (WrapWithWebRenderTextureHost(aBackend, aFlags)) {
+            result = new WebRenderTextureHost(aFlags, result);
+          }
           break;
         }
         default:
           gfxCriticalError() << "Failed texture host for backend " << (int)data.type();
           MOZ_CRASH("GFX: No texture host for backend");
       }
       break;
     }
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -49,16 +49,17 @@ class ISurfaceAllocator;
 class TextureHostOGL;
 class TextureReadLock;
 class TextureSourceOGL;
 class TextureSourceD3D11;
 class TextureSourceBasic;
 class DataTextureSource;
 class PTextureParent;
 class TextureParent;
+class WebRenderTextureHost;
 class WrappingTextureSourceYCbCrBasic;
 
 /**
  * A view on a TextureHost where the texture is internally represented as tiles
  * (contrast with a tiled buffer, where each texture is a tile). For iteration by
  * the texture's buffer host.
  * This is only useful when the underlying surface is too big to fit in one
  * device texture, which forces us to split it in smaller parts.
@@ -579,16 +580,18 @@ public:
   void SetReadLock(TextureReadLock* aReadLock);
 
   TextureReadLock* GetReadLock() { return mReadLock; }
 
   virtual Compositor* GetCompositor() = 0;
 
   virtual BufferTextureHost* AsBufferTextureHost() { return nullptr; }
 
+  virtual WebRenderTextureHost* AsWebRenderTextureHost() { return nullptr; }
+
 protected:
   void ReadUnlock();
 
   void RecycleTexture(TextureFlags aFlags);
 
   virtual void UpdatedInternal(const nsIntRegion *Region) {}
 
   /**
@@ -875,14 +878,15 @@ private:
 
 /**
  * Creates a TextureHost that can be used with any of the existing backends
  * Not all SurfaceDescriptor types are supported
  */
 already_AddRefed<TextureHost>
 CreateBackendIndependentTextureHost(const SurfaceDescriptor& aDesc,
                                     ISurfaceAllocator* aDeallocator,
+                                    LayersBackend aBackend,
                                     TextureFlags aFlags);
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -628,22 +628,23 @@ DXGIYCbCrTextureData::Deallocate(LayersI
   mHoldRefs[0] = nullptr;
   mHoldRefs[1] = nullptr;
   mHoldRefs[2] = nullptr;
 }
 
 already_AddRefed<TextureHost>
 CreateTextureHostD3D11(const SurfaceDescriptor& aDesc,
                        ISurfaceAllocator* aDeallocator,
+                       LayersBackend aBackend,
                        TextureFlags aFlags)
 {
   RefPtr<TextureHost> result;
   switch (aDesc.type()) {
     case SurfaceDescriptor::TSurfaceDescriptorBuffer: {
-      result = CreateBackendIndependentTextureHost(aDesc, aDeallocator, aFlags);
+      result = CreateBackendIndependentTextureHost(aDesc, aDeallocator, aBackend, aFlags);
       break;
     }
     case SurfaceDescriptor::TSurfaceDescriptorD3D10: {
       result = new DXGITextureHostD3D11(aFlags,
                                         aDesc.get_SurfaceDescriptorD3D10());
       break;
     }
     case SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr: {
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -1125,17 +1125,20 @@ 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*, uint32_t *aIdNamespace)
+CompositorBridgeChild::AllocPWebRenderBridgeChild(const wr::PipelineId& aPipelineId,
+                                                  const LayoutDeviceIntSize&,
+                                                  TextureFactoryIdentifier*,
+                                                  uint32_t *aIdNamespace)
 {
   WebRenderBridgeChild* child = new WebRenderBridgeChild(aPipelineId);
   child->AddIPDLReference();
   return child;
 }
 
 bool
 CompositorBridgeChild::DeallocPWebRenderBridgeChild(PWebRenderBridgeChild* aActor)
--- a/gfx/layers/ipc/CompositorBridgeChild.h
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -218,17 +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*,
+  PWebRenderBridgeChild* AllocPWebRenderBridgeChild(const wr::PipelineId& aPipelineId,
+                                                    const LayoutDeviceIntSize&,
+                                                    TextureFactoryIdentifier*,
                                                     uint32_t*) override;
   bool DeallocPWebRenderBridgeChild(PWebRenderBridgeChild* aActor) override;
 
   uint64_t DeviceResetSequenceNumber() const {
     return mDeviceResetSequenceNumber;
   }
 
 private:
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -1570,33 +1570,35 @@ CompositorBridgeParent::RecvAdoptChild(c
   if (mApzcTreeManager && parent) {
     parent->ChildAdopted(mApzcTreeManager);
   }
   return IPC_OK();
 }
 
 PWebRenderBridgeParent*
 CompositorBridgeParent::AllocPWebRenderBridgeParent(const wr::PipelineId& aPipelineId,
+                                                    const LayoutDeviceIntSize& aSize,
                                                     TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                                     uint32_t* aIdNamespace)
 {
 #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<wr::WebRenderAPI> api = wr::WebRenderAPI::Create(
+    gfxPrefs::WebRenderProfilerEnabled(), this, Move(widget), aSize);
   RefPtr<WebRenderCompositableHolder> holder = new WebRenderCompositableHolder();
   MOZ_ASSERT(api); // TODO have a fallback
   api->SetRootPipeline(aPipelineId);
   mWrBridge = new WebRenderBridgeParent(this, aPipelineId, mWidget, nullptr, Move(api), Move(holder));
   *aIdNamespace = mWrBridge->GetIdNameSpace();
 
   mCompositorScheduler = mWrBridge->CompositorScheduler();
   MOZ_ASSERT(mCompositorScheduler);
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -450,16 +450,17 @@ public:
 
   RefPtr<APZCTreeManager> GetAPZCTreeManager();
 
   CompositorOptions GetOptions() const {
     return mOptions;
   }
 
   PWebRenderBridgeParent* AllocPWebRenderBridgeParent(const wr::PipelineId& aPipelineId,
+                                                      const LayoutDeviceIntSize& aSize,
                                                       TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                                       uint32_t* aIdNamespace) override;
   bool DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor) override;
   static void SetWebRenderProfilerEnabled(bool aEnabled);
 
   static CompositorBridgeParent* GetCompositorBridgeParentFromLayersId(const uint64_t& aLayersId);
 private:
 
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -194,16 +194,17 @@ CrossProcessCompositorBridgeParent::Deal
 {
   RemoteContentController* controller = static_cast<RemoteContentController*>(aActor);
   controller->Release();
   return true;
 }
 
 PWebRenderBridgeParent*
 CrossProcessCompositorBridgeParent::AllocPWebRenderBridgeParent(const wr::PipelineId& aPipelineId,
+                                                                const LayoutDeviceIntSize& aSize,
                                                                 TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                                                 uint32_t *aIdNamespace)
 {
 #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
@@ -217,17 +218,17 @@ CrossProcessCompositorBridgeParent::Allo
   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();
+  RefPtr<WebRenderCompositableHolder> holder = new WebRenderCompositableHolder();
   parent = new WebRenderBridgeParent(this, aPipelineId, nullptr, root->CompositorScheduler(), Move(api), Move(holder));
 
   parent->AddRef(); // IPDL reference
   sIndirectLayerTrees[pipelineHandle].mCrossProcessParent = this;
   sIndirectLayerTrees[pipelineHandle].mWrBridge = parent;
   *aTextureFactoryIdentifier = parent->GetTextureFactoryIdentifier();
   *aIdNamespace = parent->GetIdNameSpace();
 
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h
@@ -150,16 +150,17 @@ public:
   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,
+                                                      const LayoutDeviceIntSize& aSize,
                                                       TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                                       uint32_t* aIdNamespace) override;
   bool DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor) override;
 
   void ObserveLayerUpdate(uint64_t aLayersId, uint64_t aEpoch, bool aActive) override;
 
 protected:
   void OnChannelConnected(int32_t pid) override {
--- a/gfx/layers/ipc/ISurfaceAllocator.h
+++ b/gfx/layers/ipc/ISurfaceAllocator.h
@@ -92,16 +92,18 @@ public:
   // ipc info
 
   virtual bool IPCOpen() const { return true; }
 
   virtual bool IsSameProcess() const = 0;
 
   virtual bool UsesImageBridge() const { return false; }
 
+  virtual bool UsesWebRenderBridge() const { return false; }
+
 protected:
   void Finalize() {}
 
   virtual ~ISurfaceAllocator() {}
 };
 
 /// Methods that are specific to the client/child side.
 class ClientIPCAllocator : public ISurfaceAllocator
--- a/gfx/layers/ipc/PCompositorBridge.ipdl
+++ b/gfx/layers/ipc/PCompositorBridge.ipdl
@@ -25,16 +25,17 @@ using mozilla::layers::MaybeZoomConstrai
 using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
 using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h";
 using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
 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 mozilla::LayoutDeviceIntSize 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 {
@@ -237,17 +238,17 @@ parent:
    */
   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)
+  sync PWebRenderBridge(PipelineId pipelineId, LayoutDeviceIntSize aSize)
     returns (TextureFactoryIdentifier textureFactoryIdentifier, uint32_t idNamespace); //XXX: use the WrIdNamespace type
 
 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);
 };
--- a/gfx/layers/ipc/WebRenderMessages.ipdlh
+++ b/gfx/layers/ipc/WebRenderMessages.ipdlh
@@ -7,17 +7,17 @@
 
 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 WrLayoutSize from "mozilla/webrender/webrender_ffi.h";
+using WrSize from "mozilla/webrender/webrender_ffi.h";
 using WrRect from "mozilla/webrender/webrender_ffi.h";
 using WrPoint from "mozilla/webrender/webrender_ffi.h";
 using WrGradientStop from "mozilla/webrender/webrender_ffi.h";
 using WrGradientExtendMode from "mozilla/webrender/webrender_ffi.h";
 using WrGlyphArray from "mozilla/webrender/webrender_ffi.h";
 using WrMixBlendMode from "mozilla/webrender/webrender_ffi.h";
 using WrBoxShadowClipMode from "mozilla/webrender/webrender_ffi.h";
 using MaybeImageMask from "mozilla/webrender/WebRenderTypes.h";
@@ -27,127 +27,20 @@ using mozilla::wr::PipelineId from "mozi
 using mozilla::wr::ImageRendering from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::ImageKey from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::FontKey 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 OpDPPushScrollLayer {
-  WrRect bounds;
-  WrRect overflow;
-  MaybeImageMask mask;
-  uint64_t scrollid;
-};
-
-struct OpDPPopScrollLayer { };
-
-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 OpDPPushLinearGradient {
-  WrRect bounds;
-  WrRect clip;
-  WrPoint startPoint;
-  WrPoint endPoint;
-  WrGradientExtendMode extendMode;
-  WrGradientStop[] stops;
-};
-
-struct OpDPPushRadialGradient {
-  WrRect bounds;
-  WrRect clip;
-  WrPoint startCenter;
-  WrPoint endCenter;
-  float startRadius;
-  float endRadius;
-  WrGradientExtendMode extendMode;
-  WrGradientStop[] stops;
-};
-
-struct OpDPPushImage {
-  WrRect bounds;
-  WrRect clip;
-  MaybeImageMask mask;
-  ImageRendering filter;
-  ImageKey key;
-};
-
 struct OpAddExternalImage {
   uint64_t externalImageId;
   ImageKey key;
 };
 
-struct OpDPPushIframe {
-  WrRect bounds;
-  WrRect clip;
-  PipelineId pipelineId;
-};
-
-struct OpDPPushText {
-  WrRect bounds;
-  WrRect clip;
-  WrGlyphArray[] glyph_array;
-  float glyph_size;
-  FontKey key;
-};
-
-struct OpDPPushBoxShadow {
-  WrRect rect;
-  WrRect clip;
-  WrRect box_bounds;
-  WrPoint offset;
-  WrColor color;
-  float blur_radius;
-  float spread_radius;
-  float border_radius;
-  WrBoxShadowClipMode clip_mode;
-};
-
-union WebRenderCommand {
-  OpDPPushStackingContext;
-  OpDPPopStackingContext;
-  OpDPPushScrollLayer;
-  OpDPPopScrollLayer;
-  OpDPPushRect;
-  OpDPPushBorder;
-  OpDPPushLinearGradient;
-  OpDPPushRadialGradient;
-  OpDPPushImage;
-  OpDPPushIframe;
-  OpDPPushText;
-  OpDPPushBoxShadow;
-};
-
 union WebRenderParentCommand {
   OpAddExternalImage;
   CompositableOperation;
 };
 
 } // namespace
 } // namespace
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -186,24 +186,26 @@ EXPORTS.mozilla.layers += [
     'opengl/TextureClientOGL.h',
     'opengl/TextureHostOGL.h',
     'PersistentBufferProvider.h',
     'RenderTrace.h',
     'SourceSurfaceSharedData.h',
     'SourceSurfaceVolatileData.h',
     'TextureWrapperImage.h',
     'TransactionIdAllocator.h',
+    'wr/WebRenderBorderLayer.h',
     'wr/WebRenderBridgeChild.h',
     'wr/WebRenderBridgeParent.h',
     'wr/WebRenderCompositableHolder.h',
     'wr/WebRenderDisplayItemLayer.h',
     'wr/WebRenderImageHost.h',
     'wr/WebRenderLayerManager.h',
     'wr/WebRenderLayersLogging.h',
     'wr/WebRenderMessageUtils.h',
+    'wr/WebRenderTextureHost.h',
 ]
 
 if CONFIG['MOZ_X11']:
     EXPORTS.mozilla.layers += [
         'basic/TextureClientX11.h',
         'basic/X11TextureSourceBasic.h',
         'composite/X11TextureHost.h',
         'ipc/ShadowLayerUtilsX11.h',
@@ -384,16 +386,17 @@ UNIFIED_SOURCES += [
     'wr/WebRenderContainerLayer.cpp',
     'wr/WebRenderDisplayItemLayer.cpp',
     'wr/WebRenderImageHost.cpp',
     'wr/WebRenderImageLayer.cpp',
     'wr/WebRenderLayerManager.cpp',
     'wr/WebRenderLayersLogging.cpp',
     'wr/WebRenderPaintedLayer.cpp',
     'wr/WebRenderTextLayer.cpp',
+    'wr/WebRenderTextureHost.cpp',
 ]
 
 SOURCES += [
     'basic/BasicImageLayer.cpp',
     'ImageContainer.cpp',
     'Layers.cpp',
     'LayerTreeInvalidation.cpp',
     'PersistentBufferProvider.cpp',
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -35,23 +35,26 @@ using namespace mozilla::gfx;
 namespace mozilla {
 namespace layers {
 
 class Compositor;
 
 already_AddRefed<TextureHost>
 CreateTextureHostOGL(const SurfaceDescriptor& aDesc,
                      ISurfaceAllocator* aDeallocator,
+                     LayersBackend aBackend,
                      TextureFlags aFlags)
 {
   RefPtr<TextureHost> result;
   switch (aDesc.type()) {
     case SurfaceDescriptor::TSurfaceDescriptorBuffer: {
       result = CreateBackendIndependentTextureHost(aDesc,
-                                                   aDeallocator, aFlags);
+                                                   aDeallocator,
+                                                   aBackend,
+                                                   aFlags);
       break;
     }
 
 #ifdef MOZ_WIDGET_ANDROID
     case SurfaceDescriptor::TSurfaceTextureDescriptor: {
       const SurfaceTextureDescriptor& desc = aDesc.get_SurfaceTextureDescriptor();
       result = new SurfaceTextureHost(aFlags,
                                       (AndroidSurfaceTexture*)desc.surfTex(),
--- a/gfx/layers/wr/WebRenderBorderLayer.cpp
+++ b/gfx/layers/wr/WebRenderBorderLayer.cpp
@@ -12,66 +12,68 @@
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
-void
-WebRenderBorderLayer::RenderLayer()
+/* static */void
+WebRenderBorderLayer::CreateWebRenderCommands(wr::DisplayListBuilder& aBuilder,
+                                              WebRenderLayer* aLayer,
+                                              BorderColors& aColors,
+                                              BorderCorners& aCorners,
+                                              BorderWidths& aWidths,
+                                              BorderStyles& aBorderStyles,
+                                              Rect aRect,
+                                              Rect aClipRect,
+                                              Rect aRelBounds,
+                                              Rect aOverflow)
 {
-  WrScrollFrameStackingContextGenerator scrollFrames(this);
-
-  LayerIntRect bounds = GetVisibleRegion().GetBounds();
-  Rect rect(0, 0, bounds.width, bounds.height);
-  Rect clip;
-  Matrix4x4 transform = GetTransform();
-  if (GetClipRect().isSome()) {
-    clip = RelativeToVisible(transform.Inverse().TransformBounds(IntRectToRect(GetClipRect().ref().ToUnknownRect())));
-  } else {
-    clip = rect;
-  }
-
-  Rect relBounds = VisibleBoundsRelativeToParent();
-  if (!transform.IsIdentity()) {
-    // WR will only apply the 'translate' of the transform, so we need to do the scale/rotation manually.
-    gfx::Matrix4x4 boundTransform = transform;
-    boundTransform._41 = 0.0f;
-    boundTransform._42 = 0.0f;
-    boundTransform._43 = 0.0f;
-    relBounds.MoveTo(boundTransform.TransformPoint(relBounds.TopLeft()));
-  }
-
-  Rect overflow(0, 0, relBounds.width, relBounds.height);
+  aBuilder.PushStackingContext(wr::ToWrRect(aRelBounds),
+                               wr::ToWrRect(aOverflow),
+                               nullptr,
+                               1.0f,
+                               aLayer->GetLayer()->GetTransform(),
+                               wr::MixBlendMode::Normal);
+  aBuilder.PushBorder(wr::ToWrRect(aRect), aBuilder.BuildClipRegion(wr::ToWrRect(aClipRect)),
+                      wr::ToWrBorderWidths(aWidths[0], aWidths[1], aWidths[2], aWidths[3]),
+                      wr::ToWrBorderSide(aColors[0], aBorderStyles[0]),
+                      wr::ToWrBorderSide(aColors[1], aBorderStyles[1]),
+                      wr::ToWrBorderSide(aColors[2], aBorderStyles[2]),
+                      wr::ToWrBorderSide(aColors[3], aBorderStyles[3]),
+                      wr::ToWrBorderRadius(aCorners[eCornerTopLeft], aCorners[eCornerTopRight],
+                                           aCorners[eCornerBottomLeft], aCorners[eCornerBottomRight]));
+  aBuilder.PopStackingContext();
+}
 
-  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());
-  }
+void
+WebRenderBorderLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
+{
+  gfx::Rect relBounds = GetWrRelBounds();
+  gfx::Rect overflow(0, 0, relBounds.width, relBounds.height);
+
+  gfx::Rect rect = GetWrBoundsRect();
+  gfx::Rect clipRect = GetWrClipRect(rect);
+
+  DumpLayerInfo("BorderLayer", rect);
 
-  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());
+  aBuilder.PushStackingContext(wr::ToWrRect(relBounds),
+                               wr::ToWrRect(overflow),
+                               nullptr,
+                               1.0f,
+                               //GetAnimations(),
+                               GetTransform(),
+                               wr::MixBlendMode::Normal);
+  aBuilder.PushBorder(wr::ToWrRect(rect), aBuilder.BuildClipRegion(wr::ToWrRect(clipRect)),
+                      wr::ToWrBorderWidths(mWidths[0], mWidths[1], mWidths[2], mWidths[3]),
+                      wr::ToWrBorderSide(mColors[0], mBorderStyles[0]),
+                      wr::ToWrBorderSide(mColors[1], mBorderStyles[1]),
+                      wr::ToWrBorderSide(mColors[2], mBorderStyles[2]),
+                      wr::ToWrBorderSide(mColors[3], mBorderStyles[3]),
+                      wr::ToWrBorderRadius(mCorners[eCornerTopLeft], mCorners[eCornerTopRight],
+                                           mCorners[eCornerBottomLeft], mCorners[eCornerBottomRight]));
+  aBuilder.PopStackingContext();
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderBorderLayer.h
+++ b/gfx/layers/wr/WebRenderBorderLayer.h
@@ -16,23 +16,35 @@ class WebRenderBorderLayer : public WebR
                              public BorderLayer {
 public:
   explicit WebRenderBorderLayer(WebRenderLayerManager* aLayerManager)
     : BorderLayer(aLayerManager, static_cast<WebRenderLayer*>(this))
   {
     MOZ_COUNT_CTOR(WebRenderBorderLayer);
   }
 
+  static void
+  CreateWebRenderCommands(wr::DisplayListBuilder& aBuilder,
+                          WebRenderLayer* aLayer,
+                          BorderColors& aColors,
+                          BorderCorners& aCorners,
+                          BorderWidths& aWidths,
+                          BorderStyles& aBorderStyles,
+                          gfx::Rect aRect,
+                          gfx::Rect aClipRect,
+                          gfx::Rect aRelBounds,
+                          gfx::Rect aOverflow);
+
 protected:
   virtual ~WebRenderBorderLayer()
   {
     MOZ_COUNT_DTOR(WebRenderBorderLayer);
   }
 
 public:
   Layer* GetLayer() override { return this; }
-  void RenderLayer() override;
+  void RenderLayer(wr::DisplayListBuilder& aBuilder) override;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // GFX_WEBRENDERBORDERLAYER_H
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -42,30 +42,16 @@ WebRenderBridgeChild::Destroy()
 
 void
 WebRenderBridgeChild::ActorDestroy(ActorDestroyReason why)
 {
   mDestroyed = true;
 }
 
 void
-WebRenderBridgeChild::AddWebRenderCommand(const WebRenderCommand& aCmd)
-{
-  MOZ_ASSERT(mIsInTransaction);
-  mCommands.AppendElement(aCmd);
-}
-
-void
-WebRenderBridgeChild::AddWebRenderCommands(const nsTArray<WebRenderCommand>& aCommands)
-{
-  MOZ_ASSERT(mIsInTransaction);
-  mCommands.AppendElements(aCommands);
-}
-
-void
 WebRenderBridgeChild::AddWebRenderParentCommand(const WebRenderParentCommand& aCmd)
 {
   MOZ_ASSERT(mIsInTransaction);
   mParentCommands.AppendElement(aCmd);
 }
 
 void
 WebRenderBridgeChild::AddWebRenderParentCommands(const nsTArray<WebRenderParentCommand>& aCommands)
@@ -83,150 +69,43 @@ WebRenderBridgeChild::DPBegin(const gfx:
   UpdateFwdTransactionId();
   this->SendDPBegin(aSize);
   mIsInTransaction = true;
   mReadLockSequenceNumber = 0;
   mReadLocks.AppendElement();
   return true;
 }
 
-wr::BuiltDisplayList
-WebRenderBridgeChild::ProcessWebrenderCommands(const gfx::IntSize &aSize,
-                                               InfallibleTArray<WebRenderCommand>& aCommands)
-{
-  wr::DisplayListBuilder builder(mPipelineId);
-  builder.Begin(ViewAs<LayerPixel>(aSize));
-
-  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::TOpDPPushScrollLayer: {
-        const OpDPPushScrollLayer& op = cmd.get_OpDPPushScrollLayer();
-        builder.PushScrollLayer(op.bounds(), op.overflow(), op.mask().ptrOr(nullptr));
-        break;
-      }
-      case WebRenderCommand::TOpDPPopScrollLayer: {
-        builder.PopScrollLayer();
-        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::TOpDPPushLinearGradient: {
-        const OpDPPushLinearGradient& op = cmd.get_OpDPPushLinearGradient();
-        builder.PushLinearGradient(op.bounds(), op.clip(),
-                                   op.startPoint(), op.endPoint(),
-                                   op.stops(), op.extendMode());
-        break;
-      }
-      case WebRenderCommand::TOpDPPushRadialGradient: {
-        const OpDPPushRadialGradient& op = cmd.get_OpDPPushRadialGradient();
-        builder.PushRadialGradient(op.bounds(), op.clip(),
-                                   op.startCenter(), op.endCenter(),
-                                   op.startRadius(), op.endRadius(),
-                                   op.stops(), op.extendMode());
-        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::TOpDPPushIframe: {
-        const OpDPPushIframe& op = cmd.get_OpDPPushIframe();
-        builder.PushIFrame(op.bounds(), op.clip(), op.pipelineId());
-        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;
-          builder.PushText(op.bounds(),
-                           op.clip(),
-                           glyph_array[i].color,
-                           op.key(),
-                           Range<const WrGlyphInstance>(glyphs.Elements(), glyphs.Length()),
-                           op.glyph_size());
-        }
-
-        break;
-      }
-      case WebRenderCommand::TOpDPPushBoxShadow: {
-        const OpDPPushBoxShadow& op = cmd.get_OpDPPushBoxShadow();
-        builder.PushBoxShadow(op.rect(),
-                              op.clip(),
-                              op.box_bounds(),
-                              op.offset(),
-                              op.color(),
-                              op.blur_radius(),
-                              op.spread_radius(),
-                              op.border_radius(),
-                              op.clip_mode());
-        break;
-      }
-      default:
-        NS_RUNTIMEABORT("not reached");
-    }
-  }
-  builder.End();
-  wr::BuiltDisplayList dl;
-  builder.Finalize(dl.dl_desc, dl.dl, dl.aux_desc, dl.aux);
-  return dl;
-}
-
 void
-WebRenderBridgeChild::DPEnd(const gfx::IntSize& aSize, bool aIsSync, uint64_t aTransactionId)
+WebRenderBridgeChild::DPEnd(wr::DisplayListBuilder &aBuilder, const gfx::IntSize& aSize, bool aIsSync, uint64_t aTransactionId)
 {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(mIsInTransaction);
 
   for (nsTArray<ReadLockInit>& locks : mReadLocks) {
     if (locks.Length()) {
       if (!SendInitReadLocks(locks)) {
         NS_WARNING("WARNING: sending read locks failed!");
         return;
       }
     }
   }
 
-  wr::BuiltDisplayList dl = ProcessWebrenderCommands(aSize, mCommands);
+  wr::BuiltDisplayList dl = aBuilder.Finalize();
   ByteBuffer dlData(Move(dl.dl));
   ByteBuffer auxData(Move(dl.aux));
 
   if (aIsSync) {
     this->SendDPSyncEnd(aSize, mParentCommands, mDestroyedActors, GetFwdTransactionId(), aTransactionId,
                         dlData, dl.dl_desc, auxData, dl.aux_desc);
   } else {
     this->SendDPEnd(aSize, mParentCommands, mDestroyedActors, GetFwdTransactionId(), aTransactionId,
                     dlData, dl.dl_desc, auxData, dl.aux_desc);
   }
 
-  mCommands.Clear();
   mParentCommands.Clear();
   mDestroyedActors.Clear();
   mReadLocks.Clear();
   mIsInTransaction = false;
 }
 
 uint64_t
 WebRenderBridgeChild::GetNextExternalImageId()
--- a/gfx/layers/wr/WebRenderBridgeChild.h
+++ b/gfx/layers/wr/WebRenderBridgeChild.h
@@ -11,40 +11,44 @@
 #include "mozilla/layers/PWebRenderBridgeChild.h"
 
 namespace mozilla {
 
 namespace widget {
 class CompositorWidget;
 }
 
+namespace wr {
+class DisplayListBuilder;
+}
+
 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);
-  void AddWebRenderCommands(const nsTArray<WebRenderCommand>& aCommands);
   void AddWebRenderParentCommand(const WebRenderParentCommand& aCmd);
   void AddWebRenderParentCommands(const nsTArray<WebRenderParentCommand>& aCommands);
 
   bool DPBegin(const  gfx::IntSize& aSize);
-  void DPEnd(const gfx::IntSize& aSize, bool aIsSync, uint64_t aTransactionId);
+  void DPEnd(wr::DisplayListBuilder &aBuilder, const gfx::IntSize& aSize, bool aIsSync, uint64_t aTransactionId);
 
   CompositorBridgeChild* GetCompositorBridgeChild();
 
+  wr::PipelineId GetPipeline() { return mPipelineId; }
+
   // KnowsCompositor
   TextureForwarder* GetTextureForwarder() override;
   LayersIPCActor* GetLayersIPCActor() override;
 
   uint64_t AllocExternalImageId(const CompositableHandle& aHandle);
   uint64_t AllocExternalImageIdForCompositable(CompositableClient* aCompositable);
   void DeallocExternalImageId(uint64_t aImageId);
 
@@ -65,19 +69,16 @@ public:
 
 private:
   friend class CompositorBridgeChild;
 
   ~WebRenderBridgeChild() {}
 
   uint64_t GetNextExternalImageId();
 
-  wr::BuiltDisplayList ProcessWebrenderCommands(const gfx::IntSize &aSize,
-                                                InfallibleTArray<WebRenderCommand>& aCommands);
-
   // 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;
@@ -105,17 +106,16 @@ private:
   void ReleaseIPDLReference() {
     MOZ_ASSERT(mIPCOpen == true);
     mIPCOpen = false;
     Release();
   }
 
   bool AddOpDestroy(const OpDestroy& aOp);
 
-  nsTArray<WebRenderCommand> mCommands;
   nsTArray<WebRenderParentCommand> mParentCommands;
   nsTArray<OpDestroy> mDestroyedActors;
   nsDataHashtable<nsUint64HashKey, CompositableClient*> mCompositables;
   nsTArray<nsTArray<ReadLockInit>> mReadLocks;
   uint64_t mReadLockSequenceNumber;
   bool mIsInTransaction;
   uint32_t mIdNamespace;
   uint32_t mResourceId;
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -13,16 +13,17 @@
 #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/WebRenderTextureHost.h"
 #include "mozilla/webrender/RenderThread.h"
 #include "mozilla/widget/CompositorWidget.h"
 
 bool is_in_main_thread()
 {
   return NS_IsMainThread();
 }
 
@@ -234,17 +235,17 @@ WebRenderBridgeParent::HandleDPEnd(const
     }
     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);
 
   ++mWrEpoch; // Update webrender epoch
-  ProcessWebrenderCommands(aSize, aCommands, wr::NewEpoch(mWrEpoch),
+  ProcessWebRenderCommands(aSize, aCommands, wr::NewEpoch(mWrEpoch),
                            dl, dlDesc, aux, auxDesc);
   HoldPendingTransactionId(mWrEpoch, aTransactionId);
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvDPEnd(const gfx::IntSize& aSize,
                                  InfallibleTArray<WebRenderParentCommand>&& aCommands,
                                  InfallibleTArray<OpDestroy>&& aToDestroy,
@@ -272,51 +273,70 @@ WebRenderBridgeParent::RecvDPSyncEnd(con
                                      const WrAuxiliaryListsDescriptor& auxDesc)
 {
   HandleDPEnd(aSize, Move(aCommands), Move(aToDestroy), aFwdTransactionId, aTransactionId,
               dl, dlDesc, aux, auxDesc);
   return IPC_OK();
 }
 
 void
-WebRenderBridgeParent::ProcessWebrenderCommands(const gfx::IntSize &aSize,
+WebRenderBridgeParent::ProcessWebRenderCommands(const gfx::IntSize &aSize,
                                                 InfallibleTArray<WebRenderParentCommand>& aCommands, const wr::Epoch& aEpoch,
                                                 const ByteBuffer& dl,
                                                 const WrBuiltDisplayListDescriptor& dlDesc,
                                                 const ByteBuffer& aux,
                                                 const WrAuxiliaryListsDescriptor& auxDesc)
 {
   // XXX remove it when external image key is used.
   std::vector<wr::ImageKey> keysToDelete;
 
   for (InfallibleTArray<WebRenderParentCommand>::index_type i = 0; i < aCommands.Length(); ++i) {
     const WebRenderParentCommand& cmd = aCommands[i];
-
     switch (cmd.type()) {
       case WebRenderParentCommand::TOpAddExternalImage: {
         const OpAddExternalImage& op = cmd.get_OpAddExternalImage();
+        wr::ImageKey key = op.key();
         MOZ_ASSERT(mExternalImageIds.Get(op.externalImageId()).get());
 
         RefPtr<CompositableHost> host = mExternalImageIds.Get(op.externalImageId());
         if (!host) {
+          NS_ERROR("CompositableHost does not exist");
+          break;
+        }
+        // XXX select Texture for video in CompositeToTarget().
+        TextureHost* texture = host->GetAsTextureHost();
+        if (!texture) {
+          NS_ERROR("TextureHost does not exist");
+          break;
+        }
+        WebRenderTextureHost* wrTexture = texture->AsWebRenderTextureHost();
+        if (wrTexture) {
+          // XXX handling YUV
+          gfx::SurfaceFormat format =
+            wrTexture->GetFormat() == SurfaceFormat::YUV ? SurfaceFormat::B8G8R8A8 : wrTexture->GetFormat();
+          wr::ImageDescriptor descriptor(wrTexture->GetSize(), wrTexture->GetRGBStride(), format);
+          mApi->AddExternalImageBuffer(key,
+                                       descriptor,
+                                       wrTexture->GetExternalImageKey());
+          mCompositableHolder->HoldExternalImage(aEpoch, texture->AsWebRenderTextureHost());
+          keysToDelete.push_back(key);
           break;
         }
         RefPtr<DataSourceSurface> dSurf = host->GetAsSurface();
         if (!dSurf) {
           break;
         }
 
         DataSourceSurface::MappedSurface map;
         if (!dSurf->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
           break;
         }
 
         IntSize size = dSurf->GetSize();
-        wr::ImageDescriptor descriptor(size, map.mStride, SurfaceFormat::B8G8R8A8);
-        wr::ImageKey key = op.key();
+        wr::ImageDescriptor descriptor(size, map.mStride, dSurf->GetFormat());
         auto slice = Range<uint8_t>(map.mData, size.height * map.mStride);
         mApi->AddImage(key, descriptor, slice);
 
         keysToDelete.push_back(key);
         dSurf->Unmap();
         // XXX workaround for releasing Readlock. See Bug 1339625
         if(host->GetType() == CompositableType::CONTENT_SINGLE) {
           host->CleanupResources();
@@ -330,16 +350,20 @@ WebRenderBridgeParent::ProcessWebrenderC
         break;
       }
       default: {
         // other commands are handle on the child
         break;
       }
     }
   }
+  if (mWidget) {
+    LayoutDeviceIntSize size = mWidget->GetClientSize();
+    mApi->SetWindowParameters(size);
+  }
   mApi->SetRootDisplayList(gfx::Color(0.3f, 0.f, 0.f, 1.f), aEpoch, LayerSize(aSize.width, aSize.height),
                            mPipelineId,
                            dlDesc, dl.mData, dl.mLength,
                            auxDesc, aux.mData, aux.mLength);
 
   ScheduleComposition();
   DeleteOldImages();
 
@@ -422,17 +446,16 @@ WebRenderBridgeParent::RecvAddExternalIm
   }
   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)
@@ -445,31 +468,29 @@ WebRenderBridgeParent::RecvAddExternalIm
   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;
@@ -525,30 +546,31 @@ WebRenderBridgeParent::FlushPendingTrans
   uint64_t id = 0;
   while (!mPendingTransactionIds.empty()) {
     id = mPendingTransactionIds.front().mId;
     mPendingTransactionIds.pop();
   }
   return id;
 }
 
-
-
 uint64_t
 WebRenderBridgeParent::FlushTransactionIdsForEpoch(const wr::Epoch& aEpoch)
 {
   uint64_t id = 0;
   while (!mPendingTransactionIds.empty()) {
     id = mPendingTransactionIds.front().mId;
     if (mPendingTransactionIds.front().mEpoch == aEpoch) {
       mPendingTransactionIds.pop();
       break;
     }
     mPendingTransactionIds.pop();
   }
+
+  mCompositableHolder->Update(aEpoch);
+
   return id;
 }
 
 WebRenderBridgeParent::~WebRenderBridgeParent()
 {
 }
 
 void
@@ -566,22 +588,27 @@ WebRenderBridgeParent::ScheduleCompositi
   if (mCompositorScheduler) {
     mCompositorScheduler->ScheduleComposition();
   }
 }
 
 void
 WebRenderBridgeParent::ClearResources()
 {
-  DeleteOldImages();
+  if (mApi) {
+    ++mWrEpoch; // Update webrender epoch
+    mApi->ClearRootDisplayList(wr::NewEpoch(mWrEpoch), mPipelineId);
+    if (!mKeysToDelete.empty()) {
+      // XXX Sync wait.
+      mApi->WaitFlushed();
+      DeleteOldImages();
+    }
+  }
   if (mCompositableHolder) {
-    for (auto iter = mExternalImageIds.Iter(); !iter.Done(); iter.Next()) {
-      uint64_t externalImageId = iter.Key();
-      mCompositableHolder->RemoveExternalImageId(externalImageId);
-    }
+    mCompositableHolder->Destroy();
   }
   mExternalImageIds.Clear();
 
   if (mWidget && mCompositorScheduler) {
     mCompositorScheduler->Destroy();
   }
   mCompositorScheduler = nullptr;
   mApi = nullptr;
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -147,17 +147,17 @@ public:
   {
     return mIdNameSpace;
   }
 
 private:
   virtual ~WebRenderBridgeParent();
 
   void DeleteOldImages();
-  void ProcessWebrenderCommands(const gfx::IntSize &aSize, InfallibleTArray<WebRenderParentCommand>& commands, const wr::Epoch& aEpoch,
+  void ProcessWebRenderCommands(const gfx::IntSize &aSize, InfallibleTArray<WebRenderParentCommand>& commands, const wr::Epoch& aEpoch,
                                     const ByteBuffer& dl,
                                     const WrBuiltDisplayListDescriptor& dlDesc,
                                     const ByteBuffer& aux,
                                     const WrAuxiliaryListsDescriptor& auxDesc);
   void ScheduleComposition();
   void ClearResources();
   uint64_t GetChildLayerObserverEpoch() const { return mChildLayerObserverEpoch; }
   bool ShouldParentObserveEpoch();
--- a/gfx/layers/wr/WebRenderCanvasLayer.cpp
+++ b/gfx/layers/wr/WebRenderCanvasLayer.cpp
@@ -41,83 +41,65 @@ WebRenderCanvasLayer::Initialize(const D
     return;
 
   gl::GLScreenBuffer* screen = mGLContext->Screen();
   auto factory = MakeUnique<gl::SurfaceFactory_Basic>(mGLContext, screen->mCaps, mFlags);
   screen->Morph(Move(factory));
 }
 
 void
-WebRenderCanvasLayer::RenderLayer()
+WebRenderCanvasLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
 {
   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 = RelativeToVisible(rect);
+
+  gfx::Rect relBounds = GetWrRelBounds();
+  gfx::Rect overflow(0, 0, relBounds.width, relBounds.height);
+
+  gfx::Rect rect = RelativeToVisible(gfx::Rect(0, 0, mBounds.width, mBounds.height));
+  gfx::Rect clipRect = GetWrClipRect(rect);
 
-  gfx::Rect clip;
-  if (GetClipRect().isSome()) {
-      clip = RelativeToVisible(transform.Inverse().TransformBounds(IntRectToRect(GetClipRect().ref().ToUnknownRect())));
-  } else {
-      clip = rect;
-  }
+  Maybe<WrImageMask> mask = BuildWrMaskLayer();
+  WrClipRegion clip = aBuilder.BuildClipRegion(wr::ToWrRect(clipRect));
+
+  wr::ImageRendering filter = wr::ToImageRendering(mSamplingFilter);
+  wr::MixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
 
-  gfx::Rect relBounds = VisibleBoundsRelativeToParent();
-  if (!transform.IsIdentity()) {
-    // WR will only apply the 'translate' of the transform, so we need to do the scale/rotation manually.
-    gfx::Matrix4x4 boundTransform = transform;
-    boundTransform._41 = 0.0f;
-    boundTransform._42 = 0.0f;
-    boundTransform._43 = 0.0f;
-    relBounds.MoveTo(boundTransform.TransformPoint(relBounds.TopLeft()));
+  DumpLayerInfo("CanvasLayer", rect);
+  if (gfxPrefs::LayersDump()) {
+    printf_stderr("CanvasLayer %p texture-filter=%s\n",
+                  this->GetLayer(),
+                  Stringify(filter).c_str());
   }
 
-  gfx::Rect overflow(0, 0, relBounds.width, relBounds.height);
-  Maybe<WrImageMask> mask = buildMaskLayer();
-  wr::ImageRendering filter = wr::ToImageRendering(mSamplingFilter);
-  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));
   WrImageKey key;
   key.mNamespace = WrBridge()->GetNamespace();
   key.mHandle = WrBridge()->GetNextResourceId();
   WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(mExternalImageId, key));
-  WrBridge()->AddWebRenderCommand(OpDPPushImage(wr::ToWrRect(rect), wr::ToWrRect(clip), Nothing(), filter, key));
-  WrBridge()->AddWebRenderCommand(OpDPPopStackingContext());
+
+  aBuilder.PushStackingContext(wr::ToWrRect(relBounds),
+                               wr::ToWrRect(overflow),
+                               mask.ptrOr(nullptr),
+                               1.0f,
+                               //GetAnimations(),
+                               transform,
+                               mixBlendMode);
+  aBuilder.PushImage(wr::ToWrRect(rect), clip, filter, key);
+  aBuilder.PopStackingContext();
 }
 
 void
 WebRenderCanvasLayer::AttachCompositable()
 {
   mCanvasClient->Connect();
 }
 
--- a/gfx/layers/wr/WebRenderCanvasLayer.h
+++ b/gfx/layers/wr/WebRenderCanvasLayer.h
@@ -40,17 +40,17 @@ protected:
   virtual ~WebRenderCanvasLayer();
   WebRenderLayerManager* Manager()
   {
     return static_cast<WebRenderLayerManager*>(mManager);
   }
 
 public:
   Layer* GetLayer() override { return this; }
-  void RenderLayer() override;
+  void RenderLayer(wr::DisplayListBuilder& aBuilder) override;
 
 protected:
   uint64_t mExternalImageId;
 };
 
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/wr/WebRenderColorLayer.cpp
+++ b/gfx/layers/wr/WebRenderColorLayer.cpp
@@ -12,65 +12,37 @@
 #include "mozilla/layers/WebRenderBridgeChild.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
 void
-WebRenderColorLayer::RenderLayer()
+WebRenderColorLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
 {
-  WrScrollFrameStackingContextGenerator scrollFrames(this);
-
   gfx::Matrix4x4 transform = GetTransform();
-  LayerIntRegion visibleRegion = GetVisibleRegion();
-  LayerIntRect bounds = visibleRegion.GetBounds();
-  Rect rect(0, 0, bounds.width, bounds.height);
-  Rect clip;
-  if (GetClipRect().isSome()) {
-      clip = RelativeToVisible(transform.Inverse().TransformBounds(IntRectToRect(GetClipRect().ref().ToUnknownRect())));
-  } else {
-      clip = rect;
-  }
-
-  gfx::Rect relBounds = VisibleBoundsRelativeToParent();
-  if (!transform.IsIdentity()) {
-    // WR will only apply the 'translate' of the transform, so we need to do the scale/rotation manually.
-    gfx::Matrix4x4 boundTransform = transform;
-    boundTransform._41 = 0.0f;
-    boundTransform._42 = 0.0f;
-    boundTransform._43 = 0.0f;
-    relBounds.MoveTo(boundTransform.TransformPoint(relBounds.TopLeft()));
-  }
-
+  gfx::Rect relBounds = GetWrRelBounds();
   gfx::Rect overflow(0, 0, relBounds.width, relBounds.height);
-  WrMixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
+
+  gfx::Rect rect = GetWrBoundsRect();
+  gfx::Rect clipRect = GetWrClipRect(rect);
 
-  Maybe<WrImageMask> mask = buildMaskLayer();
+  Maybe<WrImageMask> mask = BuildWrMaskLayer();
+  WrClipRegion clip = aBuilder.BuildClipRegion(wr::ToWrRect(clipRect));
+
+  wr::MixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
 
-  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());
-  }
+  DumpLayerInfo("ColorLayer", rect);
 
-  WrBridge()->AddWebRenderCommand(
-      OpDPPushStackingContext(wr::ToWrRect(relBounds),
+  aBuilder.PushStackingContext(wr::ToWrRect(relBounds),
                               wr::ToWrRect(overflow),
-                              mask,
+                              mask.ptrOr(nullptr),
                               1.0f,
-                              GetAnimations(),
+                              //GetAnimations(),
                               transform,
-                              mixBlendMode,
-                              FrameMetrics::NULL_SCROLL_ID));
-  WrBridge()->AddWebRenderCommand(
-    OpDPPushRect(wr::ToWrRect(rect), wr::ToWrRect(clip), wr::ToWrColor(mColor)));
-  WrBridge()->AddWebRenderCommand(OpDPPopStackingContext());
+                              mixBlendMode);
+  aBuilder.PushRect(wr::ToWrRect(rect), clip, wr::ToWrColor(mColor));
+  aBuilder.PopStackingContext();
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderColorLayer.h
+++ b/gfx/layers/wr/WebRenderColorLayer.h
@@ -24,15 +24,15 @@ public:
 protected:
   virtual ~WebRenderColorLayer()
   {
     MOZ_COUNT_DTOR(WebRenderColorLayer);
   }
 
 public:
   Layer* GetLayer() override { return this; }
-  void RenderLayer() override;
+  void RenderLayer(wr::DisplayListBuilder& aBuilder) override;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // GFX_WEBRENDERCOLORLAYER_H
--- a/gfx/layers/wr/WebRenderCompositableHolder.cpp
+++ b/gfx/layers/wr/WebRenderCompositableHolder.cpp
@@ -1,59 +1,60 @@
 /* -*- 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"
+#include "mozilla/layers/WebRenderImageHost.h"
+#include "mozilla/layers/WebRenderTextureHost.h"
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
 WebRenderCompositableHolder::WebRenderCompositableHolder()
 {
   MOZ_COUNT_CTOR(WebRenderCompositableHolder);
 }
 
 WebRenderCompositableHolder::~WebRenderCompositableHolder()
 {
   MOZ_COUNT_DTOR(WebRenderCompositableHolder);
-  Destroy();
+  MOZ_ASSERT(mWebRenderTextureHosts.empty());
 }
 
 void
 WebRenderCompositableHolder::Destroy()
 {
-  mCompositableHosts.Clear();
-}
-
-void
-WebRenderCompositableHolder::AddExternalImageId(uint64_t aExternalImageId, CompositableHost* aHost)
-{
-  MOZ_ASSERT(!mCompositableHosts.Get(aExternalImageId));
-  mCompositableHosts.Put(aExternalImageId, aHost);
+  while (!mWebRenderTextureHosts.empty()) {
+    mWebRenderTextureHosts.pop();
+  }
 }
 
 void
-WebRenderCompositableHolder::RemoveExternalImageId(uint64_t aExternalImageId)
+WebRenderCompositableHolder::HoldExternalImage(const wr::Epoch& aEpoch, WebRenderTextureHost* aTexture)
 {
-  MOZ_ASSERT(mCompositableHosts.Get(aExternalImageId));
-  mCompositableHosts.Remove(aExternalImageId);
+  MOZ_ASSERT(aTexture);
+  // Hold WebRenderTextureHost until end of its usage on RenderThread
+  mWebRenderTextureHosts.push(ForwardingTextureHosts(aEpoch, aTexture));
 }
 
 void
-WebRenderCompositableHolder::UpdateExternalImages()
+WebRenderCompositableHolder::Update(const wr::Epoch& aEpoch)
 {
-  for (auto iter = mCompositableHosts.Iter(); !iter.Done(); iter.Next()) {
-    RefPtr<CompositableHost>& host = iter.Data();
-    // XXX Change to correct TextrueSource handling here.
-    host->BindTextureSource();
+  if (mWebRenderTextureHosts.empty()) {
+    return;
+  }
+  while (!mWebRenderTextureHosts.empty()) {
+    if (aEpoch <= mWebRenderTextureHosts.front().mEpoch) {
+      break;
+    }
+    mWebRenderTextureHosts.pop();
   }
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderCompositableHolder.h
+++ b/gfx/layers/wr/WebRenderCompositableHolder.h
@@ -1,43 +1,58 @@
 /* -*- 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"
+#include <queue>
+
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/webrender/WebRenderTypes.h"
 
 namespace mozilla {
+
+namespace wr {
+class WebRenderAPI;
+}
+
 namespace layers {
 
 class CompositableHost;
+class WebRenderTextureHost;
 
 class WebRenderCompositableHolder final
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebRenderCompositableHolder)
 
   explicit WebRenderCompositableHolder();
 
 protected:
-  virtual ~WebRenderCompositableHolder();
+  ~WebRenderCompositableHolder();
 
 public:
-
-  virtual void Destroy();
-
-  void AddExternalImageId(uint64_t aExternalImageId, CompositableHost* aHost);
-  void RemoveExternalImageId(uint64_t aExternalImageId);
-  void UpdateExternalImages();
+  void Destroy();
+  void HoldExternalImage(const wr::Epoch& aEpoch, WebRenderTextureHost* aTexture);
+  void Update(const wr::Epoch& aEpoch);
 
 private:
 
-  // Holds CompositableHosts that are bound to external image ids.
-  nsDataHashtable<nsUint64HashKey, RefPtr<CompositableHost> > mCompositableHosts;
+  struct ForwardingTextureHosts {
+    ForwardingTextureHosts(const wr::Epoch& aEpoch, TextureHost* aTexture)
+      : mEpoch(aEpoch)
+      , mTexture(aTexture)
+    {}
+    wr::Epoch mEpoch;
+    CompositableTextureHostRef mTexture;
+  };
+
+  // Holds forwarding WebRenderTextureHosts.
+  std::queue<ForwardingTextureHosts> mWebRenderTextureHosts;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_WEBRENDERCOMPOSITABLE_HOLDER_H */
--- a/gfx/layers/wr/WebRenderContainerLayer.cpp
+++ b/gfx/layers/wr/WebRenderContainerLayer.cpp
@@ -10,77 +10,65 @@
 #include "LayersLogging.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 
 namespace mozilla {
 namespace layers {
 
 void
-WebRenderContainerLayer::RenderLayer()
+WebRenderContainerLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
 {
-  WrScrollFrameStackingContextGenerator scrollFrames(this);
+  nsTArray<LayerPolygon> children = SortChildrenBy3DZOrder(SortMode::WITHOUT_GEOMETRY);
 
-  nsTArray<LayerPolygon> children = SortChildrenBy3DZOrder(SortMode::WITHOUT_GEOMETRY);
   gfx::Matrix4x4 transform = GetTransform();
-  gfx::Rect relBounds = VisibleBoundsRelativeToParent();
-  if (!transform.IsIdentity()) {
-    // WR will only apply the 'translate' of the transform, so we need to do the scale/rotation manually.
-    gfx::Matrix4x4 boundTransform = transform;
-    boundTransform._41 = 0.0f;
-    boundTransform._42 = 0.0f;
-    boundTransform._43 = 0.0f;
-    relBounds.MoveTo(boundTransform.TransformPoint(relBounds.TopLeft()));
-  }
+  gfx::Rect relBounds = GetWrRelBounds();
+  gfx::Rect overflow(0, 0, relBounds.width, relBounds.height);
 
-  gfx::Rect overflow(0, 0, relBounds.width, relBounds.height);
-  WrMixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
-  Maybe<WrImageMask> mask = buildMaskLayer();
+  Maybe<WrImageMask> mask = BuildWrMaskLayer();
+
+  wr::MixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
 
   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));
+  aBuilder.PushStackingContext(wr::ToWrRect(relBounds),
+                               wr::ToWrRect(overflow),
+                               mask.ptrOr(nullptr),
+                               GetLocalOpacity(),
+                               //GetLayer()->GetAnimations(),
+                               transform,
+                               mixBlendMode);
   for (LayerPolygon& child : children) {
     if (child.layer->IsBackfaceHidden()) {
       continue;
     }
-    ToWebRenderLayer(child.layer)->RenderLayer();
+    ToWebRenderLayer(child.layer)->RenderLayer(aBuilder);
   }
-  WrBridge()->AddWebRenderCommand(
-    OpDPPopStackingContext());
+  aBuilder.PopStackingContext();
 }
 
 void
-WebRenderRefLayer::RenderLayer()
+WebRenderRefLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
 {
-  WrScrollFrameStackingContextGenerator scrollFrames(this);
+  gfx::Matrix4x4 transform;// = GetTransform();
+  gfx::Rect relBounds = TransformedVisibleBoundsRelativeToParent();
 
-  gfx::Rect relBounds = TransformedVisibleBoundsRelativeToParent();
-  gfx::Matrix4x4 transform;// = GetTransform();
+  WrClipRegion clipRegion = aBuilder.BuildClipRegion(wr::ToWrRect(relBounds));
 
   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::AsPipelineId(mId)));
+  aBuilder.PushIFrame(wr::ToWrRect(relBounds), clipRegion, wr::AsPipelineId(mId));
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderContainerLayer.h
+++ b/gfx/layers/wr/WebRenderContainerLayer.h
@@ -26,17 +26,17 @@ protected:
   virtual ~WebRenderContainerLayer()
   {
     ContainerLayer::RemoveAllChildren();
     MOZ_COUNT_DTOR(WebRenderContainerLayer);
   }
 
 public:
   Layer* GetLayer() override { return this; }
-  void RenderLayer() override;
+  void RenderLayer(wr::DisplayListBuilder& aBuilder) override;
 
   virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
   {
     DefaultComputeEffectiveTransforms(aTransformToSurface);
   }
 };
 
 class WebRenderRefLayer : public WebRenderLayer,
@@ -51,17 +51,17 @@ public:
 protected:
   virtual ~WebRenderRefLayer()
   {
     MOZ_COUNT_DTOR(WebRenderRefLayer);
   }
 
 public:
   Layer* GetLayer() override { return this; }
-  void RenderLayer() override;
+  void RenderLayer(wr::DisplayListBuilder& aBuilder) override;
 
   virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
   {
     DefaultComputeEffectiveTransforms(aTransformToSurface);
   }
 };
 
 } // namespace layers
--- a/gfx/layers/wr/WebRenderDisplayItemLayer.cpp
+++ b/gfx/layers/wr/WebRenderDisplayItemLayer.cpp
@@ -11,28 +11,29 @@
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "nsDisplayList.h"
 #include "mozilla/gfx/Matrix.h"
 
 namespace mozilla {
 namespace layers {
 
 void
-WebRenderDisplayItemLayer::RenderLayer()
+WebRenderDisplayItemLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
 {
   if (mItem) {
+    wr::DisplayListBuilder builder(WrBridge()->GetPipeline());
     // We might have recycled this layer. Throw away the old commands.
-    mCommands.Clear();
     mParentCommands.Clear();
-    mItem->CreateWebRenderCommands(mCommands, mParentCommands, this);
+    mItem->CreateWebRenderCommands(builder, mParentCommands, this);
+    mBuiltDisplayList = builder.Finalize();
   }
   // else we have an empty transaction and just use the
   // old commands.
 
-  WrBridge()->AddWebRenderCommands(mCommands);
+  aBuilder.PushBuiltDisplayList(Move(mBuiltDisplayList));
   WrBridge()->AddWebRenderParentCommands(mParentCommands);
 }
 
 uint64_t
 WebRenderDisplayItemLayer::SendImageContainer(ImageContainer* aContainer)
 {
   if (mImageContainer != aContainer) {
     AutoLockImage autoLock(aContainer);
--- a/gfx/layers/wr/WebRenderDisplayItemLayer.h
+++ b/gfx/layers/wr/WebRenderDisplayItemLayer.h
@@ -24,26 +24,25 @@ public:
     MOZ_COUNT_CTOR(WebRenderDisplayItemLayer);
   }
 
   uint64_t SendImageContainer(ImageContainer* aContainer);
 
 protected:
   virtual ~WebRenderDisplayItemLayer()
   {
-    mCommands.Clear();
     MOZ_COUNT_DTOR(WebRenderDisplayItemLayer);
   }
 
 public:
   Layer* GetLayer() override { return this; }
-  void RenderLayer() override;
+  void RenderLayer(wr::DisplayListBuilder& aBuilder) override;
 
 private:
-  nsTArray<WebRenderCommand> mCommands;
+  wr::BuiltDisplayList mBuiltDisplayList;
   nsTArray<WebRenderParentCommand> mParentCommands;
   RefPtr<ImageClient> mImageClient;
   RefPtr<ImageContainer> mImageContainer;
   uint64_t mExternalImageId;
 
 };
 
 } // namespace layers
--- a/gfx/layers/wr/WebRenderImageHost.cpp
+++ b/gfx/layers/wr/WebRenderImageHost.cpp
@@ -97,17 +97,20 @@ WebRenderImageHost::GetCompositionTime()
 {
   // XXX temporary workaround
   return TimeStamp::Now();
 }
 
 TextureHost*
 WebRenderImageHost::GetAsTextureHost(IntRect* aPictureRect)
 {
-  MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+  TimedImage* img = ChooseImage();
+  if (img) {
+    return img->mTextureHost;
+  }
   return nullptr;
 }
 
 void WebRenderImageHost::Attach(Layer* aLayer,
                        Compositor* aCompositor,
                        AttachFlags aFlags)
 {
   MOZ_ASSERT_UNREACHABLE("unexpected to be called");
--- a/gfx/layers/wr/WebRenderImageHost.h
+++ b/gfx/layers/wr/WebRenderImageHost.h
@@ -57,16 +57,18 @@ public:
   virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
 
   virtual bool Lock() override;
 
   virtual void Unlock() override;
 
   virtual void CleanupResources() override;
 
+  virtual WebRenderImageHost* AsWebRenderImageHost() override { return this; }
+
 protected:
   // ImageComposite
   virtual TimeStamp GetCompositionTime() const override;
 };
 
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/wr/WebRenderImageLayer.cpp
+++ b/gfx/layers/wr/WebRenderImageLayer.cpp
@@ -75,17 +75,17 @@ void
 WebRenderImageLayer::ClearCachedResources()
 {
   if (mImageClient) {
     mImageClient->ClearCachedResources();
   }
 }
 
 void
-WebRenderImageLayer::RenderLayer()
+WebRenderImageLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
 {
   if (!mContainer) {
      return;
   }
 
   CompositableType type = GetImageClientType();
   if (type == CompositableType::UNKNOWN) {
     return;
@@ -122,67 +122,113 @@ WebRenderImageLayer::RenderLayer()
     return;
   }
   gfx::IntSize size = image->GetSize();
 
   if (mImageClient && !mImageClient->UpdateImage(mContainer, /* unused */0)) {
     return;
   }
 
-  WrScrollFrameStackingContextGenerator scrollFrames(this);
+  gfx::Matrix4x4 transform = GetTransform();
+  gfx::Rect relBounds = GetWrRelBounds();
+  gfx::Rect overflow(0, 0, relBounds.width, relBounds.height);
 
-  Matrix4x4 transform = GetTransform();
-  Rect rect(0, 0, size.width, size.height);
-  Rect clip;
-  if (GetClipRect().isSome()) {
-      clip = RelativeToVisible(transform.Inverse().TransformBounds(IntRectToRect(GetClipRect().ref().ToUnknownRect())));
-  } else {
-      clip = rect;
+  gfx::Rect rect = gfx::Rect(0, 0, size.width, size.height);
+  if (mScaleMode != ScaleMode::SCALE_NONE) {
+    NS_ASSERTION(mScaleMode == ScaleMode::STRETCH,
+                 "No other scalemodes than stretch and none supported yet.");
+    rect = gfx::Rect(0, 0, mScaleToSize.width, mScaleToSize.height);
   }
+  rect = RelativeToVisible(rect);
+  gfx::Rect clipRect = GetWrClipRect(rect);
 
-  Rect relBounds = VisibleBoundsRelativeToParent();
-  if (!transform.IsIdentity()) {
-    // WR will only apply the 'translate' of the transform, so we need to do the scale/rotation manually.
-    gfx::Matrix4x4 boundTransform = transform;
-    boundTransform._41 = 0.0f;
-    boundTransform._42 = 0.0f;
-    boundTransform._43 = 0.0f;
-    relBounds.MoveTo(boundTransform.TransformPoint(relBounds.TopLeft()));
+  Maybe<WrImageMask> mask = BuildWrMaskLayer();
+  WrClipRegion clip = aBuilder.BuildClipRegion(wr::ToWrRect(clipRect));
+
+  wr::ImageRendering filter = wr::ToImageRendering(mSamplingFilter);
+  wr::MixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
+
+  DumpLayerInfo("Image Layer", rect);
+  if (gfxPrefs::LayersDump()) {
+    printf_stderr("ImageLayer %p texture-filter=%s \n",
+                  GetLayer(),
+                  Stringify(filter).c_str());
   }
 
-  Rect overflow(0, 0, relBounds.width, relBounds.height);
-  Maybe<WrImageMask> mask = buildMaskLayer();
-  wr::ImageRendering filter = wr::ToImageRendering(mSamplingFilter);
-  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));
   WrImageKey key;
   key.mNamespace = WrBridge()->GetNamespace();
   key.mHandle = WrBridge()->GetNextResourceId();
   WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(mExternalImageId, key));
-  WrBridge()->AddWebRenderCommand(OpDPPushImage(wr::ToWrRect(rect), wr::ToWrRect(clip), Nothing(), filter, key));
-  WrBridge()->AddWebRenderCommand(OpDPPopStackingContext());
+
+  aBuilder.PushStackingContext(wr::ToWrRect(relBounds),
+                            wr::ToWrRect(overflow),
+                            mask.ptrOr(nullptr),
+                            1.0f,
+                            //GetAnimations(),
+                            transform,
+                            mixBlendMode);
+  aBuilder.PushImage(wr::ToWrRect(rect), clip, filter, key);
+  aBuilder.PopStackingContext();
 
   //mContainer->SetImageFactory(originalIF);
 }
 
+Maybe<WrImageMask>
+WebRenderImageLayer::RenderMaskLayer()
+{
+  if (!mContainer) {
+     return Nothing();
+  }
+
+  CompositableType type = GetImageClientType();
+  if (type == CompositableType::UNKNOWN) {
+    return Nothing();
+  }
+
+  MOZ_ASSERT(GetImageClientType() != CompositableType::UNKNOWN);
+
+  if (GetImageClientType() == CompositableType::IMAGE && !mImageClient) {
+    mImageClient = ImageClient::CreateImageClient(CompositableType::IMAGE,
+                                                  WrBridge(),
+                                                  TextureFlags::DEFAULT);
+    if (!mImageClient) {
+      return Nothing();
+    }
+    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 Nothing();
+  }
+  if (mImageClient && !mImageClient->UpdateImage(mContainer, /* unused */0)) {
+    return Nothing();
+  }
+
+  WrImageKey key;
+  key.mNamespace = WrBridge()->GetNamespace();
+  key.mHandle = WrBridge()->GetNextResourceId();
+  WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(mExternalImageId, key));
+
+  gfx::IntSize size = image->GetSize();
+  WrImageMask imageMask;
+  imageMask.image = key;
+  imageMask.rect = wr::ToWrRect(Rect(0, 0, size.width, size.height));
+  imageMask.repeat = false;
+  return Some(imageMask);
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderImageLayer.h
+++ b/gfx/layers/wr/WebRenderImageLayer.h
@@ -27,17 +27,18 @@ protected:
 
   WebRenderLayerManager* Manager()
   {
     return static_cast<WebRenderLayerManager*>(mManager);
   }
 
 public:
   Layer* GetLayer() override { return this; }
-  void RenderLayer() override;
+  void RenderLayer(wr::DisplayListBuilder& aBuilder) override;
+  Maybe<WrImageMask> RenderMaskLayer() override;
 
 protected:
   CompositableType GetImageClientType();
 
   uint64_t mExternalImageId;
   RefPtr<ImageClient> mImageClient;
   CompositableType mImageClientTypeContainer;
 };
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -104,103 +104,91 @@ 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();
+WebRenderLayer::BuildWrMaskLayer()
+{
+  if (GetLayer()->GetMaskLayer()) {
+    WebRenderLayer* maskLayer = ToWebRenderLayer(GetLayer()->GetMaskLayer());
+    return maskLayer->RenderMaskLayer();
+  }
+
+  return Nothing();
+}
 
-  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());
-          WrImageKey maskKey;
-          maskKey.mNamespace = WrBridge()->GetNamespace();
-          maskKey.mHandle = WrBridge()->GetNextResourceId();
-          WrBridge()->SendAddImage(maskKey, size, map.GetStride(), SurfaceFormat::A8, buf);
+gfx::Rect
+WebRenderLayer::GetWrBoundsRect()
+{
+  LayerIntRect bounds = GetLayer()->GetVisibleRegion().GetBounds();
+  return Rect(0, 0, bounds.width, bounds.height);
+}
 
-          imageMask.image = maskKey;
-          imageMask.rect = wr::ToWrRect(Rect(0, 0, size.width, size.height));
-          imageMask.repeat = false;
-          WrManager()->AddImageKeyForDiscard(maskKey);
-          mask = Some(imageMask);
-      }
-    }
+gfx::Rect
+WebRenderLayer::GetWrClipRect(gfx::Rect& aRect)
+{
+  gfx::Rect clip;
+  Layer* layer = GetLayer();
+  Matrix4x4 transform = layer->GetTransform();
+  if (layer->GetClipRect().isSome()) {
+    clip = RelativeToVisible(transform.Inverse().TransformBounds(
+             IntRectToRect(layer->GetClipRect().ref().ToUnknownRect()))
+           );
+  } else {
+    clip = aRect;
   }
-  return mask;
+
+  return clip;
 }
 
-
-
-
-WrScrollFrameStackingContextGenerator::WrScrollFrameStackingContextGenerator(
-        WebRenderLayer* aLayer)
-  : mLayer(aLayer)
+gfx::Rect
+WebRenderLayer::GetWrRelBounds()
 {
-  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());
-    }
+  gfx::Rect relBounds = VisibleBoundsRelativeToParent();
+  gfx::Matrix4x4 transform = GetLayer()->GetTransform();
+  if (!transform.IsIdentity()) {
+    // WR will only apply the 'translate' of the transform, so we need to do the scale/rotation manually.
+    gfx::Matrix4x4 boundTransform = transform;
+    boundTransform._41 = 0.0f;
+    boundTransform._42 = 0.0f;
+    boundTransform._43 = 0.0f;
+    relBounds.MoveTo(boundTransform.TransformPoint(relBounds.TopLeft()));
+  }
 
-/*    mLayer->WrBridge()->AddWebRenderCommand(
-      OpDPPushScrollLayer(wr::ToWrRect(bounds),
-                          wr::ToWrRect(overflow),
-                          Nothing(),
-                          fm.GetScrollId()));*/
-  }
+  return relBounds;
 }
 
-WrScrollFrameStackingContextGenerator::~WrScrollFrameStackingContextGenerator()
+void
+WebRenderLayer::DumpLayerInfo(const char* aLayerType, gfx::Rect& aRect)
 {
-  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());
+  if (!gfxPrefs::LayersDump()) {
+    return;
   }
+
+  Matrix4x4 transform = GetLayer()->GetTransform();
+  Rect clip = GetWrClipRect(aRect);
+  Rect relBounds = GetWrRelBounds();
+  Rect overflow(0, 0, relBounds.width, relBounds.height);
+  WrMixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetLayer()->GetMixBlendMode());
+
+  printf_stderr("%s %p using bounds=%s, overflow=%s, transform=%s, rect=%s, clip=%s, mix-blend-mode=%s\n",
+                aLayerType,
+                GetLayer(),
+                Stringify(relBounds).c_str(),
+                Stringify(overflow).c_str(),
+                Stringify(transform).c_str(),
+                Stringify(aRect).c_str(),
+                Stringify(clip).c_str(),
+                Stringify(mixBlendMode).c_str());
 }
 
-
 WebRenderLayerManager::WebRenderLayerManager(nsIWidget* aWidget)
   : mWidget(aWidget)
   , mLatestTransactionId(0)
   , mTarget(nullptr)
 {
   MOZ_COUNT_CTOR(WebRenderLayerManager);
 }
 
@@ -213,24 +201,25 @@ WebRenderLayerManager::AsKnowsCompositor
 void
 WebRenderLayerManager::Initialize(PCompositorBridgeChild* aCBChild,
                                   wr::PipelineId aLayersId,
                                   TextureFactoryIdentifier* aTextureFactoryIdentifier)
 {
   MOZ_ASSERT(mWrChild == nullptr);
   MOZ_ASSERT(aTextureFactoryIdentifier);
 
+  LayoutDeviceIntSize size = mWidget->GetClientSize();
   TextureFactoryIdentifier textureFactoryIdentifier;
   uint32_t id_namespace;
   PWebRenderBridgeChild* bridge = aCBChild->SendPWebRenderBridgeConstructor(aLayersId,
+                                                                            size,
                                                                             &textureFactoryIdentifier,
                                                                             &id_namespace);
   MOZ_ASSERT(bridge);
   mWrChild = static_cast<WebRenderBridgeChild*>(bridge);
-  LayoutDeviceIntSize size = mWidget->GetClientSize();
   WrBridge()->SendCreate(size.ToUnknownSize());
   WrBridge()->IdentifyTextureHost(textureFactoryIdentifier);
   WrBridge()->SetNamespace(id_namespace);
   *aTextureFactoryIdentifier = textureFactoryIdentifier;
 }
 
 void
 WebRenderLayerManager::Destroy()
@@ -311,22 +300,23 @@ WebRenderLayerManager::EndTransaction(Dr
   // 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();
+  wr::DisplayListBuilder builder(WrBridge()->GetPipeline());
+  WebRenderLayer::ToWebRenderLayer(mRoot)->RenderLayer(builder);
 
   bool sync = mTarget != nullptr;
   mLatestTransactionId = mTransactionIdAllocator->GetTransactionId();
 
-  WrBridge()->DPEnd(size.ToUnknownSize(), sync, mLatestTransactionId);
+  WrBridge()->DPEnd(builder, size.ToUnknownSize(), sync, mLatestTransactionId);
 
   MakeSnapshotIfRequired(size);
 
   // this may result in Layers being deleted, which results in
   // PLayer::Send__delete__() and DeallocShmem()
   mKeepAlive.Clear();
 }
 
@@ -341,17 +331,17 @@ WebRenderLayerManager::MakeSnapshotIfReq
   // 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);
+                                            TextureFlags::SNAPSHOT);
   if (!texture) {
     return;
   }
 
   texture->InitIPDLActor(WrBridge());
   if (!texture->GetIPDLActor()) {
     return;
   }
--- a/gfx/layers/wr/WebRenderLayerManager.h
+++ b/gfx/layers/wr/WebRenderLayerManager.h
@@ -6,16 +6,17 @@
 #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"
+#include "mozilla/webrender/WebRenderAPI.h"
 
 class nsIWidget;
 
 namespace mozilla {
 namespace layers {
 
 class CompositorBridgeChild;
 class KnowsCompositor;
@@ -23,17 +24,22 @@ class PCompositorBridgeChild;
 class WebRenderBridgeChild;
 class WebRenderLayerManager;
 class APZCTreeManager;
 
 class WebRenderLayer
 {
 public:
   virtual Layer* GetLayer() = 0;
-  virtual void RenderLayer() = 0;
+  virtual void RenderLayer(wr::DisplayListBuilder& aBuilder) = 0;
+  virtual Maybe<WrImageMask> RenderMaskLayer()
+  {
+    MOZ_ASSERT(false);
+    return Nothing();
+  }
 
   virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() { return nullptr; }
   static inline WebRenderLayer*
   ToWebRenderLayer(Layer* aLayer)
   {
     return static_cast<WebRenderLayer*>(aLayer->ImplData());
   }
 
@@ -43,27 +49,21 @@ public:
   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 VisibleBoundsRelativeToParent();
   gfx::Point GetOffsetToParent();
   gfx::Rect TransformedVisibleBoundsRelativeToParent();
 protected:
-  Maybe<WrImageMask> buildMaskLayer();
-
-};
-
-class MOZ_RAII WrScrollFrameStackingContextGenerator
-{
-public:
-  explicit WrScrollFrameStackingContextGenerator(WebRenderLayer* aLayer);
-  ~WrScrollFrameStackingContextGenerator();
-private:
-  WebRenderLayer* mLayer;
+  gfx::Rect GetWrBoundsRect();
+  gfx::Rect GetWrRelBounds();
+  gfx::Rect GetWrClipRect(gfx::Rect& aRect);
+  void DumpLayerInfo(const char* aLayerType, gfx::Rect& aRect);
+  Maybe<WrImageMask> BuildWrMaskLayer();
 };
 
 class WebRenderLayerManager final : public LayerManager
 {
   typedef nsTArray<RefPtr<Layer> > LayerRefArray;
 
 public:
   explicit WebRenderLayerManager(nsIWidget* aWidget);
--- a/gfx/layers/wr/WebRenderMessageUtils.h
+++ b/gfx/layers/wr/WebRenderMessageUtils.h
@@ -94,185 +94,34 @@ struct ParamTraits<WrImageFormat>
   : public ContiguousEnumSerializer<
         WrImageFormat,
         WrImageFormat::Invalid,
         WrImageFormat::Sentinel>
 {
 };
 
 template<>
-struct ParamTraits<WrBorderStyle>
-  : public ContiguousEnumSerializer<
-        WrBorderStyle,
-        WrBorderStyle::None,
-        WrBorderStyle::Sentinel>
-{
-};
-
-template<>
-struct ParamTraits<WrColor>
-{
-  static void
-  Write(Message* aMsg, const WrColor& aParam)
-  {
-    WriteParam(aMsg, aParam.r);
-    WriteParam(aMsg, aParam.g);
-    WriteParam(aMsg, aParam.b);
-    WriteParam(aMsg, aParam.a);
-  }
-
-  static bool
-  Read(const Message* aMsg, PickleIterator* aIter, WrColor* aResult)
-  {
-    return ReadParam(aMsg, aIter, &aResult->r)
-        && ReadParam(aMsg, aIter, &aResult->g)
-        && ReadParam(aMsg, aIter, &aResult->b)
-        && ReadParam(aMsg, aIter, &aResult->a);
-  }
-};
-
-template<>
-struct ParamTraits<WrGlyphInstance>
-{
-  static void
-  Write(Message* aMsg, const WrGlyphInstance& aParam)
-  {
-    WriteParam(aMsg, aParam.index);
-    WriteParam(aMsg, aParam.x);
-    WriteParam(aMsg, aParam.y);
-  }
-
-  static bool
-  Read(const Message* aMsg, PickleIterator* aIter, WrGlyphInstance* aResult)
-  {
-    return ReadParam(aMsg, aIter, &aResult->index)
-        && ReadParam(aMsg, aIter, &aResult->x)
-        && ReadParam(aMsg, aIter, &aResult->y);
-  }
-};
-
-template<>
-struct ParamTraits<WrGlyphArray>
+struct ParamTraits<WrSize>
 {
   static void
-  Write(Message* aMsg, const WrGlyphArray& aParam)
-  {
-    WriteParam(aMsg, aParam.color);
-    size_t length = aParam.glyphs.Length();
-
-    WriteParam(aMsg, length);
-
-    for (size_t i = 0; i < length; i++) {
-      WriteParam(aMsg, aParam.glyphs[i]);
-    }
-  }
-
-  static bool
-  Read(const Message* aMsg, PickleIterator* aIter, WrGlyphArray* aResult)
-  {
-    if (!ReadParam(aMsg, aIter, &aResult->color)) {
-      return false;
-    }
-
-    size_t length;
-    if (!ReadParam(aMsg, aIter, &length)) {
-      return false;
-    }
-
-    aResult->glyphs.SetLength(length);
-
-    for (size_t i = 0; i < length; i++) {
-      if (!ReadParam(aMsg, aIter, &aResult->glyphs[i])) {
-        return false;
-      }
-    }
-
-    return true;
-  }
-};
-
-template<>
-struct ParamTraits<WrGradientStop>
-{
-  static void
-  Write(Message* aMsg, const WrGradientStop& aParam)
-  {
-    WriteParam(aMsg, aParam.offset);
-    WriteParam(aMsg, aParam.color);
-  }
-
-  static bool
-  Read(const Message* aMsg, PickleIterator* aIter, WrGradientStop* aResult)
-  {
-    return ReadParam(aMsg, aIter, &aResult->offset)
-        && ReadParam(aMsg, aIter, &aResult->color);
-  }
-};
-
-template<>
-struct ParamTraits<WrBorderSide>
-{
-  static void
-  Write(Message* aMsg, const WrBorderSide& aParam)
-  {
-    WriteParam(aMsg, aParam.width);
-    WriteParam(aMsg, aParam.color);
-    WriteParam(aMsg, aParam.style);
-  }
-
-  static bool
-  Read(const Message* aMsg, PickleIterator* aIter, WrBorderSide* aResult)
-  {
-    return ReadParam(aMsg, aIter, &aResult->width)
-        && ReadParam(aMsg, aIter, &aResult->color)
-        && ReadParam(aMsg, aIter, &aResult->style);
-  }
-};
-
-template<>
-struct ParamTraits<WrLayoutSize>
-{
-  static void
-  Write(Message* aMsg, const WrLayoutSize& aParam)
+  Write(Message* aMsg, const WrSize& aParam)
   {
     WriteParam(aMsg, aParam.width);
     WriteParam(aMsg, aParam.height);
   }
 
   static bool
-  Read(const Message* aMsg, PickleIterator* aIter, WrLayoutSize* aResult)
+  Read(const Message* aMsg, PickleIterator* aIter, WrSize* aResult)
   {
     return ReadParam(aMsg, aIter, &aResult->width)
         && ReadParam(aMsg, aIter, &aResult->height);
   }
 };
 
 template<>
-struct ParamTraits<WrBorderRadius>
-{
-  static void
-  Write(Message* aMsg, const WrBorderRadius& aParam)
-  {
-    WriteParam(aMsg, aParam.top_left);
-    WriteParam(aMsg, aParam.top_right);
-    WriteParam(aMsg, aParam.bottom_left);
-    WriteParam(aMsg, aParam.bottom_right);
-  }
-
-  static bool
-  Read(const Message* aMsg, PickleIterator* aIter, WrBorderRadius* aResult)
-  {
-    return ReadParam(aMsg, aIter, &aResult->top_left)
-        && ReadParam(aMsg, aIter, &aResult->top_right)
-        && ReadParam(aMsg, aIter, &aResult->bottom_left)
-        && ReadParam(aMsg, aIter, &aResult->bottom_right);
-  }
-};
-
-template<>
 struct ParamTraits<WrRect>
 {
   static void
   Write(Message* aMsg, const WrRect& aParam)
   {
     WriteParam(aMsg, aParam.x);
     WriteParam(aMsg, aParam.y);
     WriteParam(aMsg, aParam.width);
@@ -303,36 +152,16 @@ struct ParamTraits<WrPoint>
   Read(const Message* aMsg, PickleIterator* aIter, WrPoint* aResult)
   {
     return ReadParam(aMsg, aIter, &aResult->x) &&
            ReadParam(aMsg, aIter, &aResult->y);
   }
 };
 
 template<>
-struct ParamTraits<WrImageMask>
-{
-  static void
-  Write(Message* aMsg, const WrImageMask& aParam)
-  {
-    WriteParam(aMsg, aParam.image);
-    WriteParam(aMsg, aParam.rect);
-    WriteParam(aMsg, aParam.repeat);
-  }
-
-  static bool
-  Read(const Message* aMsg, PickleIterator* aIter, WrImageMask* aResult)
-  {
-    return ReadParam(aMsg, aIter, &aResult->image)
-        && ReadParam(aMsg, aIter, &aResult->rect)
-        && ReadParam(aMsg, aIter, &aResult->repeat);
-  }
-};
-
-template<>
 struct ParamTraits<WrBuiltDisplayListDescriptor>
 {
   static void
   Write(Message* aMsg, const WrBuiltDisplayListDescriptor& aParam)
   {
     WriteParam(aMsg, aParam.display_list_items_size);
   }
 
@@ -360,48 +189,11 @@ struct ParamTraits<WrAuxiliaryListsDescr
   {
     return ReadParam(aMsg, aIter, &aResult->gradient_stops_size)
         && ReadParam(aMsg, aIter, &aResult->complex_clip_regions_size)
         && ReadParam(aMsg, aIter, &aResult->filters_size)
         && ReadParam(aMsg, aIter, &aResult->glyph_instances_size);
   }
 };
 
-
-template<>
-struct ParamTraits<WrImageRendering>
-  : public ContiguousEnumSerializer<
-        WrImageRendering,
-        WrImageRendering::Auto,
-        WrImageRendering::Sentinel>
-{
-};
-
-template<>
-struct ParamTraits<WrMixBlendMode>
-  : public ContiguousEnumSerializer<
-        WrMixBlendMode,
-        WrMixBlendMode::Normal,
-        WrMixBlendMode::Sentinel>
-{
-};
-
-template<>
-struct ParamTraits<WrBoxShadowClipMode>
-  : public ContiguousEnumSerializer<
-        WrBoxShadowClipMode,
-        WrBoxShadowClipMode::None,
-        WrBoxShadowClipMode::Sentinel>
-{
-};
-
-template<>
-struct ParamTraits<WrGradientExtendMode>
-  : public ContiguousEnumSerializer<
-        WrGradientExtendMode,
-        WrGradientExtendMode::Clamp,
-        WrGradientExtendMode::Sentinel>
-{
-};
-
 } // namespace IPC
 
 #endif // GFX_WEBRENDERMESSAGEUTILS_H
--- a/gfx/layers/wr/WebRenderPaintedLayer.cpp
+++ b/gfx/layers/wr/WebRenderPaintedLayer.cpp
@@ -109,17 +109,17 @@ WebRenderPaintedLayer::RenderLayerWithRe
   if (aReadback && UsedForReadback()) {
     aReadback->GetPaintedLayerUpdates(this, &readbackUpdates);
   }
 
   PaintThebes(&readbackUpdates);
 }
 
 void
-WebRenderPaintedLayer::RenderLayer()
+WebRenderPaintedLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
 {
   // XXX We won't keep using ContentClient for WebRenderPaintedLayer in the future and
   // there is a crash problem for ContentClient on MacOS. So replace ContentClient with
   // ImageClient. See bug 1341001.
   //RenderLayerWithReadback(nullptr);
 
   if (!mImageContainer) {
     mImageContainer = LayerManager::CreateImageContainer();
@@ -177,69 +177,42 @@ WebRenderPaintedLayer::RenderLayer()
                                          ctx,
                                          visibleRegion.ToUnknownRegion(), visibleRegion.ToUnknownRegion(),
                                          DrawRegionClip::DRAW, nsIntRegion(), Manager()->GetPaintedLayerCallbackData());
   }
   RefPtr<TextureWrapperImage> image = new TextureWrapperImage(texture, IntRect(IntPoint(0, 0), imageSize));
   mImageContainer->SetCurrentImageInTransaction(image);
   if (!mImageClient->UpdateImage(mImageContainer, /* unused */0)) {
     return;
-   }
- 
-  WrScrollFrameStackingContextGenerator scrollFrames(this);
-
-  Matrix4x4 transform = GetTransform();
-
-  // Since we are creating a stacking context below using the visible region of
-  // this layer, we need to make sure the image display item has coordinates
-  // relative to the visible region.
-  Rect rect(0, 0, size.width, size.height);
-  Rect clip;
-  if (GetClipRect().isSome()) {
-      clip = RelativeToVisible(transform.Inverse().TransformBounds(IntRectToRect(GetClipRect().ref().ToUnknownRect())));
-  } else {
-      clip = rect;
   }
 
-  Maybe<WrImageMask> mask = buildMaskLayer();
-  Rect relBounds = VisibleBoundsRelativeToParent();
-  if (!transform.IsIdentity()) {
-    // WR will only apply the 'translate' of the transform, so we need to do the scale/rotation manually.
-    gfx::Matrix4x4 boundTransform = transform;
-    boundTransform._41 = 0.0f;
-    boundTransform._42 = 0.0f;
-    boundTransform._43 = 0.0f;
-    relBounds.MoveTo(boundTransform.TransformPoint(relBounds.TopLeft()));
-  }
+  gfx::Matrix4x4 transform = GetTransform();
+  gfx::Rect relBounds = GetWrRelBounds();
+  gfx::Rect overflow(0, 0, relBounds.width, relBounds.height);
 
-  Rect overflow(0, 0, relBounds.width, relBounds.height);
-  WrMixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
+  gfx::Rect rect(0, 0, size.width, size.height);
+  gfx::Rect clipRect = GetWrClipRect(rect);
 
-  if (gfxPrefs::LayersDump()) {
-    printf_stderr("PaintedLayer %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());
-  }
+  Maybe<WrImageMask> mask = BuildWrMaskLayer();
+  WrClipRegion clip = aBuilder.BuildClipRegion(wr::ToWrRect(clipRect));
 
-  WrBridge()->AddWebRenderCommand(
-      OpDPPushStackingContext(wr::ToWrRect(relBounds),
-                              wr::ToWrRect(overflow),
-                              mask,
-                              1.0f,
-                              GetAnimations(),
-                              transform,
-                              mixBlendMode,
-                              FrameMetrics::NULL_SCROLL_ID));
+  wr::MixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
+
+  DumpLayerInfo("PaintedLayer", rect);
+
   WrImageKey key;
   key.mNamespace = WrBridge()->GetNamespace();
   key.mHandle = WrBridge()->GetNextResourceId();
   WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(mExternalImageId, key));
-  WrBridge()->AddWebRenderCommand(OpDPPushImage(wr::ToWrRect(rect), wr::ToWrRect(clip), Nothing(), wr::ImageRendering::Auto, key));
-  WrBridge()->AddWebRenderCommand(OpDPPopStackingContext());
+
+  aBuilder.PushStackingContext(wr::ToWrRect(relBounds),
+                              wr::ToWrRect(overflow),
+                              mask.ptrOr(nullptr),
+                              1.0f,
+                              //GetAnimations(),
+                              transform,
+                              mixBlendMode);
+  aBuilder.PushImage(wr::ToWrRect(rect), clip, wr::ImageRendering::Auto, key);
+  aBuilder.PopStackingContext();
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderPaintedLayer.h
+++ b/gfx/layers/wr/WebRenderPaintedLayer.h
@@ -46,17 +46,17 @@ protected:
 public:
   virtual void InvalidateRegion(const nsIntRegion& aRegion) override
   {
     mInvalidRegion.Add(aRegion);
     mValidRegion.Sub(mValidRegion, mInvalidRegion.GetRegion());
   }
 
   Layer* GetLayer() override { return this; }
-  void RenderLayer() override;
+  void RenderLayer(wr::DisplayListBuilder& aBuilder) override;
   void PaintThebes(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates);
   void RenderLayerWithReadback(ReadbackProcessor *aReadback);
   RefPtr<ContentClient> mContentClient;
   RefPtr<ImageContainer> mImageContainer;
   RefPtr<ImageClient> mImageClient;
 };
 
 } // namespace layers
--- a/gfx/layers/wr/WebRenderTextLayer.cpp
+++ b/gfx/layers/wr/WebRenderTextLayer.cpp
@@ -13,17 +13,17 @@
 #include "mozilla/gfx/2D.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
 void
-WebRenderTextLayer::RenderLayer()
+WebRenderTextLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
 {
     if (mBounds.IsEmpty()) {
         return;
     }
 
     gfx::Rect rect = RelativeToParent(GetTransform().TransformBounds(IntRectToRect(mBounds)));
     gfx::Rect clip;
     if (GetClipRect().isSome()) {
@@ -34,16 +34,14 @@ WebRenderTextLayer::RenderLayer()
 
     if (gfxPrefs::LayersDump()) {
         printf_stderr("TextLayer %p using rect=%s, clip=%s\n",
                       this->GetLayer(),
                       Stringify(rect).c_str(),
                       Stringify(clip).c_str());
     }
 
-    nsTArray<WebRenderCommand> commands;
-    mGlyphHelper.BuildWebRenderCommands(WrBridge(), commands, mGlyphs, mFont,
+    mGlyphHelper.BuildWebRenderCommands(WrBridge(), aBuilder, mGlyphs, mFont,
                                         GetOffsetToParent(), rect, clip);
-    WrBridge()->AddWebRenderCommands(commands);
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderTextLayer.h
+++ b/gfx/layers/wr/WebRenderTextLayer.h
@@ -26,18 +26,18 @@ public:
 protected:
     virtual ~WebRenderTextLayer()
     {
         MOZ_COUNT_DTOR(WebRenderTextLayer);
     }
 
 public:
   Layer* GetLayer() override { return this; }
-  void RenderLayer() override;
+  void RenderLayer(wr::DisplayListBuilder& aBuilder) override;
 
 protected:
   gfx::WebRenderGlyphHelper mGlyphHelper;
 };
 
 } // namespace layers
 } // namespace mozilla
 
-#endif // GFX_WEBRENDERTEXTLAYER_H
\ No newline at end of file
+#endif // GFX_WEBRENDERTEXTLAYER_H
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderTextureHost.cpp
@@ -0,0 +1,125 @@
+/* -*- 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 "WebRenderTextureHost.h"
+
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/webrender/RenderTextureHost.h"
+#include "mozilla/webrender/RenderThread.h"
+
+namespace mozilla {
+namespace layers {
+
+uint64_t WebRenderTextureHost::sSerialCounter(0);
+
+WebRenderTextureHost::WebRenderTextureHost(TextureFlags aFlags,
+                                           TextureHost* aTexture)
+  : TextureHost(aFlags)
+  , mExternalImageId(++sSerialCounter)
+{
+  MOZ_COUNT_CTOR(WebRenderTextureHost);
+  mWrappedTextureHost = aTexture;
+
+  // XXX support only BufferTextureHost for now.
+  BufferTextureHost* bufferTexture = aTexture->AsBufferTextureHost();
+  MOZ_ASSERT(bufferTexture);
+  RefPtr<wr::RenderTextureHost> texture =
+    new wr::RenderTextureHost(bufferTexture->GetBuffer(),
+                              bufferTexture->GetBufferDescriptor());
+  wr::RenderThread::Get()->RegisterExternalImage(mExternalImageId, texture);
+}
+
+WebRenderTextureHost::~WebRenderTextureHost()
+{
+  MOZ_COUNT_DTOR(WebRenderTextureHost);
+  wr::RenderThread::Get()->UnregisterExternalImage(mExternalImageId);
+}
+
+bool
+WebRenderTextureHost::Lock()
+{
+  MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+  return false;
+}
+
+void
+WebRenderTextureHost::Unlock()
+{
+  MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+  return;
+}
+
+bool
+WebRenderTextureHost::BindTextureSource(CompositableTextureSourceRef& aTexture)
+{
+  MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+  return false;
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+WebRenderTextureHost::GetAsSurface()
+{
+  if (!mWrappedTextureHost) {
+    return nullptr;
+  }
+  return mWrappedTextureHost->GetAsSurface();
+}
+
+Compositor*
+WebRenderTextureHost::GetCompositor()
+{
+  //MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+  return nullptr;
+}
+
+void
+WebRenderTextureHost::SetCompositor(Compositor* aCompositor)
+{
+}
+
+YUVColorSpace
+WebRenderTextureHost::GetYUVColorSpace() const
+{
+  if (mWrappedTextureHost) {
+    return mWrappedTextureHost->GetYUVColorSpace();
+  }
+  return YUVColorSpace::UNKNOWN;
+}
+
+gfx::IntSize
+WebRenderTextureHost::GetSize() const
+{
+  if (!mWrappedTextureHost) {
+    return gfx::IntSize();
+  }
+  return mWrappedTextureHost->GetSize();
+}
+
+gfx::SurfaceFormat
+WebRenderTextureHost::GetFormat() const
+{
+  if (!mWrappedTextureHost) {
+    return gfx::SurfaceFormat::UNKNOWN;
+  }
+  return mWrappedTextureHost->GetFormat();
+}
+
+int32_t
+WebRenderTextureHost::GetRGBStride()
+{
+  if (!mWrappedTextureHost) {
+    return 0;
+  }
+  gfx::SurfaceFormat format = GetFormat();
+  if (GetFormat() == SurfaceFormat::YUV) {
+    // XXX this stride is used until yuv image rendering by webrender is used.
+    // Software converted RGB buffers strides are aliened to 16
+    return GetAlignedStride<16>(GetSize().width, BytesPerPixel(SurfaceFormat::B8G8R8A8));
+  }
+  return ImageDataSerializer::ComputeRGBStride(format, GetSize().width);
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderTextureHost.h
@@ -0,0 +1,60 @@
+/* -*- 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_WEBRENDERTEXTUREHOST_H
+#define MOZILLA_GFX_WEBRENDERTEXTUREHOST_H
+
+#include "mozilla/layers/TextureHost.h"
+
+namespace mozilla {
+namespace layers {
+
+class WebRenderTextureHost : public TextureHost
+{
+public:
+  WebRenderTextureHost(TextureFlags aFlags,
+                       TextureHost* aTexture);
+  virtual ~WebRenderTextureHost();
+
+  virtual void DeallocateDeviceData() override {}
+
+  virtual void SetCompositor(Compositor* aCompositor) override;
+
+  virtual Compositor* GetCompositor() override;
+
+  virtual bool Lock() override;
+
+  virtual void Unlock() override;
+
+  virtual gfx::SurfaceFormat GetFormat() const override;
+
+  virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+  virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+  virtual YUVColorSpace GetYUVColorSpace() const override;
+
+  virtual gfx::IntSize GetSize() const override;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+  virtual const char* Name() override { return "WebRenderTextureHost"; }
+#endif
+
+  virtual WebRenderTextureHost* AsWebRenderTextureHost() override { return this; }
+
+  uint64_t GetExternalImageKey() { return mExternalImageId; }
+
+  int32_t GetRGBStride();
+protected:
+  RefPtr<TextureHost> mWrappedTextureHost;
+  uint64_t mExternalImageId;
+
+  static uint64_t sSerialCounter;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_WEBRENDERTEXTUREHOST_H
--- a/gfx/tests/gtest/TestTextures.cpp
+++ b/gfx/tests/gtest/TestTextures.cpp
@@ -143,16 +143,17 @@ void TestTextureClientSurface(TextureCli
   // client serialization
   SurfaceDescriptor descriptor;
   ASSERT_TRUE(texture->ToSurfaceDescriptor(descriptor));
 
   ASSERT_NE(descriptor.type(), SurfaceDescriptor::Tnull_t);
 
   // host deserialization
   RefPtr<TextureHost> host = CreateBackendIndependentTextureHost(descriptor, nullptr,
+                                                                 LayersBackend::LAYERS_NONE,
                                                                  texture->GetFlags());
 
   ASSERT_TRUE(host.get() != nullptr);
   ASSERT_EQ(host->GetFlags(), texture->GetFlags());
 
   // host read
 
   // XXX - this can fail because lock tries to upload the texture but it needs a
@@ -187,16 +188,17 @@ void TestTextureClientYCbCr(TextureClien
   ASSERT_EQ(bufferDesc.desc().type(), BufferDescriptor::TYCbCrDescriptor);
   auto ycbcrDesc = bufferDesc.desc().get_YCbCrDescriptor();
   ASSERT_EQ(ycbcrDesc.ySize(), ycbcrData.mYSize);
   ASSERT_EQ(ycbcrDesc.cbCrSize(), ycbcrData.mCbCrSize);
   ASSERT_EQ(ycbcrDesc.stereoMode(), ycbcrData.mStereoMode);
 
   // host deserialization
   RefPtr<TextureHost> textureHost = CreateBackendIndependentTextureHost(descriptor, nullptr,
+                                                                        LayersBackend::LAYERS_NONE,
                                                                         client->GetFlags());
 
   RefPtr<BufferTextureHost> host = static_cast<BufferTextureHost*>(textureHost.get());
 
   ASSERT_TRUE(host.get() != nullptr);
   ASSERT_EQ(host->GetFlags(), client->GetFlags());
 
   // host read
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -455,23 +455,25 @@ private:
   DECL_GFX_PREF(Once, "image.multithreaded_decoding.limit",    ImageMTDecodingLimit, int32_t, -1);
 
   DECL_GFX_PREF(Once, "layers.acceleration.disabled",          LayersAccelerationDisabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps",          LayersDrawFPS, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.print-histogram",  FPSPrintHistogram, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.write-to-file", WriteFPSToFile, bool, false);
   DECL_GFX_PREF(Once, "layers.acceleration.force-enabled",     LayersAccelerationForceEnabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Live, "layers.advanced.border-layers",         LayersAllowBorderLayers, bool, false);
-  DECL_GFX_PREF(Live, "layers.advanced.text-layers",           LayersAllowTextLayers, bool, false);
-  DECL_GFX_PREF(Live, "layers.advanced.bullet-layers",         LayersAllowBulletLayers, bool, false);
-  DECL_GFX_PREF(Live, "layers.advanced.caret-layers",          LayersAllowCaretLayers, bool, false);
+  DECL_GFX_PREF(Live, "layers.advanced.boxshadow-inset-layers", LayersAllowInsetBoxShadow, bool, false);
   DECL_GFX_PREF(Live, "layers.advanced.boxshadow-outer-layers", LayersAllowOuterBoxShadow, bool, false);
-  DECL_GFX_PREF(Live, "layers.advanced.boxshadow-inset-layers", LayersAllowInsetBoxShadow, bool, false);
+  DECL_GFX_PREF(Live, "layers.advanced.bullet-layers",         LayersAllowBulletLayers, bool, false);
+  DECL_GFX_PREF(Live, "layers.advanced.button-foreground-layers", LayersAllowButtonForegroundLayers, bool, false);
+  DECL_GFX_PREF(Live, "layers.advanced.caret-layers",          LayersAllowCaretLayers, bool, false);
+  DECL_GFX_PREF(Live, "layers.advanced.displaybuttonborder-layers", LayersAllowDisplayButtonBorder, bool, false);
+  DECL_GFX_PREF(Live, "layers.advanced.image-layers",          LayersAllowImageLayers, bool, false);
   DECL_GFX_PREF(Live, "layers.advanced.outline-layers",        LayersAllowOutlineLayers, bool, false);
-  DECL_GFX_PREF(Live, "layers.advanced.image-layers",          LayersAllowImageLayers, bool, false);
+  DECL_GFX_PREF(Live, "layers.advanced.text-layers",           LayersAllowTextLayers, bool, false);
   DECL_GFX_PREF(Once, "layers.amd-switchable-gfx.enabled",     LayersAMDSwitchableGfxEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.async-pan-zoom.enabled",         AsyncPanZoomEnabledDoNotUseDirectly, bool, true);
   DECL_GFX_PREF(Once, "layers.async-pan-zoom.separate-event-thread", AsyncPanZoomSeparateEventThread, bool, false);
   DECL_GFX_PREF(Live, "layers.bench.enabled",                  LayersBenchEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.bufferrotation.enabled",         BufferRotationEnabled, bool, true);
   DECL_GFX_PREF(Live, "layers.child-process-shutdown",         ChildProcessShutdown, bool, true);
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
   // If MOZ_GFX_OPTIMIZE_MOBILE is defined, we force component alpha off
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -22,16 +22,17 @@
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/gfx/Swizzle.h"
 #include "mozilla/layers/WebRenderMessages.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtrExtensions.h"
 #include "mozilla/Vector.h"
 #include "mozilla/webrender/WebRenderTypes.h"
+#include "mozilla/webrender/WebRenderAPI.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIClipboardHelper.h"
 #include "nsIFile.h"
 #include "nsIGfxInfo.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsRegion.h"
@@ -1455,17 +1456,17 @@ WriteFontFileData(const uint8_t* aData, 
   helper->mFontData = fontData;
   helper->mFontDataLength = aLength;
   helper->mIndex = aIndex;
   helper->mGlyphSize = aGlyphSize;
 }
 
 void
 WebRenderGlyphHelper::BuildWebRenderCommands(WebRenderBridgeChild* aBridge,
-                                             nsTArray<WebRenderCommand>& aCommands,
+                                             wr::DisplayListBuilder& aBuilder,
                                              const nsTArray<GlyphArray>& aGlyphs,
                                              ScaledFont* aFont,
                                              const Point& aOffset,
                                              const Rect& aBounds,
                                              const Rect& aClip)
 {
   MOZ_ASSERT(aFont);
   MOZ_ASSERT(!aGlyphs.IsEmpty());
@@ -1476,36 +1477,34 @@ WebRenderGlyphHelper::BuildWebRenderComm
   wr::ByteBuffer fontBuffer(mFontDataLength, mFontData);
 
   WrFontKey key;
   key.mNamespace = aBridge->GetNamespace();
   key.mHandle = aBridge->GetNextResourceId();
 
   aBridge->SendAddRawFont(key, fontBuffer, mIndex);
 
-  nsTArray<WrGlyphArray> wr_glyphs;
-  wr_glyphs.SetLength(aGlyphs.Length());
+  WrClipRegion clipRegion = aBuilder.BuildClipRegion(wr::ToWrRect(aClip));
 
   for (size_t i = 0; i < aGlyphs.Length(); i++) {
     GlyphArray glyph_array = aGlyphs[i];
     nsTArray<gfx::Glyph>& glyphs = glyph_array.glyphs();
 
-    nsTArray<WrGlyphInstance>& wr_glyph_instances = wr_glyphs[i].glyphs;
+    nsTArray<WrGlyphInstance> wr_glyph_instances;
     wr_glyph_instances.SetLength(glyphs.Length());
-    wr_glyphs[i].color = glyph_array.color().value();
 
     for (size_t j = 0; j < glyphs.Length(); j++) {
       wr_glyph_instances[j].index = glyphs[j].mIndex;
       wr_glyph_instances[j].x = glyphs[j].mPosition.x - aOffset.x;
       wr_glyph_instances[j].y = glyphs[j].mPosition.y - aOffset.y;
     }
-  }
+    aBuilder.PushText(wr::ToWrRect(aBounds),
+                      clipRegion,
+                      glyph_array.color().value(),
+                      key,
+                      Range<const WrGlyphInstance>(wr_glyph_instances.Elements(), wr_glyph_instances.Length()),
+                      mGlyphSize);
 
-  aCommands.AppendElement(OpDPPushText(
-                            wr::ToWrRect(aBounds),
-                            wr::ToWrRect(aClip),
-                            wr_glyphs,
-                            mGlyphSize,
-                            key));
+  }
 }
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -30,16 +30,19 @@ namespace layers {
 class WebRenderBridgeChild;
 class GlyphArray;
 struct PlanarYCbCrData;
 class WebRenderCommand;
 } // namespace layers
 namespace image {
 class ImageRegion;
 } // namespace image
+namespace wr {
+class DisplayListBuilder;
+} // namespace wr
 } // namespace mozilla
 
 class gfxUtils {
 public:
     typedef mozilla::gfx::DataSourceSurface DataSourceSurface;
     typedef mozilla::gfx::DrawTarget DrawTarget;
     typedef mozilla::gfx::IntPoint IntPoint;
     typedef mozilla::gfx::Matrix Matrix;
@@ -323,17 +326,17 @@ public:
   ~WebRenderGlyphHelper()
   {
     if (mFontData) {
       free(mFontData);
     }
   }
 
   void BuildWebRenderCommands(layers::WebRenderBridgeChild* aChild,
-                              nsTArray<layers::WebRenderCommand>& aCommands,
+                              wr::DisplayListBuilder& aBuilder,
                               const nsTArray<layers::GlyphArray>& aGlyphs,
                               ScaledFont* aFont,
                               const Point& aOffset,
                               const Rect& aBounds,
                               const Rect& aClip);
 
 public:
   uint8_t* mFontData;
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -1,46 +1,44 @@
 [package]
 name = "webrender"
-version = "0.19.0"
+version = "0.24.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 build = "build.rs"
 
 [features]
-default = ["codegen", "freetype-lib"]
-codegen = ["webrender_traits/codegen"]
+default = ["freetype-lib"]
 freetype-lib = ["freetype/servo-freetype-sys"]
-serde_derive = ["webrender_traits/serde_derive"]
 profiler = ["thread_profiler/thread_profiler"]
 
 [dependencies]
 app_units = "0.4"
 bincode = "1.0.0-alpha2"
 bit-set = "0.4"
 byteorder = "1.0"
 euclid = "0.11"
-fnv="1.0"
+fnv = "1.0"
 gleam = "0.2.30"
 lazy_static = "0.2"
 log = "0.3"
 num-traits = "0.1.32"
 offscreen_gl_context = {version = "0.6", features = ["serde", "osmesa"]}
 time = "0.1"
 threadpool = "1.3.2"
-webrender_traits = {path = "../webrender_traits", default-features = false}
+webrender_traits = {path = "../webrender_traits"}
 bitflags = "0.7"
 gamma-lut = "0.1"
 thread_profiler = "0.1.1"
 
 [dev-dependencies]
 angle = {git = "https://github.com/servo/angle", branch = "servo"}
 
 [target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
 freetype = { version = "0.2", default-features = false }
 
 [target.'cfg(target_os = "windows")'.dependencies]
-servo-dwrote = "0.2"
+dwrote = "0.3"
 
 [target.'cfg(target_os = "macos")'.dependencies]
 core-graphics = "0.7.0"
 core-text = "4.0"
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -620,22 +620,19 @@ struct YuvImage {
     vec4 v_st_rect;
     vec2 size;
     int color_space;
 };
 
 YuvImage fetch_yuv_image(int index) {
     YuvImage image;
 
-    ivec2 uv = get_fetch_uv_4(index);
+    ivec2 uv = get_fetch_uv_1(index);
 
-    image.y_st_rect = texelFetchOffset(sData64, uv, 0, ivec2(0, 0));
-    image.u_st_rect = texelFetchOffset(sData64, uv, 0, ivec2(1, 0));
-    image.v_st_rect = texelFetchOffset(sData64, uv, 0, ivec2(2, 0));
-    vec4 size_color_space = texelFetchOffset(sData64, uv, 0, ivec2(3, 0));
+    vec4 size_color_space = texelFetchOffset(sData16, uv, 0, ivec2(0, 0));
     image.size = size_color_space.xy;
     image.color_space = int(size_color_space.z);
 
     return image;
 }
 
 struct BoxShadow {
     vec4 src_rect;
--- a/gfx/webrender/res/ps_composite.fs.glsl
+++ b/gfx/webrender/res/ps_composite.fs.glsl
@@ -146,73 +146,91 @@ vec3 Saturation(vec3 Cb, vec3 Cs) {
 vec3 Color(vec3 Cb, vec3 Cs) {
     return SetLum(Cs, Lum(Cb));
 }
 
 vec3 Luminosity(vec3 Cb, vec3 Cs) {
     return SetLum(Cb, Lum(Cs));
 }
 
+const int MixBlendMode_Multiply    = 1;
+const int MixBlendMode_Screen      = 2;
+const int MixBlendMode_Overlay     = 3;
+const int MixBlendMode_Darken      = 4;
+const int MixBlendMode_Lighten     = 5;
+const int MixBlendMode_ColorDodge  = 6;
+const int MixBlendMode_ColorBurn   = 7;
+const int MixBlendMode_HardLight   = 8;
+const int MixBlendMode_SoftLight   = 9;
+const int MixBlendMode_Difference  = 10;
+const int MixBlendMode_Exclusion   = 11;
+const int MixBlendMode_Hue         = 12;
+const int MixBlendMode_Saturation  = 13;
+const int MixBlendMode_Color       = 14;
+const int MixBlendMode_Luminosity  = 15;
+
 void main(void) {
     vec4 Cb = texture(sCache, vUv0);
     vec4 Cs = texture(sCache, vUv1);
 
     // Return yellow if none of the branches match (shouldn't happen).
     vec4 result = vec4(1.0, 1.0, 0.0, 1.0);
 
     switch (vOp) {
-        case 1:
+        case MixBlendMode_Multiply:
             result.rgb = Multiply(Cb.rgb, Cs.rgb);
             break;
-        case 2:
+        case MixBlendMode_Screen:
             result.rgb = Screen(Cb.rgb, Cs.rgb);
             break;
-        case 3:
-            result.rgb = HardLight(Cs.rgb, Cb.rgb);        // Overlay is inverse of Hardlight
+        case MixBlendMode_Overlay:
+            // Overlay is inverse of Hardlight
+            result.rgb = HardLight(Cs.rgb, Cb.rgb);
             break;
-        case 4:
-            // mix-blend-mode: darken
+        case MixBlendMode_Darken:
             result.rgb = min(Cs.rgb, Cb.rgb);
             break;
-        case 5:
-            // mix-blend-mode: lighten
+        case MixBlendMode_Lighten:
             result.rgb = max(Cs.rgb, Cb.rgb);
             break;
-        case 6:
+        case MixBlendMode_ColorDodge:
             result.r = ColorDodge(Cb.r, Cs.r);
             result.g = ColorDodge(Cb.g, Cs.g);
             result.b = ColorDodge(Cb.b, Cs.b);
             break;
-        case 7:
+        case MixBlendMode_ColorBurn:
             result.r = ColorBurn(Cb.r, Cs.r);
             result.g = ColorBurn(Cb.g, Cs.g);
             result.b = ColorBurn(Cb.b, Cs.b);
             break;
-        case 8:
+        case MixBlendMode_HardLight:
             result.rgb = HardLight(Cb.rgb, Cs.rgb);
             break;
-        case 9:
+        case MixBlendMode_SoftLight:
             result.r = SoftLight(Cb.r, Cs.r);
             result.g = SoftLight(Cb.g, Cs.g);
             result.b = SoftLight(Cb.b, Cs.b);
             break;
-        case 10:
+        case MixBlendMode_Difference:
             result.rgb = Difference(Cb.rgb, Cs.rgb);
             break;
-        case 11:
+        case MixBlendMode_Exclusion:
             result.rgb = Exclusion(Cb.rgb, Cs.rgb);
             break;
-        case 12:
+        case MixBlendMode_Hue:
             result.rgb = Hue(Cb.rgb, Cs.rgb);
             break;
-        case 13:
+        case MixBlendMode_Saturation:
             result.rgb = Saturation(Cb.rgb, Cs.rgb);
             break;
-        case 14:
+        case MixBlendMode_Color:
             result.rgb = Color(Cb.rgb, Cs.rgb);
             break;
-        case 15:
+        case MixBlendMode_Luminosity:
             result.rgb = Luminosity(Cb.rgb, Cs.rgb);
             break;
     }
 
+    result.rgb = (1.0 - Cb.a) * Cs.rgb + Cb.a * result.rgb;
+    result.a = Cs.a;
+
     oFragColor = result;
 }
--- a/gfx/webrender/res/ps_composite.vs.glsl
+++ b/gfx/webrender/res/ps_composite.vs.glsl
@@ -14,18 +14,18 @@ void main(void) {
                        src_task.screen_space_origin;
 
     vec2 local_pos = mix(dest_origin,
                          dest_origin + src_task.size,
                          aPosition.xy);
 
     vec2 texture_size = vec2(textureSize(sCache, 0));
 
-    vec2 st0 = (backdrop_task.render_target_origin + vec2(0.0, backdrop_task.size.y)) / texture_size;
-    vec2 st1 = (backdrop_task.render_target_origin + vec2(backdrop_task.size.x, 0.0)) / texture_size;
+    vec2 st0 = backdrop_task.render_target_origin / texture_size;
+    vec2 st1 = (backdrop_task.render_target_origin + backdrop_task.size) / texture_size;
     vUv0 = vec3(mix(st0, st1, aPosition.xy), backdrop_task.render_target_layer_index);
 
     st0 = src_task.render_target_origin / texture_size;
     st1 = (src_task.render_target_origin + src_task.size) / texture_size;
     vUv1 = vec3(mix(st0, st1, aPosition.xy), src_task.render_target_layer_index);
 
     vOp = pi.sub_index;
 
--- a/gfx/webrender/res/ps_gradient.vs.glsl
+++ b/gfx/webrender/res/ps_gradient.vs.glsl
@@ -7,35 +7,43 @@ void main(void) {
     Primitive prim = load_primitive();
     Gradient gradient = fetch_gradient(prim.prim_index);
 
     GradientStop g0 = fetch_gradient_stop(prim.sub_index + 0);
     GradientStop g1 = fetch_gradient_stop(prim.sub_index + 1);
 
     RectWithSize segment_rect;
     vec2 axis;
+    vec4 adjusted_color_g0 = g0.color;
+    vec4 adjusted_color_g1 = g1.color;
     if (gradient.start_end_point.y == gradient.start_end_point.w) {
-        float x0 = mix(gradient.start_end_point.x,
-                       gradient.start_end_point.z,
-                       g0.offset.x);
-        float x1 = mix(gradient.start_end_point.x,
-                       gradient.start_end_point.z,
-                       g1.offset.x);
-        segment_rect.p0 = vec2(x0, prim.local_rect.p0.y);
-        segment_rect.size = vec2(x1 - x0, prim.local_rect.size.y);
+        vec2 x = mix(gradient.start_end_point.xx, gradient.start_end_point.zz,
+                     vec2(g0.offset.x, g1.offset.x));
+        // The start and end point of gradient might exceed the geometry rect. So clamp
+        // it to the geometry rect.
+        x = clamp(x, prim.local_rect.p0.xx, prim.local_rect.p0.xx + prim.local_rect.size.xx);
+        vec2 adjusted_offset =
+            (x - gradient.start_end_point.xx) / (gradient.start_end_point.zz - gradient.start_end_point.xx);
+        adjusted_color_g0 = mix(g0.color, g1.color, adjusted_offset.x);
+        adjusted_color_g1 = mix(g0.color, g1.color, adjusted_offset.y);
+        segment_rect.p0 = vec2(x.x, prim.local_rect.p0.y);
+        segment_rect.size = vec2(x.y - x.x, prim.local_rect.size.y);
         axis = vec2(1.0, 0.0);
     } else {
-        float y0 = mix(gradient.start_end_point.y,
-                       gradient.start_end_point.w,
-                       g0.offset.x);
-        float y1 = mix(gradient.start_end_point.y,
-                       gradient.start_end_point.w,
-                       g1.offset.x);
-        segment_rect.p0 = vec2(prim.local_rect.p0.x, y0);
-        segment_rect.size = vec2(prim.local_rect.size.x, y1 - y0);
+        vec2 y = mix(gradient.start_end_point.yy, gradient.start_end_point.ww,
+                     vec2(g0.offset.x, g1.offset.x));
+        // The start and end point of gradient might exceed the geometry rect. So clamp
+        // it to the geometry rect.
+        y = clamp(y, prim.local_rect.p0.yy, prim.local_rect.p0.yy + prim.local_rect.size.yy);
+        vec2 adjusted_offset =
+            (y - gradient.start_end_point.yy) / (gradient.start_end_point.ww - gradient.start_end_point.yy);
+        adjusted_color_g0 = mix(g0.color, g1.color, adjusted_offset.x);
+        adjusted_color_g1 = mix(g0.color, g1.color, adjusted_offset.y);
+        segment_rect.p0 = vec2(prim.local_rect.p0.x, y.x);
+        segment_rect.size = vec2(prim.local_rect.size.x, y.y - y.x);
         axis = vec2(0.0, 1.0);
     }
 
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(segment_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
@@ -51,10 +59,10 @@ void main(void) {
                                  prim.task);
 
     vec2 f = (vi.local_pos - segment_rect.p0) / segment_rect.size;
     vPos = vi.local_pos;
 #endif
 
     write_clip(vi.screen_pos, prim.clip_area);
 
-    vColor = mix(g0.color, g1.color, dot(f, axis));
+    vColor = mix(adjusted_color_g0, adjusted_color_g1, dot(f, axis));
 }
--- a/gfx/webrender/res/ps_image.fs.glsl
+++ b/gfx/webrender/res/ps_image.fs.glsl
@@ -26,10 +26,10 @@ void main(void) {
     vec2 position_in_tile = mod(relative_pos_in_rect, vStretchSize + vTileSpacing);
     // We clamp the texture coordinates to the half-pixel offset from the borders
     // in order to avoid sampling outside of the texture area.
     vec2 st = vTextureOffset + ((position_in_tile / vStretchSize) * vTextureSize);
     st = clamp(st, vStRect.xy, vStRect.zw);
 
     alpha = alpha * float(all(bvec2(step(position_in_tile, vStretchSize))));
 
-    oFragColor = vec4(1.0, 1.0, 1.0, alpha) * textureLod(sColor0, st, 0.0);
+    oFragColor = vec4(alpha) * textureLod(sColor0, st, 0.0);
 }
--- a/gfx/webrender/res/ps_yuv_image.vs.glsl
+++ b/gfx/webrender/res/ps_yuv_image.vs.glsl
@@ -18,30 +18,33 @@ void main(void) {
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task);
     vLocalPos = vi.local_pos - vi.local_rect.p0;
 #endif
 
     YuvImage image = fetch_yuv_image(prim.prim_index);
+    ResourceRect y_rect = fetch_resource_rect(prim.user_data.x);
+    ResourceRect u_rect = fetch_resource_rect(prim.user_data.x + 1);
+    ResourceRect v_rect = fetch_resource_rect(prim.user_data.x + 2);
 
     vec2 y_texture_size = vec2(textureSize(sColor0, 0));
-    vec2 y_st0 = image.y_st_rect.xy / y_texture_size;
-    vec2 y_st1 = image.y_st_rect.zw / y_texture_size;
+    vec2 y_st0 = y_rect.uv_rect.xy / y_texture_size;
+    vec2 y_st1 = y_rect.uv_rect.zw / y_texture_size;
 
     vTextureSizeY = y_st1 - y_st0;
     vTextureOffsetY = y_st0;
 
     vec2 uv_texture_size = vec2(textureSize(sColor1, 0));
-    vec2 u_st0 = image.u_st_rect.xy / uv_texture_size;
-    vec2 u_st1 = image.u_st_rect.zw / uv_texture_size;
+    vec2 u_st0 = u_rect.uv_rect.xy / uv_texture_size;
+    vec2 u_st1 = u_rect.uv_rect.zw / uv_texture_size;
 
-    vec2 v_st0 = image.v_st_rect.xy / uv_texture_size;
-    vec2 v_st1 = image.v_st_rect.zw / uv_texture_size;
+    vec2 v_st0 = v_rect.uv_rect.xy / uv_texture_size;
+    vec2 v_st1 = v_rect.uv_rect.zw / uv_texture_size;
 
     // This assumes the U and V surfaces have the same size.
     vTextureSizeUv = u_st1 - u_st0;
     vTextureOffsetU = u_st0;
     vTextureOffsetV = v_st0;
 
     vStretchSize = image.size;
 
--- a/gfx/webrender/src/clip_scroll_node.rs
+++ b/gfx/webrender/src/clip_scroll_node.rs
@@ -1,74 +1,171 @@
 /* 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/. */
 
 use euclid::Point3D;
 use geometry::ray_intersects_rect;
+use mask_cache::{ClipSource, MaskCacheInfo};
+use prim_store::GpuBlock32;
+use renderer::VertexDataStore;
 use spring::{DAMPING, STIFFNESS, Spring};
-use webrender_traits::{LayerPoint, LayerRect, LayerSize, LayerToScrollTransform};
-use webrender_traits::{LayerToWorldTransform, PipelineId, ScrollEventPhase, ScrollLayerId};
-use webrender_traits::{ScrollLayerRect, ScrollLocation, ScrollToWorldTransform, WorldPoint};
-use webrender_traits::{WorldPoint4D};
+use tiling::PackedLayerIndex;
+use util::TransformedRect;
+use webrender_traits::{ClipRegion, LayerPixel, LayerPoint, LayerRect, LayerSize};
+use webrender_traits::{LayerToScrollTransform, LayerToWorldTransform, PipelineId};
+use webrender_traits::{ScrollEventPhase, ScrollLayerId, ScrollLayerRect, ScrollLocation};
+use webrender_traits::{ServoScrollRootId, WorldPoint, WorldPoint4D};
 
 #[cfg(target_os = "macos")]
 const CAN_OVERSCROLL: bool = true;
 
 #[cfg(not(target_os = "macos"))]
 const CAN_OVERSCROLL: bool = false;
 
+#[derive(Clone, Debug)]
+pub struct ClipInfo {
+    /// The ClipSource for this node, which is used to generate mask_cache_info.
+    pub clip_source: ClipSource,
+
+    /// The MaskCacheInfo for this node, which is produced by processing the
+    /// provided ClipSource.
+    pub mask_cache_info: Option<MaskCacheInfo>,
+
+    /// The packed layer index for this node, which is used to render a clip mask
+    /// for it, if necessary.
+    pub packed_layer_index: PackedLayerIndex,
+
+    /// The final transformed rectangle of this clipping region for this node,
+    /// which depends on the screen rectangle and the transformation of all of
+    /// the parents.
+    pub xf_rect: Option<TransformedRect>,
+
+    /// An external identifier that is used to scroll this clipping node
+    /// from the API.
+    pub scroll_root_id: Option<ServoScrollRootId>,
+}
+
+impl ClipInfo {
+    pub fn new(clip_region: &ClipRegion,
+               clip_store: &mut VertexDataStore<GpuBlock32>,
+               packed_layer_index: PackedLayerIndex,
+               scroll_root_id: Option<ServoScrollRootId>)
+               -> ClipInfo {
+        // We pass true here for the MaskCacheInfo because this type of
+        // mask needs an extra clip for the clip rectangle.
+        let clip_source = ClipSource::Region(clip_region.clone());
+        ClipInfo {
+            mask_cache_info: MaskCacheInfo::new(&clip_source, true, clip_store),
+            clip_source: clip_source,
+            packed_layer_index: packed_layer_index,
+            xf_rect: None,
+            scroll_root_id: scroll_root_id,
+        }
+    }
+
+    pub fn is_masking(&self) -> bool {
+        match self.mask_cache_info {
+            Some(ref info) => info.is_masking(),
+            _ => false,
+        }
+    }
+
+}
+#[derive(Clone, Debug)]
+pub enum NodeType {
+    /// Transform for this layer, relative to parent reference frame. A reference
+    /// frame establishes a new coordinate space in the tree.
+    ReferenceFrame(LayerToScrollTransform),
+
+    /// Other nodes just do clipping, but no transformation.
+    Clip(ClipInfo),
+}
 
 /// Contains scrolling and transform information stacking contexts.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct ClipScrollNode {
     /// Manages scrolling offset, overscroll state etc.
     pub scrolling: ScrollingState,
 
     /// Size of the content inside the scroll region (in logical pixels)
     pub content_size: LayerSize,
 
-    /// Viewing rectangle
+    /// Viewing rectangle in the coordinate system of the parent reference frame.
     pub local_viewport_rect: LayerRect,
 
-    /// Viewing rectangle clipped against parent layer(s)
+    /// Clip rect of this node - typically the same as viewport rect, except
+    /// in overscroll cases.
+    pub local_clip_rect: LayerRect,
+
+    /// Viewport rectangle clipped against parent layer(s) viewport rectangles.
+    /// This is in the coordinate system of the parent reference frame.
     pub combined_local_viewport_rect: LayerRect,
 
-    /// World transform for the viewport rect itself.
+    /// World transform for the viewport rect itself. This is the parent
+    /// reference frame transformation plus the scrolling offsets provided by
+    /// the nodes in between the reference frame and this node.
     pub world_viewport_transform: LayerToWorldTransform,
 
-    /// World transform for content within this layer
+    /// World transform for content transformed by this node.
     pub world_content_transform: LayerToWorldTransform,
 
-    /// Transform for this layer, relative to parent scrollable layer.
-    pub local_transform: LayerToScrollTransform,
-
     /// Pipeline that this layer belongs to
     pub pipeline_id: PipelineId,
 
+    /// Parent layer. If this is None, we are the root node.
+    pub parent: Option<ScrollLayerId>,
+
     /// Child layers
     pub children: Vec<ScrollLayerId>,
+
+    /// Whether or not this node is a reference frame.
+    pub node_type: NodeType,
 }
 
 impl ClipScrollNode {
-    pub fn new(local_viewport_rect: &LayerRect,
+    pub fn new(pipeline_id: PipelineId,
+               parent_id: ScrollLayerId,
+               local_viewport_rect: &LayerRect,
                content_size: LayerSize,
-               local_transform: &LayerToScrollTransform,
-               pipeline_id: PipelineId)
+               clip_info: ClipInfo)
                -> ClipScrollNode {
         ClipScrollNode {
             scrolling: ScrollingState::new(),
             content_size: content_size,
             local_viewport_rect: *local_viewport_rect,
-            combined_local_viewport_rect: *local_viewport_rect,
+            local_clip_rect: *local_viewport_rect,
+            combined_local_viewport_rect: LayerRect::zero(),
             world_viewport_transform: LayerToWorldTransform::identity(),
             world_content_transform: LayerToWorldTransform::identity(),
-            local_transform: *local_transform,
+            parent: Some(parent_id),
             children: Vec::new(),
             pipeline_id: pipeline_id,
+            node_type: NodeType::Clip(clip_info),
+        }
+    }
+
+    pub fn new_reference_frame(parent_id: Option<ScrollLayerId>,
+                               local_viewport_rect: &LayerRect,
+                               content_size: LayerSize,
+                               local_transform: &LayerToScrollTransform,
+                               pipeline_id: PipelineId)
+                               -> ClipScrollNode {
+        ClipScrollNode {
+            scrolling: ScrollingState::new(),
+            content_size: content_size,
+            local_viewport_rect: *local_viewport_rect,
+            local_clip_rect: *local_viewport_rect,
+            combined_local_viewport_rect: LayerRect::zero(),
+            world_viewport_transform: LayerToWorldTransform::identity(),
+            world_content_transform: LayerToWorldTransform::identity(),
+            parent: parent_id,
+            children: Vec::new(),
+            pipeline_id: pipeline_id,
+            node_type: NodeType::ReferenceFrame(*local_transform),
         }
     }
 
     pub fn add_child(&mut self, child: ScrollLayerId) {
         self.children.push(child);
     }
 
     pub fn finalize(&mut self, scrolling: &ScrollingState) {
@@ -112,40 +209,69 @@ impl ClipScrollNode {
 
         self.scrolling.offset = new_offset;
         self.scrolling.bouncing_back = false;
         self.scrolling.started_bouncing_back = false;
         return true;
     }
 
     pub fn update_transform(&mut self,
-                            parent_world_transform: &ScrollToWorldTransform,
-                            parent_viewport_rect: &ScrollLayerRect) {
-        let inv_transform = match self.local_transform.inverse() {
+                            parent_reference_frame_transform: &LayerToWorldTransform,
+                            parent_combined_viewport_rect: &ScrollLayerRect,
+                            parent_accumulated_scroll_offset: LayerPoint) {
+
+        let local_transform = match self.node_type {
+            NodeType::ReferenceFrame(transform) => transform,
+            NodeType::Clip(_) => LayerToScrollTransform::identity(),
+        };
+
+        let inv_transform = match local_transform.inverse() {
             Some(transform) => transform,
             None => {
                 // If a transform function causes the current transformation matrix of an object
                 // to be non-invertible, the object and its content do not get displayed.
                 self.combined_local_viewport_rect = LayerRect::zero();
                 return;
             }
         };
 
-        let parent_viewport_rect_in_local_space = inv_transform.transform_rect(parent_viewport_rect)
-                                                               .translate(&-self.scrolling.offset);
-        let local_viewport_rect = self.local_viewport_rect.translate(&-self.scrolling.offset);
-        let viewport_rect = parent_viewport_rect_in_local_space.intersection(&local_viewport_rect)
-                                                               .unwrap_or(LayerRect::zero());
+        // We are trying to move the combined viewport rectangle of our parent nodes into the
+        // coordinate system of this node, so we must invert our transformation (only for
+        // reference frames) and then apply the scroll offset of all the parent layers.
+        let parent_combined_viewport_in_local_space =
+            inv_transform.pre_translated(-parent_accumulated_scroll_offset.x,
+                                         -parent_accumulated_scroll_offset.y,
+                                         0.0)
+                         .transform_rect(parent_combined_viewport_rect);
+
+        // Now that we have the combined viewport rectangle of the parent nodes in local space,
+        // we do the intersection and get our combined viewport rect in the coordinate system
+        // starting from our origin.
+        self.combined_local_viewport_rect =
+            parent_combined_viewport_in_local_space.intersection(&self.local_clip_rect)
+                                                    .unwrap_or(LayerRect::zero());
 
-        self.combined_local_viewport_rect = viewport_rect;
-        self.world_viewport_transform = parent_world_transform.pre_mul(&self.local_transform);
-        self.world_content_transform = self.world_viewport_transform
-                                                     .pre_translated(self.scrolling.offset.x,
-                                                                     self.scrolling.offset.y,
-                                                                     0.0);
+        // The transformation for this viewport in world coordinates is the transformation for
+        // our parent reference frame, plus any accumulated scrolling offsets from nodes
+        // between our reference frame and this node. For reference frames, we also include
+        // whatever local transformation this reference frame provides. This can be combined
+        // with the local_viewport_rect to get its position in world space.
+        self.world_viewport_transform =
+            parent_reference_frame_transform
+                .pre_translated(parent_accumulated_scroll_offset.x,
+                                parent_accumulated_scroll_offset.y,
+                                0.0)
+                .pre_mul(&local_transform.with_destination::<LayerPixel>());
+
+        // The transformation for any content inside of us is the viewport transformation, plus
+        // whatever scrolling offset we supply as well.
+        self.world_content_transform =
+            self.world_viewport_transform.pre_translated(self.scrolling.offset.x,
+                                                         self.scrolling.offset.y,
+                                                         0.0);
     }
 
     pub fn scrollable_height(&self) -> f32 {
         self.content_size.height - self.local_viewport_rect.size.height
     }
 
     pub fn scrollable_width(&self) -> f32 {
         self.content_size.width - self.local_viewport_rect.size.width
@@ -260,17 +386,17 @@ impl ClipScrollNode {
 
         if self.scrollable_width() <= 0. && self.scrollable_height() <= 0. {
             return false;
         }
         ray_intersects_rect(p0, p1, self.local_viewport_rect.to_untyped())
     }
 }
 
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Debug)]
 pub struct ScrollingState {
     pub offset: LayerPoint,
     pub spring: Spring,
     pub started_bouncing_back: bool,
     pub bouncing_back: bool,
     pub should_handoff_scroll: bool
 }
 
--- a/gfx/webrender/src/clip_scroll_tree.rs
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -1,20 +1,20 @@
 /* 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/. */
 
+use clip_scroll_node::{ClipScrollNode, NodeType, ScrollingState};
 use fnv::FnvHasher;
-use clip_scroll_node::{ClipScrollNode, ScrollingState};
 use std::collections::{HashMap, HashSet};
 use std::hash::BuildHasherDefault;
-use webrender_traits::{LayerPoint, LayerRect, LayerSize, LayerToScrollTransform, PipelineId};
-use webrender_traits::{ScrollEventPhase, ScrollLayerId, ScrollLayerInfo, ScrollLayerPixel};
-use webrender_traits::{ScrollLayerRect, ScrollLayerState, ScrollLocation, ScrollToWorldTransform};
-use webrender_traits::{ServoScrollRootId, WorldPoint, as_scroll_parent_rect};
+use webrender_traits::{LayerPoint, LayerRect, LayerToScrollTransform, LayerToWorldTransform};
+use webrender_traits::{PipelineId, ScrollEventPhase, ScrollLayerId, ScrollLayerInfo};
+use webrender_traits::{ScrollLayerRect, ScrollLayerState, ScrollLocation, ServoScrollRootId};
+use webrender_traits::{WorldPoint, as_scroll_parent_rect};
 
 pub type ScrollStates = HashMap<ScrollLayerId, ScrollingState, BuildHasherDefault<FnvHasher>>;
 
 pub struct ClipScrollTree {
     pub nodes: HashMap<ScrollLayerId, ClipScrollNode, BuildHasherDefault<FnvHasher>>,
     pub pending_scroll_offsets: HashMap<(PipelineId, ServoScrollRootId), LayerPoint>,
 
     /// The ScrollLayerId of the currently scrolling node. Used to allow the same
@@ -43,17 +43,17 @@ impl ClipScrollTree {
     pub fn new() -> ClipScrollTree {
         let dummy_pipeline = PipelineId(0, 0);
         ClipScrollTree {
             nodes: HashMap::with_hasher(Default::default()),
             pending_scroll_offsets: HashMap::new(),
             current_scroll_layer_id: None,
             root_reference_frame_id: ScrollLayerId::root_reference_frame(dummy_pipeline),
             topmost_scroll_layer_id: ScrollLayerId::root_scroll_layer(dummy_pipeline),
-            current_reference_frame_id: 1,
+            current_reference_frame_id: 0,
             pipelines_to_discard: HashSet::new(),
         }
     }
 
     pub fn root_reference_frame_id(&self) -> ScrollLayerId {
         // TODO(mrobinson): We should eventually make this impossible to misuse.
         debug_assert!(!self.nodes.is_empty());
         debug_assert!(self.nodes.contains_key(&self.root_reference_frame_id));
@@ -62,36 +62,16 @@ impl ClipScrollTree {
 
     pub fn topmost_scroll_layer_id(&self) -> ScrollLayerId {
         // TODO(mrobinson): We should eventually make this impossible to misuse.
         debug_assert!(!self.nodes.is_empty());
         debug_assert!(self.nodes.contains_key(&self.topmost_scroll_layer_id));
         self.topmost_scroll_layer_id
     }
 
-    pub fn establish_root(&mut self,
-                          pipeline_id: PipelineId,
-                          viewport_size: &LayerSize,
-                          content_size: &LayerSize) {
-        debug_assert!(self.nodes.is_empty());
-
-        let identity = LayerToScrollTransform::identity();
-        let viewport = LayerRect::new(LayerPoint::zero(), *viewport_size);
-
-        let root_reference_frame_id = ScrollLayerId::root_reference_frame(pipeline_id);
-        self.root_reference_frame_id = root_reference_frame_id;
-        let reference_frame = ClipScrollNode::new(&viewport, viewport.size, &identity, pipeline_id);
-        self.nodes.insert(self.root_reference_frame_id, reference_frame);
-
-        let scroll_node = ClipScrollNode::new(&viewport, *content_size, &identity, pipeline_id);
-        let topmost_scroll_layer_id = ScrollLayerId::root_scroll_layer(pipeline_id);
-        self.topmost_scroll_layer_id = topmost_scroll_layer_id;
-        self.add_node(scroll_node, topmost_scroll_layer_id, root_reference_frame_id);
-    }
-
     pub fn collect_nodes_bouncing_back(&self)
                                        -> HashSet<ScrollLayerId, BuildHasherDefault<FnvHasher>> {
         let mut nodes_bouncing_back = HashSet::with_hasher(Default::default());
         for (scroll_layer_id, node) in self.nodes.iter() {
             if node.scrolling.bouncing_back {
                 nodes_bouncing_back.insert(*scroll_layer_id);
             }
         }
@@ -124,26 +104,26 @@ impl ClipScrollTree {
 
     pub fn find_scrolling_node_at_point(&self, cursor: &WorldPoint) -> ScrollLayerId {
         self.find_scrolling_node_at_point_in_node(cursor, self.root_reference_frame_id())
             .unwrap_or(self.topmost_scroll_layer_id())
     }
 
     pub fn get_scroll_node_state(&self) -> Vec<ScrollLayerState> {
         let mut result = vec![];
-        for (scroll_layer_id, scroll_node) in self.nodes.iter() {
-            match scroll_layer_id.info {
-                ScrollLayerInfo::Scrollable(_, servo_scroll_root_id) => {
+        for (_, node) in self.nodes.iter() {
+            match node.node_type {
+                NodeType::Clip(ref info) if info.scroll_root_id.is_some() => {
                     result.push(ScrollLayerState {
-                        pipeline_id: scroll_node.pipeline_id,
-                        scroll_root_id: servo_scroll_root_id,
-                        scroll_offset: scroll_node.scrolling.offset,
+                        pipeline_id: node.pipeline_id,
+                        scroll_root_id: info.scroll_root_id.unwrap(),
+                        scroll_offset: node.scrolling.offset,
                     })
                 }
-                ScrollLayerInfo::ReferenceFrame(..) => {}
+                _ => {},
             }
         }
         result
     }
 
     pub fn drain(&mut self) -> ScrollStates {
         self.current_reference_frame_id = 1;
 
@@ -172,20 +152,20 @@ impl ClipScrollTree {
 
         let mut scrolled_a_node = false;
         let mut found_node = false;
         for (layer_id, node) in self.nodes.iter_mut() {
             if layer_id.pipeline_id != pipeline_id {
                 continue;
             }
 
-            match layer_id.info {
-                ScrollLayerInfo::Scrollable(_, id) if id != scroll_root_id => continue,
-                ScrollLayerInfo::ReferenceFrame(..) => continue,
-                ScrollLayerInfo::Scrollable(..) => {}
+            match node.node_type {
+                NodeType::Clip(ref info) if info.scroll_root_id != Some(scroll_root_id) => continue,
+                NodeType::ReferenceFrame(..) => continue,
+                NodeType::Clip(_) => {},
             }
 
             found_node = true;
             scrolled_a_node |= node.set_scroll_origin(&origin);
         }
 
         if !found_node {
             self.pending_scroll_offsets.insert((pipeline_id, scroll_root_id), origin);
@@ -253,81 +233,109 @@ impl ClipScrollTree {
             ScrollEventPhase::End => {
                 // clean-up when gesture ends.
                 let mut current_node = self.nodes.get_mut(&scroll_layer_id).unwrap();
                 current_node.scrolling.should_handoff_scroll = false;
                 false
             },
         };
 
-        let scroll_node_info = if switch_node {
-            topmost_scroll_layer_id.info
+        let scroll_layer_id = if switch_node {
+            topmost_scroll_layer_id
         } else {
-            scroll_layer_id.info
+            scroll_layer_id
         };
 
-        let scroll_root_id = match scroll_node_info {
-             ScrollLayerInfo::Scrollable(_, scroll_root_id) => scroll_root_id,
-             _ => unreachable!("Tried to scroll a reference frame."),
+        // TODO(mrobinson): Once we remove the concept of shared scroll root ids we can remove
+        // this entirely and just scroll the node based on the ScrollLayerId.
+        let scroll_root_id = {
+            let node = self.nodes.get_mut(&scroll_layer_id).unwrap();
+            let scroll_root_id = match node.node_type {
+                NodeType::Clip(ref info) => info.scroll_root_id,
+                NodeType::ReferenceFrame(..) => unreachable!("Tried to scroll a reference frame."),
+            };
 
+            if scroll_root_id.is_none() {
+                return node.scroll(scroll_location, phase);
+            }
+
+            scroll_root_id
         };
 
         let mut scrolled_a_node = false;
         for (layer_id, node) in self.nodes.iter_mut() {
             if layer_id.pipeline_id != scroll_layer_id.pipeline_id {
                 continue;
             }
 
-            match layer_id.info {
-                ScrollLayerInfo::Scrollable(_, id) if id != scroll_root_id => continue,
-                ScrollLayerInfo::ReferenceFrame(..) => continue,
-                _ => {}
+            match node.node_type {
+                NodeType::Clip(ref info) if info.scroll_root_id == scroll_root_id => { }
+                _ => continue,
             }
 
             let scrolled_this_node = node.scroll(scroll_location, phase);
             scrolled_a_node = scrolled_a_node || scrolled_this_node;
         }
         scrolled_a_node
     }
 
-    pub fn update_all_node_transforms(&mut self) {
+    pub fn update_all_node_transforms(&mut self, pan: LayerPoint) {
         if self.nodes.is_empty() {
             return;
         }
 
         let root_reference_frame_id = self.root_reference_frame_id();
-        let root_viewport = self.nodes[&root_reference_frame_id].local_viewport_rect;
+        let root_viewport = self.nodes[&root_reference_frame_id].local_clip_rect;
         self.update_node_transform(root_reference_frame_id,
-                                    &ScrollToWorldTransform::identity(),
-                                    &as_scroll_parent_rect(&root_viewport));
+                                   &LayerToWorldTransform::create_translation(pan.x, pan.y, 0.0),
+                                   &as_scroll_parent_rect(&root_viewport),
+                                   LayerPoint::zero());
     }
 
     fn update_node_transform(&mut self,
                              layer_id: ScrollLayerId,
-                             parent_world_transform: &ScrollToWorldTransform,
-                             parent_viewport_rect: &ScrollLayerRect) {
+                             parent_reference_frame_transform: &LayerToWorldTransform,
+                             parent_viewport_rect: &ScrollLayerRect,
+                             parent_accumulated_scroll_offset: LayerPoint) {
         // TODO(gw): This is an ugly borrow check workaround to clone these.
         //           Restructure this to avoid the clones!
-        let (node_transform_for_children, viewport_rect, node_children) = {
+        let (reference_frame_transform, viewport_rect, accumulated_scroll_offset, node_children) = {
             match self.nodes.get_mut(&layer_id) {
                 Some(node) => {
-                    node.update_transform(parent_world_transform, parent_viewport_rect);
+                    node.update_transform(parent_reference_frame_transform,
+                                          parent_viewport_rect,
+                                          parent_accumulated_scroll_offset);
 
-                    (node.world_content_transform.with_source::<ScrollLayerPixel>(),
+                    // The transformation we are passing is the transformation of the parent
+                    // reference frame and the offset is the accumulated offset of all the nodes
+                    // between us and the parent reference frame. If we are a reference frame,
+                    // we need to reset both these values.
+                    let (transform, offset) = match node.node_type {
+                        NodeType::ReferenceFrame(..) =>
+                            (node.world_viewport_transform, LayerPoint::zero()),
+                        NodeType::Clip(_) => {
+                            (*parent_reference_frame_transform,
+                             parent_accumulated_scroll_offset + node.scrolling.offset)
+                        }
+                    };
+
+                    (transform,
                      as_scroll_parent_rect(&node.combined_local_viewport_rect),
+                     offset,
                      node.children.clone())
                 }
                 None => return,
             }
         };
 
         for child_layer_id in node_children {
             self.update_node_transform(child_layer_id,
-                                       &node_transform_for_children,
-                                       &viewport_rect);
+                                       &reference_frame_transform,
+                                       &viewport_rect,
+                                       accumulated_scroll_offset);
         }
     }
 
     pub fn tick_scrolling_bounce_animations(&mut self) {
         for (_, node) in &mut self.nodes {
             node.tick_scrolling_bounce_animation()
         }
     }
@@ -338,53 +346,62 @@ impl ClipScrollTree {
         for (scroll_layer_id, node) in &mut self.nodes {
             let scrolling_state = match old_states.get(&scroll_layer_id) {
                 Some(old_scrolling_state) => *old_scrolling_state,
                 None => ScrollingState::new(),
             };
 
             node.finalize(&scrolling_state);
 
-            let scroll_root_id = match scroll_layer_id.info {
-                ScrollLayerInfo::Scrollable(_, scroll_root_id) => scroll_root_id,
+            let scroll_root_id = match node.node_type {
+                NodeType::Clip(ref info) if info.scroll_root_id.is_some() =>
+                    info.scroll_root_id.unwrap(),
                 _ => continue,
             };
 
 
             let pipeline_id = scroll_layer_id.pipeline_id;
             if let Some(pending_offset) =
                 self.pending_scroll_offsets.remove(&(pipeline_id, scroll_root_id)) {
                 node.set_scroll_origin(&pending_offset);
             }
         }
 
     }
 
     pub fn add_reference_frame(&mut self,
-                               rect: LayerRect,
-                               transform: LayerToScrollTransform,
+                               rect: &LayerRect,
+                               transform: &LayerToScrollTransform,
                                pipeline_id: PipelineId,
-                               parent_id: ScrollLayerId) -> ScrollLayerId {
+                               parent_id: Option<ScrollLayerId>)
+                               -> ScrollLayerId {
         let reference_frame_id = ScrollLayerId {
             pipeline_id: pipeline_id,
             info: ScrollLayerInfo::ReferenceFrame(self.current_reference_frame_id),
         };
         self.current_reference_frame_id += 1;
 
-        let node = ClipScrollNode::new(&rect, rect.size, &transform, pipeline_id);
-        self.add_node(node, reference_frame_id, parent_id);
+        let node = ClipScrollNode::new_reference_frame(parent_id,
+                                                       &rect,
+                                                       rect.size,
+                                                       &transform,
+                                                       pipeline_id);
+        self.add_node(node, reference_frame_id);
         reference_frame_id
     }
 
-    pub fn add_node(&mut self, node: ClipScrollNode, id: ScrollLayerId, parent_id: ScrollLayerId) {
+    pub fn add_node(&mut self, node: ClipScrollNode, id: ScrollLayerId) {
+        // When the parent node is None this means we are adding the root.
+        match node.parent {
+            Some(parent_id) => self.nodes.get_mut(&parent_id).unwrap().add_child(id),
+            None => self.root_reference_frame_id = id,
+        }
+
         debug_assert!(!self.nodes.contains_key(&id));
         self.nodes.insert(id, node);
-
-        debug_assert!(parent_id != id);
-        self.nodes.get_mut(&parent_id).unwrap().add_child(id);
     }
 
     pub fn discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId) {
         self.pipelines_to_discard.insert(pipeline_id);
 
         match self.current_scroll_layer_id {
             Some(id) if id.pipeline_id == pipeline_id => self.current_scroll_layer_id = None,
             _ => {}
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -17,16 +17,19 @@ use std::io::Read;
 use std::iter::repeat;
 use std::mem;
 use std::path::PathBuf;
 //use std::sync::mpsc::{channel, Sender};
 //use std::thread;
 use webrender_traits::{ColorF, ImageFormat};
 use webrender_traits::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintSize};
 
+#[derive(Debug, Copy, Clone)]
+pub struct FrameId(usize);
+
 #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
 const GL_FORMAT_A: gl::GLuint = gl::RED;
 
 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
 const GL_FORMAT_A: gl::GLuint = gl::ALPHA;
 
 #[cfg(any(target_os = "windows", all(unix, not(target_os = "android"))))]
 const GL_FORMAT_BGRA: gl::GLuint = gl::BGRA;
@@ -492,42 +495,46 @@ pub struct GpuSample<T> {
     pub time_ns: u64,
 }
 
 pub struct GpuFrameProfile<T> {
     queries: Vec<gl::GLuint>,
     samples: Vec<GpuSample<T>>,
     next_query: usize,
     pending_query: gl::GLuint,
+    frame_id: FrameId,
 }
 
 impl<T> GpuFrameProfile<T> {
     #[cfg(not(target_os = "android"))]
     fn new() -> GpuFrameProfile<T> {
         let queries = gl::gen_queries(MAX_EVENTS_PER_FRAME as gl::GLint);
 
         GpuFrameProfile {
             queries: queries,
             samples: Vec::new(),
             next_query: 0,
             pending_query: 0,
+            frame_id: FrameId(0),
         }
     }
 
     #[cfg(target_os = "android")]
     fn new() -> GpuFrameProfile<T> {
         GpuFrameProfile {
             queries: Vec::new(),
             samples: Vec::new(),
             next_query: 0,
             pending_query: 0,
+            frame_id: FrameId(0),
         }
     }
 
-    fn begin_frame(&mut self) {
+    fn begin_frame(&mut self, frame_id: FrameId) {
+        self.frame_id = frame_id;
         self.next_query = 0;
         self.pending_query = 0;
         self.samples.clear();
     }
 
     #[cfg(not(target_os = "android"))]
     fn end_frame(&mut self) {
         if self.pending_query != 0 {
@@ -567,17 +574,17 @@ impl<T> GpuFrameProfile<T> {
     fn add_marker(&mut self, tag: T) {
         self.samples.push(GpuSample {
             tag: tag,
             time_ns: 0,
         });
     }
 
     fn is_valid(&self) -> bool {
-        self.next_query <= MAX_EVENTS_PER_FRAME
+        self.next_query > 0 && self.next_query <= MAX_EVENTS_PER_FRAME
     }
 
     #[cfg(not(target_os = "android"))]
     fn build_samples(&mut self) -> Vec<GpuSample<T>> {
         for (index, sample) in self.samples.iter_mut().enumerate() {
             sample.time_ns = gl::get_query_object_ui64v(self.queries[index], gl::QUERY_RESULT)
         }
 
@@ -614,28 +621,28 @@ impl<T> GpuProfiler<T> {
                       GpuFrameProfile::new(),
                       GpuFrameProfile::new(),
                       GpuFrameProfile::new(),
                       GpuFrameProfile::new(),
                     ],
         }
     }
 
-    pub fn build_samples(&mut self) -> Option<Vec<GpuSample<T>>> {
+    pub fn build_samples(&mut self) -> Option<(FrameId, Vec<GpuSample<T>>)> {
         let frame = &mut self.frames[self.next_frame];
         if frame.is_valid() {
-            Some(frame.build_samples())
+            Some((frame.frame_id, frame.build_samples()))
         } else {
             None
         }
     }
 
-    pub fn begin_frame(&mut self) {
+    pub fn begin_frame(&mut self, frame_id: FrameId) {
         let frame = &mut self.frames[self.next_frame];
-        frame.begin_frame();
+        frame.begin_frame(frame_id);
     }
 
     pub fn end_frame(&mut self) {
         let frame = &mut self.frames[self.next_frame];
         frame.end_frame();
         self.next_frame = (self.next_frame + 1) % MAX_PROFILE_FRAMES;
     }
 
@@ -816,16 +823,20 @@ pub struct Device {
     shader_preamble: String,
     //file_watcher: FileWatcherThread,
 
     // Used on android only
     #[allow(dead_code)]
     next_vao_id: gl::GLuint,
 
     max_texture_size: u32,
+
+    // Frame counter. This is used to map between CPU
+    // frames and GPU frames.
+    frame_id: FrameId,
 }
 
 impl Device {
     pub fn new(resource_override_path: Option<PathBuf>,
                _file_changed_handler: Box<FileWatcherHandler>) -> Device {
         //let file_watcher = FileWatcherThread::new(file_changed_handler);
 
         let shader_preamble = get_shader_source(SHADER_PREAMBLE, &resource_override_path);
@@ -855,17 +866,18 @@ impl Device {
             programs: HashMap::with_hasher(Default::default()),
             vaos: HashMap::with_hasher(Default::default()),
 
             shader_preamble: shader_preamble,
 
             next_vao_id: 1,
             //file_watcher: file_watcher,
 
-            max_texture_size: gl::get_integer_v(gl::MAX_TEXTURE_SIZE) as u32
+            max_texture_size: gl::get_integer_v(gl::MAX_TEXTURE_SIZE) as u32,
+            frame_id: FrameId(0),
         }
     }
 
     pub fn max_texture_size(&self) -> u32 {
         self.max_texture_size
     }
 
     pub fn get_capabilities(&self) -> &Capabilities {
@@ -898,17 +910,17 @@ impl Device {
         } else {
             if !log.is_empty() {
                 println!("Warnings detected on shader: {:?}\n{}", name, log);
             }
             Ok(id)
         }
     }
 
-    pub fn begin_frame(&mut self, device_pixel_ratio: f32) {
+    pub fn begin_frame(&mut self, device_pixel_ratio: f32) -> FrameId {
         debug_assert!(!self.inside_frame);
         self.inside_frame = true;
         self.device_pixel_ratio = device_pixel_ratio;
 
         // Retrive the currently set FBO.
         let default_read_fbo = gl::get_integer_v(gl::READ_FRAMEBUFFER_BINDING);
         self.default_read_fbo = default_read_fbo as gl::GLuint;
         let default_draw_fbo = gl::get_integer_v(gl::DRAW_FRAMEBUFFER_BINDING);
@@ -933,16 +945,18 @@ impl Device {
         self.bound_read_fbo = FBOId(self.default_read_fbo);
         self.bound_draw_fbo = FBOId(self.default_draw_fbo);
 
         // Pixel op state
         gl::pixel_store_i(gl::UNPACK_ALIGNMENT, 1);
 
         // Default is sampler 0, always
         gl::active_texture(gl::TEXTURE0);
+
+        self.frame_id
     }
 
     pub fn bind_texture(&mut self,
                         sampler: TextureSampler,
                         texture_id: TextureId) {
         debug_assert!(self.inside_frame);
 
         let sampler_index = sampler as usize;
@@ -1821,16 +1835,18 @@ impl Device {
         gl::use_program(0);
 
         for i in 0..self.bound_textures.len() {
             gl::active_texture(gl::TEXTURE0 + i as gl::GLuint);
             gl::bind_texture(gl::TEXTURE_2D, 0);
         }
 
         gl::active_texture(gl::TEXTURE0);
+
+        self.frame_id.0 += 1;
     }
 
     pub fn assign_ubo_binding(&self, program_id: ProgramId, name: &str, value: u32) -> u32 {
         let index = gl::get_uniform_block_index(program_id.0, name);
         gl::uniform_block_binding(program_id.0, index, value);
         index
     }
 
@@ -1881,16 +1897,40 @@ impl Device {
             clear_bits |= gl::DEPTH_BUFFER_BIT;
         }
 
         if clear_bits != 0 {
             gl::clear(clear_bits);
         }
     }
 
+    pub fn clear_target_rect(&self,
+                             color: Option<[f32; 4]>,
+                             depth: Option<f32>,
+                             rect: DeviceIntRect) {
+        let mut clear_bits = 0;
+
+        if let Some(color) = color {
+            gl::clear_color(color[0], color[1], color[2], color[3]);
+            clear_bits |= gl::COLOR_BUFFER_BIT;
+        }
+
+        if let Some(depth) = depth {
+            gl::clear_depth(depth as f64);
+            clear_bits |= gl::DEPTH_BUFFER_BIT;
+        }
+
+        if clear_bits != 0 {
+            gl::enable(gl::SCISSOR_TEST);
+            gl::scissor(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
+            gl::clear(clear_bits);
+            gl::disable(gl::SCISSOR_TEST);
+        }
+    }
+
     pub fn enable_depth(&self) {
         gl::enable(gl::DEPTH_TEST);
     }
 
     pub fn disable_depth(&self) {
         gl::disable(gl::DEPTH_TEST);
     }
 
@@ -1918,17 +1958,17 @@ impl Device {
         if enable {
             gl::enable(gl::BLEND);
         } else {
             gl::disable(gl::BLEND);
         }
     }
 
     pub fn set_blend_mode_premultiplied_alpha(&self) {
-        gl::blend_func(gl::SRC_ALPHA, gl::ZERO);
+        gl::blend_func(gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
         gl::blend_equation(gl::FUNC_ADD);
     }
 
     pub fn set_blend_mode_alpha(&self) {
         //gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
         gl::blend_func_separate(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA,
                                 gl::ONE, gl::ONE);
         gl::blend_equation(gl::FUNC_ADD);
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -1,41 +1,64 @@
 /* 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/. */
 
 use app_units::Au;
+use euclid::rect::rect;
 use fnv::FnvHasher;
 use internal_types::{ANGLE_FLOAT_TO_FIXED, AxisDirection};
 use internal_types::{LowLevelFilterOp};
 use internal_types::{RendererFrame};
 use frame_builder::{FrameBuilder, FrameBuilderConfig};
-use clip_scroll_node::ClipScrollNode;
 use clip_scroll_tree::{ClipScrollTree, ScrollStates};
 use profiler::TextureCacheProfileCounters;
 use resource_cache::ResourceCache;
 use scene::{Scene, SceneProperties};
 use std::collections::HashMap;
 use std::hash::BuildHasherDefault;
 use tiling::{AuxiliaryListsMap, CompositeOps, PrimitiveFlags};
-use webrender_traits::{AuxiliaryLists, ClipRegion, ColorF, DisplayItem, Epoch, FilterOp};
-use webrender_traits::{LayerPoint, LayerRect, LayerSize, LayerToScrollTransform, LayoutTransform, TileOffset};
-use webrender_traits::{MixBlendMode, PipelineId, ScrollEventPhase, ScrollLayerId, ScrollLayerState};
-use webrender_traits::{ScrollLocation, ScrollPolicy, ServoScrollRootId, SpecificDisplayItem};
-use webrender_traits::{StackingContext, WorldPoint, ImageDisplayItem, DeviceUintSize};
+use webrender_traits::{AuxiliaryLists, ClipDisplayItem, ClipRegion, ColorF, DeviceUintRect};
+use webrender_traits::{DeviceUintSize, DisplayItem, Epoch, FilterOp, ImageDisplayItem, LayerPoint};
+use webrender_traits::{LayerRect, LayerSize, LayerToScrollTransform, LayoutTransform};
+use webrender_traits::{MixBlendMode, PipelineId, ScrollEventPhase, ScrollLayerId};
+use webrender_traits::{ScrollLayerState, ScrollLocation, ScrollPolicy, ServoScrollRootId};
+use webrender_traits::{SpecificDisplayItem, StackingContext, TileOffset, WorldPoint};
 
 #[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
 pub struct FrameId(pub u32);
 
 static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF { r: 0.3, g: 0.3, b: 0.3, a: 0.6 };
 
 struct FlattenContext<'a> {
     scene: &'a Scene,
     builder: &'a mut FrameBuilder,
     resource_cache: &'a mut ResourceCache,
+    replacements: Vec<(ScrollLayerId, ScrollLayerId)>,
+}
+
+impl<'a> FlattenContext<'a> {
+    fn new(scene: &'a Scene,
+           builder: &'a mut FrameBuilder,
+           resource_cache: &'a mut ResourceCache)
+           -> FlattenContext<'a> {
+        FlattenContext {
+            scene: scene,
+            builder: builder,
+            resource_cache: resource_cache,
+            replacements: Vec::new(),
+        }
+    }
+
+    fn scroll_layer_id_with_replacement(&self, id: ScrollLayerId) -> ScrollLayerId {
+        match self.replacements.last() {
+            Some(&(to_replace, replacement)) if to_replace == id => replacement,
+            _ => id,
+        }
+    }
 }
 
 // TODO: doc
 pub struct Frame {
     pub clip_scroll_tree: ClipScrollTree,
     pub pipeline_epoch_map: HashMap<PipelineId, Epoch, BuildHasherDefault<FnvHasher>>,
     pub pipeline_auxiliary_lists: AuxiliaryListsMap,
     id: FrameId,
@@ -136,20 +159,25 @@ impl<'a> DisplayListTraversal<'a> {
     pub fn new_skipping_first(display_list: &'a Vec<DisplayItem>) -> DisplayListTraversal {
         DisplayListTraversal {
             display_list: display_list,
             next_item_index: 1,
         }
     }
 
     pub fn skip_current_stacking_context(&mut self) {
+        let mut depth = 0;
         for item in self {
-            if item.item == SpecificDisplayItem::PopStackingContext {
-                return;
+            match item.item {
+                SpecificDisplayItem::PushStackingContext(..) => depth += 1,
+                SpecificDisplayItem::PopStackingContext if depth == 0 => return,
+                SpecificDisplayItem::PopStackingContext => depth -= 1,
+                _ => {}
             }
+            debug_assert!(depth >= 0);
         }
     }
 
     pub fn current_stacking_context_empty(&self) -> bool {
         match self.peek() {
             Some(item) => item.item == SpecificDisplayItem::PopStackingContext,
             None => true,
         }
@@ -223,149 +251,121 @@ impl Frame {
     pub fn tick_scrolling_bounce_animations(&mut self) {
         self.clip_scroll_tree.tick_scrolling_bounce_animations();
     }
 
     pub fn discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId) {
         self.clip_scroll_tree.discard_frame_state_for_pipeline(pipeline_id);
     }
 
-    pub fn create(&mut self, scene: &Scene, resource_cache: &mut ResourceCache) {
+    pub fn create(&mut self,
+                  scene: &Scene,
+                  resource_cache: &mut ResourceCache,
+                  window_size: DeviceUintSize,
+                  inner_rect: DeviceUintRect,
+                  device_pixel_ratio: f32) {
         let root_pipeline_id = match scene.root_pipeline_id {
             Some(root_pipeline_id) => root_pipeline_id,
             None => return,
         };
 
         let root_pipeline = match scene.pipeline_map.get(&root_pipeline_id) {
             Some(root_pipeline) => root_pipeline,
             None => return,
         };
 
         let display_list = scene.display_lists.get(&root_pipeline_id);
         let display_list = match display_list {
             Some(display_list) => display_list,
             None => return,
         };
 
+        if window_size.width == 0 || window_size.height == 0 {
+            println!("ERROR: Invalid window dimensions! Please call api.set_window_size()");
+            return;
+        }
+
         let old_scrolling_states = self.reset();
         self.pipeline_auxiliary_lists = scene.pipeline_auxiliary_lists.clone();
 
         self.pipeline_epoch_map.insert(root_pipeline_id, root_pipeline.epoch);
 
         let (root_stacking_context, root_clip) = match display_list.starting_stacking_context() {
             Some(some) => some,
             None => {
                 warn!("Pipeline display list does not start with a stacking context.");
                 return;
             }
         };
 
-        self.clip_scroll_tree.establish_root(root_pipeline_id,
-                                             &root_pipeline.viewport_size,
-                                             &root_clip.main.size);
-
         let background_color = root_pipeline.background_color.and_then(|color| {
             if color.a > 0.0 {
                 Some(color)
             } else {
                 None
             }
         });
 
-        let mut frame_builder = FrameBuilder::new(root_pipeline.viewport_size,
+        let mut frame_builder = FrameBuilder::new(window_size,
                                                   background_color,
                                                   self.frame_builder_config);
 
         {
-            let mut context = FlattenContext {
-                scene: scene,
-                builder: &mut frame_builder,
-                resource_cache: resource_cache
-            };
+            let mut context = FlattenContext::new(scene, &mut frame_builder, resource_cache);
+
+            let scroll_layer_id = context.builder.push_root(root_pipeline_id,
+                                                            &root_pipeline.viewport_size,
+                                                            &root_clip.main.size,
+                                                            &mut self.clip_scroll_tree);
+
+            context.builder.setup_viewport_offset(window_size,
+                                                  inner_rect,
+                                                  device_pixel_ratio,
+                                                  &mut self.clip_scroll_tree);
 
             let mut traversal = DisplayListTraversal::new_skipping_first(display_list);
-            let reference_frame_id = self.clip_scroll_tree.root_reference_frame_id();
-            let topmost_scroll_layer_id = self.clip_scroll_tree.topmost_scroll_layer_id();
-            debug_assert!(reference_frame_id != topmost_scroll_layer_id);
-
-            let viewport_rect = LayerRect::new(LayerPoint::zero(), root_pipeline.viewport_size);
-            let clip = ClipRegion::simple(&viewport_rect);
-            context.builder.push_clip_scroll_node(reference_frame_id,
-                                                  &clip,
-                                                  &LayerPoint::zero(),
-                                                  &root_pipeline.viewport_size);
-            context.builder.push_clip_scroll_node(topmost_scroll_layer_id,
-                                                  &clip,
-                                                  &LayerPoint::zero(),
-                                                  &root_clip.main.size);
-
             self.flatten_stacking_context(&mut traversal,
                                           root_pipeline_id,
                                           &mut context,
-                                          reference_frame_id,
-                                          topmost_scroll_layer_id,
-                                          LayerToScrollTransform::identity(),
+                                          scroll_layer_id,
+                                          LayerPoint::zero(),
                                           0,
                                           &root_stacking_context,
                                           root_clip);
-
-            context.builder.pop_clip_scroll_node();
-            context.builder.pop_clip_scroll_node();
         }
 
         self.frame_builder = Some(frame_builder);
         self.clip_scroll_tree.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
     }
 
-    fn flatten_scroll_layer<'a>(&mut self,
-                                traversal: &mut DisplayListTraversal<'a>,
-                                pipeline_id: PipelineId,
-                                context: &mut FlattenContext,
-                                current_reference_frame_id: ScrollLayerId,
-                                parent_scroll_layer_id: ScrollLayerId,
-                                layer_relative_transform: LayerToScrollTransform,
-                                level: i32,
-                                clip: &ClipRegion,
-                                content_size: &LayerSize,
-                                new_scroll_layer_id: ScrollLayerId) {
-        // Avoid doing unnecessary work for empty stacking contexts.
-        if traversal.current_stacking_context_empty() {
-            traversal.skip_current_stacking_context();
-            return;
-        }
+    fn flatten_clip<'a>(&mut self,
+                        context: &mut FlattenContext,
+                        pipeline_id: PipelineId,
+                        parent_id: ScrollLayerId,
+                        item: &ClipDisplayItem,
+                        reference_frame_relative_offset: LayerPoint,
+                        clip: &ClipRegion) {
+        let clip_rect = clip.main.translate(&reference_frame_relative_offset);
+        context.builder.add_clip_scroll_node(item.id,
+                                             parent_id,
+                                             pipeline_id,
+                                             &clip_rect,
+                                             &item.content_size,
+                                             item.scroll_root_id,
+                                             clip,
+                                             &mut self.clip_scroll_tree);
 
-        let clip_rect = clip.main;
-        let node = ClipScrollNode::new(&clip_rect,
-                                       *content_size,
-                                       &layer_relative_transform,
-                                       pipeline_id);
-        self.clip_scroll_tree.add_node(node, new_scroll_layer_id, parent_scroll_layer_id);
-        context.builder.push_clip_scroll_node(new_scroll_layer_id,
-                                              clip,
-                                              &clip_rect.origin,
-                                              &content_size);
-
-        self.flatten_items(traversal,
-                           pipeline_id,
-                           context,
-                           current_reference_frame_id,
-                           new_scroll_layer_id,
-                           LayerToScrollTransform::identity(),
-                           level);
-
-        context.builder.pop_clip_scroll_node();
     }
 
     fn flatten_stacking_context<'a>(&mut self,
                                     traversal: &mut DisplayListTraversal<'a>,
                                     pipeline_id: PipelineId,
                                     context: &mut FlattenContext,
-                                    current_reference_frame_id: ScrollLayerId,
-                                    current_scroll_layer_id: ScrollLayerId,
-                                    layer_relative_transform: LayerToScrollTransform,
+                                    context_scroll_layer_id: ScrollLayerId,
+                                    mut reference_frame_relative_offset: LayerPoint,
                                     level: i32,
                                     stacking_context: &StackingContext,
                                     clip_region: &ClipRegion) {
         // Avoid doing unnecessary work for empty stacking contexts.
         if traversal.current_stacking_context_empty() {
             traversal.skip_current_stacking_context();
             return;
         }
@@ -379,102 +379,112 @@ impl Frame {
                 stacking_context.mix_blend_mode_for_compositing())
         };
 
         if composition_operations.will_make_invisible() {
             traversal.skip_current_stacking_context();
             return;
         }
 
-        let stacking_context_transform = context.scene
-                                                .properties
-                                                .resolve_layout_transform(&stacking_context.transform);
+        let mut scroll_layer_id =
+            context.scroll_layer_id_with_replacement(context_scroll_layer_id);
 
-        let mut transform =
-            layer_relative_transform.pre_translated(stacking_context.bounds.origin.x,
-                                                    stacking_context.bounds.origin.y,
-                                                    0.0)
-                                     .pre_mul(&stacking_context_transform)
-                                     .pre_mul(&stacking_context.perspective);
-
-        let mut reference_frame_id = current_reference_frame_id;
-        let mut scroll_layer_id = match stacking_context.scroll_policy {
-            ScrollPolicy::Fixed => current_reference_frame_id,
-            ScrollPolicy::Scrollable => current_scroll_layer_id,
-        };
+        if stacking_context.scroll_policy == ScrollPolicy::Fixed {
+            context.replacements.push((context_scroll_layer_id,
+                                       context.builder.current_reference_frame_id()));
+        }
 
         // If we have a transformation, we establish a new reference frame. This means
         // that fixed position stacking contexts are positioned relative to us.
-        if stacking_context_transform != LayoutTransform::identity() ||
-           stacking_context.perspective != LayoutTransform::identity() {
-            scroll_layer_id = self.clip_scroll_tree.add_reference_frame(clip_region.main,
-                                                                        transform,
-                                                                        pipeline_id,
-                                                                        scroll_layer_id);
-            reference_frame_id = scroll_layer_id;
-            transform = LayerToScrollTransform::identity();
+        let is_reference_frame = stacking_context.transform.is_some() ||
+                                 stacking_context.perspective.is_some();
+        if is_reference_frame {
+            let transform = stacking_context.transform.as_ref();
+            let transform = context.scene.properties.resolve_layout_transform(transform);
+            let perspective =
+                stacking_context.perspective.unwrap_or_else(LayoutTransform::identity);
+            let transform =
+                LayerToScrollTransform::create_translation(reference_frame_relative_offset.x,
+                                                           reference_frame_relative_offset.y,
+                                                           0.0)
+                                        .pre_translated(stacking_context.bounds.origin.x,
+                                                        stacking_context.bounds.origin.y,
+                                                        0.0)
+                                        .pre_mul(&transform)
+                                        .pre_mul(&perspective);
+
+            scroll_layer_id = context.builder.push_reference_frame(Some(scroll_layer_id),
+                                                                   pipeline_id,
+                                                                   &clip_region.main,
+                                                                   &transform,
+                                                                   &mut self.clip_scroll_tree);
+            context.replacements.push((context_scroll_layer_id, scroll_layer_id));
+            reference_frame_relative_offset = LayerPoint::zero();
+        } else {
+            reference_frame_relative_offset = LayerPoint::new(
+                reference_frame_relative_offset.x + stacking_context.bounds.origin.x,
+                reference_frame_relative_offset.y + stacking_context.bounds.origin.y);
         }
 
+        // TODO(gw): Int with overflow etc
+        context.builder.push_stacking_context(reference_frame_relative_offset,
+                                              clip_region.main,
+                                              pipeline_id,
+                                              level == 0,
+                                              composition_operations);
+
         if level == 0 {
             if let Some(pipeline) = context.scene.pipeline_map.get(&pipeline_id) {
                 if let Some(bg_color) = pipeline.background_color {
-
-                    // Adding a dummy layer for this rectangle in order to disable clipping.
-                    let no_clip = ClipRegion::simple(&clip_region.main);
-                    context.builder.push_stacking_context(clip_region.main,
-                                                          transform,
-                                                          pipeline_id,
-                                                          scroll_layer_id,
-                                                          CompositeOps::empty());
-
-                    //Note: we don't use the original clip region here,
+                    // Note: we don't use the original clip region here,
                     // it's already processed by the node we just pushed.
-                    context.builder.add_solid_rectangle(&clip_region.main,
-                                                        &no_clip,
+                    let background_rect = LayerRect::new(LayerPoint::zero(), clip_region.main.size);
+                    context.builder.add_solid_rectangle(scroll_layer_id,
+                                                        &clip_region.main,
+                                                        &ClipRegion::simple(&background_rect),
                                                         &bg_color,
                                                         PrimitiveFlags::None);
-
-                    context.builder.pop_stacking_context();
                 }
             }
         }
 
-         // TODO(gw): Int with overflow etc
-        context.builder.push_stacking_context(clip_region.main,
-                                              transform,
-                                              pipeline_id,
-                                              scroll_layer_id,
-                                              composition_operations);
-
         self.flatten_items(traversal,
                            pipeline_id,
                            context,
-                           reference_frame_id,
-                           scroll_layer_id,
-                           transform,
+                           reference_frame_relative_offset,
                            level);
 
         if level == 0 && self.frame_builder_config.enable_scrollbars {
             let scrollbar_rect = LayerRect::new(LayerPoint::zero(), LayerSize::new(10.0, 70.0));
             context.builder.add_solid_rectangle(
+                scroll_layer_id,
                 &scrollbar_rect,
                 &ClipRegion::simple(&scrollbar_rect),
                 &DEFAULT_SCROLLBAR_COLOR,
                 PrimitiveFlags::Scrollbar(self.clip_scroll_tree.topmost_scroll_layer_id(), 4.0));
         }
 
+        if stacking_context.scroll_policy == ScrollPolicy::Fixed {
+            context.replacements.pop();
+        }
+
+        if is_reference_frame {
+            context.replacements.pop();
+            context.builder.pop_reference_frame();
+        }
+
         context.builder.pop_stacking_context();
     }
 
     fn flatten_iframe<'a>(&mut self,
                           pipeline_id: PipelineId,
+                          parent_id: ScrollLayerId,
                           bounds: &LayerRect,
                           context: &mut FlattenContext,
-                          current_scroll_layer_id: ScrollLayerId,
-                          layer_relative_transform: LayerToScrollTransform) {
+                          reference_frame_relative_offset: LayerPoint) {
 
         let pipeline = match context.scene.pipeline_map.get(&pipeline_id) {
             Some(pipeline) => pipeline,
             None => return,
         };
 
         let display_list = context.scene.display_lists.get(&pipeline_id);
         let display_list = match display_list {
@@ -488,184 +498,262 @@ impl Frame {
                 warn!("Pipeline display list does not start with a stacking context.");
                 return;
             }
         };
 
         self.pipeline_epoch_map.insert(pipeline_id, pipeline.epoch);
 
         let iframe_rect = LayerRect::new(LayerPoint::zero(), bounds.size);
-        let transform = layer_relative_transform.pre_translated(bounds.origin.x,
-                                                                bounds.origin.y,
-                                                                0.0);
+        let transform = LayerToScrollTransform::create_translation(
+            reference_frame_relative_offset.x + bounds.origin.x,
+            reference_frame_relative_offset.y + bounds.origin.y,
+            0.0);
+
         let iframe_reference_frame_id =
-            self.clip_scroll_tree.add_reference_frame(iframe_rect,
-                                                      transform,
-                                                      pipeline_id,
-                                                      current_scroll_layer_id);
+            context.builder.push_reference_frame(Some(parent_id),
+                                                 pipeline_id,
+                                                 &iframe_rect,
+                                                 &transform,
+                                                 &mut self.clip_scroll_tree);
+
         let iframe_scroll_layer_id = ScrollLayerId::root_scroll_layer(pipeline_id);
-        let node = ClipScrollNode::new(&LayerRect::new(LayerPoint::zero(), iframe_rect.size),
-                                       iframe_clip.main.size,
-                                       &LayerToScrollTransform::identity(),
-                                       pipeline_id);
-        self.clip_scroll_tree.add_node(node.clone(),
-                                       iframe_scroll_layer_id,
-                                       iframe_reference_frame_id);
-
-        context.builder.push_clip_scroll_node(iframe_reference_frame_id,
-                                              iframe_clip,
-                                              &LayerPoint::zero(),
-                                              &iframe_rect.size);
-        context.builder.push_clip_scroll_node(iframe_scroll_layer_id,
-                                              iframe_clip,
-                                              &LayerPoint::zero(),
-                                              &iframe_clip.main.size);
+        context.builder.add_clip_scroll_node(
+            iframe_scroll_layer_id,
+            iframe_reference_frame_id,
+            pipeline_id,
+            &LayerRect::new(LayerPoint::zero(), iframe_rect.size),
+            &iframe_clip.main.size,
+            Some(ServoScrollRootId(0)),
+            iframe_clip,
+            &mut self.clip_scroll_tree);
 
         let mut traversal = DisplayListTraversal::new_skipping_first(display_list);
-
         self.flatten_stacking_context(&mut traversal,
                                       pipeline_id,
                                       context,
-                                      iframe_reference_frame_id,
                                       iframe_scroll_layer_id,
-                                      LayerToScrollTransform::identity(),
+                                      LayerPoint::zero(),
                                       0,
                                       &iframe_stacking_context,
                                       iframe_clip);
 
-        context.builder.pop_clip_scroll_node();
-        context.builder.pop_clip_scroll_node();
+        context.builder.pop_reference_frame();
     }
 
     fn flatten_items<'a>(&mut self,
                          traversal: &mut DisplayListTraversal<'a>,
                          pipeline_id: PipelineId,
                          context: &mut FlattenContext,
-                         current_reference_frame_id: ScrollLayerId,
-                         current_scroll_layer_id: ScrollLayerId,
-                         layer_relative_transform: LayerToScrollTransform,
+                         reference_frame_relative_offset: LayerPoint,
                          level: i32) {
         while let Some(item) = traversal.next() {
+            let scroll_layer_id = context.scroll_layer_id_with_replacement(item.scroll_layer_id);
             match item.item {
                 SpecificDisplayItem::WebGL(ref info) => {
-                    context.builder.add_webgl_rectangle(item.rect,
-                                                        &item.clip, info.context_id);
+                    context.builder.add_webgl_rectangle(scroll_layer_id,
+                                                        item.rect,
+                                                        &item.clip,
+                                                        info.context_id);
                 }
                 SpecificDisplayItem::Image(ref info) => {
                     let image = context.resource_cache.get_image_properties(info.image_key);
                     if let Some(tile_size) = image.tiling {
                         // The image resource is tiled. We have to generate an image primitive
                         // for each tile.
                         let image_size = DeviceUintSize::new(image.descriptor.width, image.descriptor.height);
-                        self.decompose_tiled_image(context, &item, info, image_size, tile_size as u32);
+                        self.decompose_image(scroll_layer_id,
+                                             context,
+                                             &item.rect,
+                                             &item.clip,
+                                             info,
+                                             image_size,
+                                             tile_size as u32);
                     } else {
-                        context.builder.add_image(item.rect,
+                        context.builder.add_image(scroll_layer_id,
+                                                  item.rect,
                                                   &item.clip,
                                                   &info.stretch_size,
                                                   &info.tile_spacing,
                                                   None,
                                                   info.image_key,
                                                   info.image_rendering,
                                                   None);
                     }
                 }
                 SpecificDisplayItem::YuvImage(ref info) => {
-                    context.builder.add_yuv_image(item.rect,
+                    context.builder.add_yuv_image(scroll_layer_id,
+                                                  item.rect,
                                                   &item.clip,
                                                   info.y_image_key,
                                                   info.u_image_key,
                                                   info.v_image_key,
                                                   info.color_space);
                 }
                 SpecificDisplayItem::Text(ref text_info) => {
-                    context.builder.add_text(item.rect,
+                    context.builder.add_text(scroll_layer_id,
+                                             item.rect,
                                              &item.clip,
                                              text_info.font_key,
                                              text_info.size,
                                              text_info.blur_radius,
                                              &text_info.color,
                                              text_info.glyphs,
                                              text_info.glyph_options);
                 }
                 SpecificDisplayItem::Rectangle(ref info) => {
-                    context.builder.add_solid_rectangle(&item.rect,
+                    context.builder.add_solid_rectangle(scroll_layer_id,
+                                                        &item.rect,
                                                         &item.clip,
                                                         &info.color,
                                                         PrimitiveFlags::None);
                 }
                 SpecificDisplayItem::Gradient(ref info) => {
-                    context.builder.add_gradient(item.rect,
+                    context.builder.add_gradient(scroll_layer_id,
+                                                 item.rect,
                                                  &item.clip,
-                                                 info.start_point,
-                                                 info.end_point,
-                                                 info.stops,
-                                                 info.extend_mode);
+                                                 info.gradient.start_point,
+                                                 info.gradient.end_point,
+                                                 info.gradient.stops,
+                                                 info.gradient.extend_mode);
                 }
                 SpecificDisplayItem::RadialGradient(ref info) => {
-                    context.builder.add_radial_gradient(item.rect,
+                    context.builder.add_radial_gradient(scroll_layer_id,
+                                                        item.rect,
                                                         &item.clip,
-                                                        info.start_center,
-                                                        info.start_radius,
-                                                        info.end_center,
-                                                        info.end_radius,
-                                                        info.stops,
-                                                        info.extend_mode);
+                                                        info.gradient.start_center,
+                                                        info.gradient.start_radius,
+                                                        info.gradient.end_center,
+                                                        info.gradient.end_radius,
+                                                        info.gradient.stops,
+                                                        info.gradient.extend_mode);
                 }
                 SpecificDisplayItem::BoxShadow(ref box_shadow_info) => {
-                    context.builder.add_box_shadow(&box_shadow_info.box_bounds,
+                    context.builder.add_box_shadow(scroll_layer_id,
+                                                   &box_shadow_info.box_bounds,
                                                    &item.clip,
                                                    &box_shadow_info.offset,
                                                    &box_shadow_info.color,
                                                    box_shadow_info.blur_radius,
                                                    box_shadow_info.spread_radius,
                                                    box_shadow_info.border_radius,
                                                    box_shadow_info.clip_mode);
                 }
                 SpecificDisplayItem::Border(ref info) => {
-                    context.builder.add_border(item.rect, &item.clip, info);
+                    context.builder.add_border(scroll_layer_id,
+                                               item.rect,
+                                               &item.clip,
+                                               info);
                 }
                 SpecificDisplayItem::PushStackingContext(ref info) => {
                     self.flatten_stacking_context(traversal,
                                                   pipeline_id,
                                                   context,
-                                                  current_reference_frame_id,
-                                                  current_scroll_layer_id,
-                                                  layer_relative_transform,
+                                                  item.scroll_layer_id,
+                                                  reference_frame_relative_offset,
                                                   level + 1,
                                                   &info.stacking_context,
                                                   &item.clip);
                 }
-                SpecificDisplayItem::PushScrollLayer(ref info) => {
-                    self.flatten_scroll_layer(traversal,
-                                              pipeline_id,
-                                              context,
-                                              current_reference_frame_id,
-                                              current_scroll_layer_id,
-                                              layer_relative_transform,
-                                              level,
-                                              &item.clip,
-                                              &info.content_size,
-                                              info.id);
-                }
                 SpecificDisplayItem::Iframe(ref info) => {
                     self.flatten_iframe(info.pipeline_id,
+                                        scroll_layer_id,
                                         &item.rect,
                                         context,
-                                        current_scroll_layer_id,
-                                        layer_relative_transform);
+                                        reference_frame_relative_offset);
+                }
+                SpecificDisplayItem::Clip(ref info) => {
+                    self.flatten_clip(context,
+                                      pipeline_id,
+                                      scroll_layer_id,
+                                      &info,
+                                      reference_frame_relative_offset,
+                                      &item.clip);
                 }
-                SpecificDisplayItem::PopStackingContext |
-                SpecificDisplayItem::PopScrollLayer => return,
+                SpecificDisplayItem::PopStackingContext => return,
+            }
+        }
+    }
+
+    /// Decomposes an image display item that is repeated into an image per individual repetition.
+    /// We need to do this when we are unable to perform the repetition in the shader,
+    /// for example if the image is tiled.
+    ///
+    /// In all of the "decompose" methods below, we independently handle horizontal and vertical
+    /// decomposition. This lets us generate the minimum amount of primitives by, for  example,
+    /// decompositing the repetition horizontally while repeating vertically in the shader (for
+    /// an image where the width is too bug but the height is not).
+    ///
+    /// decompose_image and decompose_image_row handle image repetitions while decompose_tiled_image
+    /// takes care of the decomposition required by the internal tiling of the image.
+    fn decompose_image(&mut self,
+                       scroll_layer_id: ScrollLayerId,
+                       context: &mut FlattenContext,
+                       item_rect: &LayerRect,
+                       item_clip: &ClipRegion,
+                       info: &ImageDisplayItem,
+                       image_size: DeviceUintSize,
+                       tile_size: u32) {
+        let no_vertical_tiling = image_size.height <= tile_size;
+        let no_vertical_spacing = info.tile_spacing.height == 0.0;
+        if no_vertical_tiling && no_vertical_spacing {
+            self.decompose_image_row(scroll_layer_id, context, item_rect, item_clip, info, image_size, tile_size);
+            return;
+        }
+
+        // Decompose each vertical repetition into rows.
+        let layout_stride = info.stretch_size.height + info.tile_spacing.height;
+        let num_repetitions = (item_rect.size.height / layout_stride).ceil() as u32;
+        for i in 0..num_repetitions {
+            if let Some(row_rect) = rect(
+                item_rect.origin.x,
+                item_rect.origin.y + (i as f32) * layout_stride,
+                item_rect.size.width,
+                info.stretch_size.height
+            ).intersection(&item_rect) {
+                self.decompose_image_row(scroll_layer_id, context, &row_rect, item_clip, info, image_size, tile_size);
+            }
+        }
+    }
+
+    fn decompose_image_row(&mut self,
+                           scroll_layer_id: ScrollLayerId,
+                           context: &mut FlattenContext,
+                           item_rect: &LayerRect,
+                           item_clip: &ClipRegion,
+                           info: &ImageDisplayItem,
+                           image_size: DeviceUintSize,
+                           tile_size: u32) {
+        let no_horizontal_tiling = image_size.width <= tile_size;
+        let no_horizontal_spacing = info.tile_spacing.width == 0.0;
+        if no_horizontal_tiling && no_horizontal_spacing {
+            self.decompose_tiled_image(scroll_layer_id, context, item_rect, item_clip, info, image_size, tile_size);
+            return;
+        }
+
+        // Decompose each horizontal repetition.
+        let layout_stride = info.stretch_size.width + info.tile_spacing.width;
+        let num_repetitions = (item_rect.size.width / layout_stride).ceil() as u32;
+        for i in 0..num_repetitions {
+            if let Some(decomposed_rect) = rect(
+                item_rect.origin.x + (i as f32) * layout_stride,
+                item_rect.origin.y,
+                info.stretch_size.width,
+                item_rect.size.height,
+            ).intersection(&item_rect) {
+                self.decompose_tiled_image(scroll_layer_id, context, &decomposed_rect, item_clip, info, image_size, tile_size);
             }
         }
     }
 
     fn decompose_tiled_image(&mut self,
+                             scroll_layer_id: ScrollLayerId,
                              context: &mut FlattenContext,
-                             item: &DisplayItem,
+                             item_rect: &LayerRect,
+                             item_clip: &ClipRegion,
                              info: &ImageDisplayItem,
                              image_size: DeviceUintSize,
                              tile_size: u32) {
         // The image resource is tiled. We have to generate an image primitive
         // for each tile.
         // We need to do this because the image is broken up into smaller tiles in the texture
         // cache and the image shader is not able to work with this type of sparse representation.
 
@@ -683,120 +771,127 @@ impl Frame {
         //  #----+----+----+----+
         //
         // In the ascii diagram above, a large image is plit into tiles of almost regular size.
         // The tiles on the right and bottom edges (hatched in the diagram) are smaller than
         // the regular tiles and are handled separately in the code see leftover_width/height.
         // each generated image primitive corresponds to a tile in the texture cache, with the
         // assumption that the smaller tiles with leftover sizes are sized to fit their own
         // irregular size in the texture cache.
-
-        // TODO(nical) supporting tiled repeated images isn't implemented yet.
-        // One way to implement this is to have another level of decomposition on top of this one,
-        // and generate a set of image primitive per repetition just like we have a primitive
-        // per tile here.
         //
         // For the case where we don't tile along an axis, we can still perform the repetition in
         // the shader (for this particular axis), and it is worth special-casing for this to avoid
         // generating many primitives.
         // This can happen with very tall and thin images used as a repeating background.
         // Apparently web authors do that...
 
-        let mut stretch_size = info.stretch_size;
-
         let mut repeat_x = false;
         let mut repeat_y = false;
 
-        if stretch_size.width < item.rect.size.width {
-            if image_size.width < tile_size {
-                // we don't actually tile in this dimmension so repeating can be done in the shader.
-                repeat_x = true;
-            } else {
-                println!("Unimplemented! repeating a tiled image (x axis)");
-                stretch_size.width = item.rect.size.width;
-            }
+        if info.stretch_size.width < item_rect.size.width {
+            // If this assert blows up it means we haven't properly decomposed the image in decompose_image_row.
+            debug_assert!(image_size.width <= tile_size);
+            // we don't actually tile in this dimmension so repeating can be done in the shader.
+            repeat_x = true;
         }
 
-        if stretch_size.height < item.rect.size.height {
-                // we don't actually tile in this dimmension so repeating can be done in the shader.
-            if image_size.height < tile_size {
-                repeat_y = true;
-            } else {
-                println!("Unimplemented! repeating a tiled image (y axis)");
-                stretch_size.height = item.rect.size.height;
-            }
+        if info.stretch_size.height < item_rect.size.height {
+            // If this assert blows up it means we haven't properly decomposed the image in decompose_image.
+            debug_assert!(image_size.height <= tile_size);
+            // we don't actually tile in this dimmension so repeating can be done in the shader.
+            repeat_y = true;
         }
 
         let tile_size_f32 = tile_size as f32;
 
         // Note: this rounds down so it excludes the partially filled tiles on the right and
         // bottom edges (we handle them separately below).
         let num_tiles_x = (image_size.width / tile_size) as u16;
         let num_tiles_y = (image_size.height / tile_size) as u16;
 
         // Ratio between (image space) tile size and image size.
         let img_dw = tile_size_f32 / (image_size.width as f32);
         let img_dh = tile_size_f32 / (image_size.height as f32);
 
         // Strected size of the tile in layout space.
         let stretched_tile_size = LayerSize::new(
-            img_dw * stretch_size.width,
-            img_dh * stretch_size.height
+            img_dw * info.stretch_size.width,
+            img_dh * info.stretch_size.height
         );
 
         // The size in pixels of the tiles on the right and bottom edges, smaller
         // than the regular tile size if the image is not a multiple of the tile size.
         // Zero means the image size is a multiple of the tile size.
         let leftover = DeviceUintSize::new(image_size.width % tile_size, image_size.height % tile_size);
 
         for ty in 0..num_tiles_y {
             for tx in 0..num_tiles_x {
-                self.add_tile_primitive(context, item, info,
+                self.add_tile_primitive(scroll_layer_id,
+                                        context,
+                                        item_rect,
+                                        item_clip,
+                                        info,
                                         TileOffset::new(tx, ty),
                                         stretched_tile_size,
                                         1.0, 1.0,
                                         repeat_x, repeat_y);
             }
             if leftover.width != 0 {
                 // Tiles on the right edge that are smaller than the tile size.
-                self.add_tile_primitive(context, item, info,
+                self.add_tile_primitive(scroll_layer_id,
+                                        context,
+                                        item_rect,
+                                        item_clip,
+                                        info,
                                         TileOffset::new(num_tiles_x, ty),
                                         stretched_tile_size,
                                         (leftover.width as f32) / tile_size_f32,
                                         1.0,
                                         repeat_x, repeat_y);
             }
         }
 
         if leftover.height != 0 {
             for tx in 0..num_tiles_x {
                 // Tiles on the bottom edge that are smaller than the tile size.
-                self.add_tile_primitive(context, item, info,
+                self.add_tile_primitive(scroll_layer_id,
+                                        context,
+                                        item_rect,
+                                        item_clip,
+                                        info,
                                         TileOffset::new(tx, num_tiles_y),
                                         stretched_tile_size,
                                         1.0,
                                         (leftover.height as f32) / tile_size_f32,
-                                        repeat_x, repeat_y);
+                                        repeat_x,
+                                        repeat_y);
             }
 
             if leftover.width != 0 {
                 // Finally, the bottom-right tile with a "leftover" size.
-                self.add_tile_primitive(context, item, info,
+                self.add_tile_primitive(scroll_layer_id,
+                                        context,
+                                        item_rect,
+                                        item_clip,
+                                        info,
                                         TileOffset::new(num_tiles_x, num_tiles_y),
                                         stretched_tile_size,
                                         (leftover.width as f32) / tile_size_f32,
                                         (leftover.height as f32) / tile_size_f32,
-                                        repeat_x, repeat_y);
+                                        repeat_x,
+                                        repeat_y);
             }
         }
     }
 
     fn add_tile_primitive(&mut self,
+                          scroll_layer_id: ScrollLayerId,
                           context: &mut FlattenContext,
-                          item: &DisplayItem,
+                          item_rect: &LayerRect,
+                          item_clip: &ClipRegion,
                           info: &ImageDisplayItem,
                           tile_offset: TileOffset,
                           stretched_tile_size: LayerSize,
                           tile_ratio_width: f32,
                           tile_ratio_height: f32,
                           repeat_x: bool,
                           repeat_y: bool) {
         // If the the image is tiled along a given axis, we can't have the shader compute
@@ -809,53 +904,55 @@ impl Frame {
         // See the repeat_x/y code below.
 
         let stretched_size = LayerSize::new(
             stretched_tile_size.width * tile_ratio_width,
             stretched_tile_size.height * tile_ratio_height,
         );
 
         let mut prim_rect = LayerRect::new(
-            item.rect.origin + LayerPoint::new(
+            item_rect.origin + LayerPoint::new(
                 tile_offset.x as f32 * stretched_tile_size.width,
                 tile_offset.y as f32 * stretched_tile_size.height,
             ),
             stretched_size,
         );
 
         if repeat_x {
             assert_eq!(tile_offset.x, 0);
-            prim_rect.size.width = item.rect.size.width;
+            prim_rect.size.width = item_rect.size.width;
         }
 
         if repeat_y {
             assert_eq!(tile_offset.y, 0);
-            prim_rect.size.height = item.rect.size.height;
+            prim_rect.size.height = item_rect.size.height;
         }
 
         // Fix up the primitive's rect if it overflows the original item rect.
-        if let Some(prim_rect) = prim_rect.intersection(&item.rect) {
-            context.builder.add_image(prim_rect,
-                                      &item.clip,
+        if let Some(prim_rect) = prim_rect.intersection(&item_rect) {
+            context.builder.add_image(scroll_layer_id,
+                                      prim_rect,
+                                      &item_clip,
                                       &stretched_size,
                                       &info.tile_spacing,
                                       None,
                                       info.image_key,
                                       info.image_rendering,
                                       Some(tile_offset));
         }
     }
 
     pub fn build(&mut self,
                  resource_cache: &mut ResourceCache,
                  auxiliary_lists_map: &AuxiliaryListsMap,
                  device_pixel_ratio: f32,
+                 pan: LayerPoint,
                  texture_cache_profile: &mut TextureCacheProfileCounters)
                  -> RendererFrame {
-        self.clip_scroll_tree.update_all_node_transforms();
+        self.clip_scroll_tree.update_all_node_transforms(pan);
         let frame = self.build_frame(resource_cache,
                                      auxiliary_lists_map,
                                      device_pixel_ratio,
                                      texture_cache_profile);
         resource_cache.expire_old_resources(self.id);
         frame
     }
 
@@ -864,17 +961,17 @@ impl Frame {
                    auxiliary_lists_map: &AuxiliaryListsMap,
                    device_pixel_ratio: f32,
                    texture_cache_profile: &mut TextureCacheProfileCounters)
                    -> RendererFrame {
         let mut frame_builder = self.frame_builder.take();
         let frame = frame_builder.as_mut().map(|builder|
             builder.build(resource_cache,
                           self.id,
-                          &self.clip_scroll_tree,
+                          &mut self.clip_scroll_tree,
                           auxiliary_lists_map,
                           device_pixel_ratio,
                           texture_cache_profile)
         );
         self.frame_builder = frame_builder;
 
         let nodes_bouncing_back = self.clip_scroll_tree.collect_nodes_bouncing_back();
         RendererFrame::new(self.pipeline_epoch_map.clone(), nodes_bouncing_back, frame)
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -13,30 +13,33 @@ use prim_store::{GradientPrimitiveCpu, G
 use prim_store::{ImagePrimitiveKind, PrimitiveContainer, PrimitiveGeometry, PrimitiveIndex};
 use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu, RadialGradientPrimitiveGpu};
 use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu, TextRunPrimitiveGpu};
 use prim_store::{TexelRect, YuvImagePrimitiveCpu, YuvImagePrimitiveGpu};
 use profiler::{FrameProfileCounters, TextureCacheProfileCounters};
 use render_task::{AlphaRenderItem, MaskCacheKey, MaskResult, RenderTask, RenderTaskIndex};
 use render_task::RenderTaskLocation;
 use resource_cache::ResourceCache;
+use clip_scroll_node::{ClipInfo, ClipScrollNode, NodeType};
 use clip_scroll_tree::ClipScrollTree;
 use std::{cmp, f32, i32, mem, usize};
+use euclid::SideOffsets2D;
+use tiling::StackingContextIndex;
 use tiling::{AuxiliaryListsMap, ClipScrollGroup, ClipScrollGroupIndex, CompositeOps, Frame};
 use tiling::{PackedLayer, PackedLayerIndex, PrimitiveFlags, PrimitiveRunCmd, RenderPass};
-use tiling::{RenderTargetContext, RenderTaskCollection, ScrollbarPrimitive, ScrollLayer};
-use tiling::{ScrollLayerIndex, StackingContext, StackingContextIndex};
-use util::{self, pack_as_float, rect_from_points_f, subtract_rect, TransformedRect};
+use tiling::{RenderTargetContext, RenderTaskCollection, ScrollbarPrimitive, StackingContext};
+use util::{self, pack_as_float, rect_from_points_f, subtract_rect};
 use util::{RectHelpers, TransformedRectKind};
-use webrender_traits::{as_scroll_parent_rect, BorderDetails, BorderDisplayItem, BorderSide, BorderStyle};
-use webrender_traits::{BoxShadowClipMode, ClipRegion, ColorF, device_length, DeviceIntPoint};
-use webrender_traits::{DeviceIntRect, DeviceIntSize, DeviceUintSize, ExtendMode, FontKey, TileOffset};
+use webrender_traits::{BorderDetails, BorderDisplayItem, BorderSide, BorderStyle};
+use webrender_traits::{BoxShadowClipMode, ClipRegion, ColorF, DeviceIntPoint, DeviceIntRect};
+use webrender_traits::{DeviceIntSize, DeviceUintRect, DeviceUintSize, ExtendMode, FontKey};
 use webrender_traits::{FontRenderMode, GlyphOptions, ImageKey, ImageRendering, ItemRange};
 use webrender_traits::{LayerPoint, LayerRect, LayerSize, LayerToScrollTransform, PipelineId};
-use webrender_traits::{RepeatMode, ScrollLayerId, ScrollLayerPixel, WebGLContextId, YuvColorSpace};
+use webrender_traits::{RepeatMode, ScrollLayerId, ServoScrollRootId, TileOffset, WebGLContextId};
+use webrender_traits::YuvColorSpace;
 
 #[derive(Debug, Clone)]
 struct ImageBorderSegment {
     geom_rect: LayerRect,
     sub_rect: TexelRect,
     stretch_size: LayerSize,
     tile_spacing: LayerSize,
 }
@@ -97,57 +100,70 @@ impl FrameBuilderConfig {
             enable_scrollbars: enable_scrollbars,
             enable_subpixel_aa: enable_subpixel_aa,
             debug: debug,
         }
     }
 }
 
 pub struct FrameBuilder {
-    screen_rect: LayerRect,
+    screen_size: DeviceUintSize,
     background_color: Option<ColorF>,
     prim_store: PrimitiveStore,
     cmds: Vec<PrimitiveRunCmd>,
     config: FrameBuilderConfig,
 
     stacking_context_store: Vec<StackingContext>,
-    scroll_layer_store: Vec<ScrollLayer>,
     clip_scroll_group_store: Vec<ClipScrollGroup>,
     packed_layers: Vec<PackedLayer>,
 
     scrollbar_prims: Vec<ScrollbarPrimitive>,
 
     /// A stack of scroll nodes used during display list processing to properly
     /// parent new scroll nodes.
-    clip_scroll_node_stack: Vec<ScrollLayerIndex>,
+    reference_frame_stack: Vec<ScrollLayerId>,
+
+    /// A stack of stacking contexts used for creating ClipScrollGroups as
+    /// primitives are added to the frame.
+    stacking_context_stack: Vec<StackingContextIndex>,
 }
 
 impl FrameBuilder {
-    pub fn new(viewport_size: LayerSize,
+    pub fn new(screen_size: DeviceUintSize,
                background_color: Option<ColorF>,
                config: FrameBuilderConfig) -> FrameBuilder {
         FrameBuilder {
-            screen_rect: LayerRect::new(LayerPoint::zero(), viewport_size),
+            screen_size: screen_size,
             background_color: background_color,
             stacking_context_store: Vec::new(),
-            scroll_layer_store: Vec::new(),
             clip_scroll_group_store: Vec::new(),
             prim_store: PrimitiveStore::new(),
             cmds: Vec::new(),
             packed_layers: Vec::new(),
             scrollbar_prims: Vec::new(),
             config: config,
-            clip_scroll_node_stack: Vec::new(),
+            reference_frame_stack: Vec::new(),
+            stacking_context_stack: Vec::new(),
         }
     }
 
     fn add_primitive(&mut self,
+                     scroll_layer_id: ScrollLayerId,
                      rect: &LayerRect,
                      clip_region: &ClipRegion,
-                     container: PrimitiveContainer) -> PrimitiveIndex {
+                     container: PrimitiveContainer)
+                     -> PrimitiveIndex {
+        let stacking_context_index = *self.stacking_context_stack.last().unwrap();
+        if !self.stacking_context_store[stacking_context_index.0]
+                .has_clip_scroll_group(scroll_layer_id) {
+            let group_index = self.create_clip_scroll_group(stacking_context_index,
+                                                            scroll_layer_id);
+            let stacking_context = &mut self.stacking_context_store[stacking_context_index.0];
+            stacking_context.clip_scroll_groups.push(group_index);
+        }
 
         let geometry = PrimitiveGeometry {
             local_rect: *rect,
             local_clip_rect: clip_region.main,
         };
         let clip_source = if clip_region.is_complex() {
             ClipSource::Region(clip_region.clone())
         } else {
@@ -158,135 +174,195 @@ impl FrameBuilder {
                                            &mut self.prim_store.gpu_data32);
 
         let prim_index = self.prim_store.add_primitive(geometry,
                                                        Box::new(clip_source),
                                                        clip_info,
                                                        container);
 
         match self.cmds.last_mut().unwrap() {
-            &mut PrimitiveRunCmd::PrimitiveRun(_run_prim_index, ref mut count) => {
-                debug_assert!(_run_prim_index.0 + *count == prim_index.0);
-                *count += 1;
-                return prim_index;
+            &mut PrimitiveRunCmd::PrimitiveRun(_run_prim_index, ref mut count, run_layer_id)
+                if run_layer_id == scroll_layer_id => {
+                    debug_assert!(_run_prim_index.0 + *count == prim_index.0);
+                    *count += 1;
+                    return prim_index;
             }
+            &mut PrimitiveRunCmd::PrimitiveRun(..) |
             &mut PrimitiveRunCmd::PushStackingContext(..) |
-            &mut PrimitiveRunCmd::PopStackingContext |
-            &mut PrimitiveRunCmd::PushScrollLayer(..) |
-            &mut PrimitiveRunCmd::PopScrollLayer => {}
+            &mut PrimitiveRunCmd::PopStackingContext => {}
         }
 
-        self.cmds.push(PrimitiveRunCmd::PrimitiveRun(prim_index, 1));
-
+        self.cmds.push(PrimitiveRunCmd::PrimitiveRun(prim_index, 1, scroll_layer_id));
         prim_index
     }
 
     pub fn create_clip_scroll_group(&mut self,
                                     stacking_context_index: StackingContextIndex,
-                                    scroll_layer_id: ScrollLayerId,
-                                    pipeline_id: PipelineId)
+                                    scroll_layer_id: ScrollLayerId)
                                     -> ClipScrollGroupIndex {
         let packed_layer_index = PackedLayerIndex(self.packed_layers.len());
         self.packed_layers.push(PackedLayer::empty());
 
         self.clip_scroll_group_store.push(ClipScrollGroup {
             stacking_context_index: stacking_context_index,
             scroll_layer_id: scroll_layer_id,
             packed_layer_index: packed_layer_index,
-            pipeline_id: pipeline_id,
             xf_rect: None,
          });
 
-        ClipScrollGroupIndex(self.clip_scroll_group_store.len() - 1)
+        ClipScrollGroupIndex(self.clip_scroll_group_store.len() - 1, scroll_layer_id)
     }
 
     pub fn push_stacking_context(&mut self,
+                                 reference_frame_offset: LayerPoint,
                                  rect: LayerRect,
-                                 transform: LayerToScrollTransform,
                                  pipeline_id: PipelineId,
-                                 scroll_layer_id: ScrollLayerId,
+                                 is_page_root: bool,
                                  composite_ops: CompositeOps) {
+        if let Some(parent_index) = self.stacking_context_stack.last() {
+            let parent_is_root = self.stacking_context_store[parent_index.0].is_page_root;
+
+            if composite_ops.mix_blend_mode.is_some() && !parent_is_root {
+                // the parent stacking context of a stacking context with mix-blend-mode
+                // must be drawn with a transparent background, unless the parent stacking context
+                // is the root of the page
+                self.stacking_context_store[parent_index.0].should_isolate = true;
+            }
+        }
+
         let stacking_context_index = StackingContextIndex(self.stacking_context_store.len());
-        let group_index = self.create_clip_scroll_group(stacking_context_index,
-                                                        scroll_layer_id,
-                                                        pipeline_id);
         self.stacking_context_store.push(StackingContext::new(pipeline_id,
-                                                              transform,
+                                                              reference_frame_offset,
                                                               rect,
-                                                              composite_ops,
-                                                              group_index));
+                                                              is_page_root,
+                                                              composite_ops));
         self.cmds.push(PrimitiveRunCmd::PushStackingContext(stacking_context_index));
+        self.stacking_context_stack.push(stacking_context_index);
     }
 
     pub fn pop_stacking_context(&mut self) {
         self.cmds.push(PrimitiveRunCmd::PopStackingContext);
+        self.stacking_context_stack.pop();
+    }
+
+    pub fn push_reference_frame(&mut self,
+                                parent_id: Option<ScrollLayerId>,
+                                pipeline_id: PipelineId,
+                                rect: &LayerRect,
+                                transform: &LayerToScrollTransform,
+                                clip_scroll_tree: &mut ClipScrollTree)
+                                -> ScrollLayerId {
+        let new_id = clip_scroll_tree.add_reference_frame(rect, transform, pipeline_id, parent_id);
+        self.reference_frame_stack.push(new_id);
+        new_id
+    }
+
+    pub fn current_reference_frame_id(&self) -> ScrollLayerId {
+        *self.reference_frame_stack.last().unwrap()
     }
 
-    pub fn push_clip_scroll_node(&mut self,
-                                 scroll_layer_id: ScrollLayerId,
-                                 clip_region: &ClipRegion,
-                                 node_origin: &LayerPoint,
-                                 content_size: &LayerSize) {
-        let scroll_layer_index = ScrollLayerIndex(self.scroll_layer_store.len());
-        let parent_index = *self.clip_scroll_node_stack.last().unwrap_or(&scroll_layer_index);
-        self.clip_scroll_node_stack.push(scroll_layer_index);
-
-        let packed_layer_index = PackedLayerIndex(self.packed_layers.len());
-
-        let clip_source = ClipSource::Region(clip_region.clone());
-        let clip_info = MaskCacheInfo::new(&clip_source,
-                                           true, // needs an extra clip for the clip rectangle
-                                           &mut self.prim_store.gpu_data32);
+    pub fn setup_viewport_offset(&mut self,
+                                 window_size: DeviceUintSize,
+                                 inner_rect: DeviceUintRect,
+                                 device_pixel_ratio: f32,
+                                 clip_scroll_tree: &mut ClipScrollTree) {
+        let inner_origin = inner_rect.origin.to_f32();
+        let viewport_offset = LayerPoint::new((inner_origin.x / device_pixel_ratio).round(),
+                                              (inner_origin.y / device_pixel_ratio).round());
+        let outer_size = window_size.to_f32();
+        let outer_size = LayerSize::new((outer_size.width / device_pixel_ratio).round(),
+                                        (outer_size.height / device_pixel_ratio).round());
+        let clip_size = LayerSize::new(outer_size.width + 2.0 * viewport_offset.x,
+                                       outer_size.height + 2.0 * viewport_offset.y);
 
-        self.scroll_layer_store.push(ScrollLayer {
-            scroll_layer_id: scroll_layer_id,
-            parent_index: parent_index,
-            clip_source: clip_source,
-            clip_cache_info: clip_info,
-            xf_rect: None,
-            packed_layer_index: packed_layer_index,
-        });
-        self.packed_layers.push(PackedLayer::empty());
-        self.cmds.push(PrimitiveRunCmd::PushScrollLayer(scroll_layer_index));
-
+        let viewport_clip = LayerRect::new(LayerPoint::new(-viewport_offset.x, -viewport_offset.y),
+                                           LayerSize::new(clip_size.width, clip_size.height));
 
-        // We need to push a fake stacking context here, because primitives that are
-        // direct children of this stacking context, need to be adjusted by the scroll
-        // offset of this layer. Eventually we should be able to remove this.
-        let rect = LayerRect::new(LayerPoint::zero(),
-                                  LayerSize::new(content_size.width + node_origin.x,
-                                                 content_size.height + node_origin.y));
-        self.push_stacking_context(rect,
-                                   LayerToScrollTransform::identity(),
-                                   scroll_layer_id.pipeline_id,
-                                   scroll_layer_id,
-                                   CompositeOps::empty());
+        let root_id = clip_scroll_tree.root_reference_frame_id();
+        if let Some(root_node) = clip_scroll_tree.nodes.get_mut(&root_id) {
+            if let NodeType::ReferenceFrame(ref mut transform) = root_node.node_type {
+                *transform = LayerToScrollTransform::create_translation(viewport_offset.x,
+                                                                        viewport_offset.y,
+                                                                        0.0);
+            }
+            root_node.local_clip_rect = viewport_clip;
+        }
 
+        let scroll_layer_id = clip_scroll_tree.topmost_scroll_layer_id();
+        if let Some(root_node) = clip_scroll_tree.nodes.get_mut(&scroll_layer_id) {
+            root_node.local_clip_rect = viewport_clip;
+        }
     }
 
-    pub fn pop_clip_scroll_node(&mut self) {
-        self.pop_stacking_context();
-        self.cmds.push(PrimitiveRunCmd::PopScrollLayer);
-        self.clip_scroll_node_stack.pop();
+    pub fn push_root(&mut self,
+                     pipeline_id: PipelineId,
+                     viewport_size: &LayerSize,
+                     content_size: &LayerSize,
+                     clip_scroll_tree: &mut ClipScrollTree)
+                     -> ScrollLayerId {
+        let viewport_rect = LayerRect::new(LayerPoint::zero(), *viewport_size);
+        let identity = &LayerToScrollTransform::identity();
+        self.push_reference_frame(None, pipeline_id, &viewport_rect, identity, clip_scroll_tree);
+
+        let topmost_scroll_layer_id = ScrollLayerId::root_scroll_layer(pipeline_id);
+        clip_scroll_tree.topmost_scroll_layer_id = topmost_scroll_layer_id;
+        self.add_clip_scroll_node(topmost_scroll_layer_id,
+                                   clip_scroll_tree.root_reference_frame_id,
+                                   pipeline_id,
+                                   &viewport_rect,
+                                   content_size,
+                                   Some(ServoScrollRootId(0)),
+                                   &ClipRegion::simple(&viewport_rect),
+                                   clip_scroll_tree);
+        topmost_scroll_layer_id
+    }
+
+    pub fn add_clip_scroll_node(&mut self,
+                                new_node_id: ScrollLayerId,
+                                parent_id: ScrollLayerId,
+                                pipeline_id: PipelineId,
+                                local_viewport_rect: &LayerRect,
+                                content_size: &LayerSize,
+                                scroll_root_id: Option<ServoScrollRootId>,
+                                clip_region: &ClipRegion,
+                                clip_scroll_tree: &mut ClipScrollTree) {
+        let clip_info = ClipInfo::new(clip_region,
+                                      &mut self.prim_store.gpu_data32,
+                                      PackedLayerIndex(self.packed_layers.len()),
+                                      scroll_root_id);
+        let node = ClipScrollNode::new(pipeline_id,
+                                       parent_id,
+                                       local_viewport_rect,
+                                       *content_size,
+                                       clip_info);
+
+        clip_scroll_tree.add_node(node, new_node_id);
+        self.packed_layers.push(PackedLayer::empty());
+    }
+
+    pub fn pop_reference_frame(&mut self) {
+        self.reference_frame_stack.pop();
     }
 
     pub fn add_solid_rectangle(&mut self,
+                               scroll_layer_id: ScrollLayerId,
                                rect: &LayerRect,
                                clip_region: &ClipRegion,
                                color: &ColorF,
                                flags: PrimitiveFlags) {
         if color.a == 0.0 {
             return;
         }
 
         let prim = RectanglePrimitive {
             color: *color,
         };
 
-        let prim_index = self.add_primitive(rect,
+        let prim_index = self.add_primitive(scroll_layer_id,
+                                            rect,
                                             clip_region,
                                             PrimitiveContainer::Rectangle(prim));
 
         match flags {
             PrimitiveFlags::None => {}
             PrimitiveFlags::Scrollbar(scroll_layer_id, border_radius) => {
                 self.scrollbar_prims.push(ScrollbarPrimitive {
                     prim_index: prim_index,
@@ -313,19 +389,62 @@ impl FrameBuilder {
             _ => {
                 println!("TODO: Other border styles {:?}", border.style);
                 return false;
             }
         }
     }
 
     pub fn add_border(&mut self,
+                      scroll_layer_id: ScrollLayerId,
                       rect: LayerRect,
                       clip_region: &ClipRegion,
                       border_item: &BorderDisplayItem) {
+        let create_segments = |outset: SideOffsets2D<f32>| {
+            // Calculate the modified rect as specific by border-image-outset
+            let origin = LayerPoint::new(rect.origin.x - outset.left,
+                                         rect.origin.y - outset.top);
+            let size = LayerSize::new(rect.size.width + outset.left + outset.right,
+                                      rect.size.height + outset.top + outset.bottom);
+            let rect = LayerRect::new(origin, size);
+
+            let tl_outer = LayerPoint::new(rect.origin.x, rect.origin.y);
+            let tl_inner = tl_outer + LayerPoint::new(border_item.widths.left, border_item.widths.top);
+
+            let tr_outer = LayerPoint::new(rect.origin.x + rect.size.width, rect.origin.y);
+            let tr_inner = tr_outer + LayerPoint::new(-border_item.widths.right, border_item.widths.top);
+
+            let bl_outer = LayerPoint::new(rect.origin.x, rect.origin.y + rect.size.height);
+            let bl_inner = bl_outer + LayerPoint::new(border_item.widths.left, -border_item.widths.bottom);
+
+            let br_outer = LayerPoint::new(rect.origin.x + rect.size.width,
+                                           rect.origin.y + rect.size.height);
+            let br_inner = br_outer - LayerPoint::new(border_item.widths.right, border_item.widths.bottom);
+
+            // Build the list of gradient segments
+            vec![
+                // Top left
+                LayerRect::from_floats(tl_outer.x, tl_outer.y, tl_inner.x, tl_inner.y),
+                // Top right
+                LayerRect::from_floats(tr_inner.x, tr_outer.y, tr_outer.x, tr_inner.y),
+                // Bottom right
+                LayerRect::from_floats(br_inner.x, br_inner.y, br_outer.x, br_outer.y),
+                // Bottom left
+                LayerRect::from_floats(bl_outer.x, bl_inner.y, bl_inner.x, bl_outer.y),
+                // Top
+                LayerRect::from_floats(tl_inner.x, tl_outer.y, tr_inner.x, tl_inner.y),
+                // Bottom
+                LayerRect::from_floats(bl_inner.x, bl_inner.y, br_inner.x, bl_outer.y),
+                // Left
+                LayerRect::from_floats(tl_outer.x, tl_inner.y, tl_inner.x, bl_inner.y),
+                // Right
+                LayerRect::from_floats(tr_inner.x, tr_inner.y, br_outer.x, br_inner.y),
+            ]
+        };
+
         match border_item.details {
             BorderDetails::Image(ref border) => {
                 // Calculate the modified rect as specific by border-image-outset
                 let origin = LayerPoint::new(rect.origin.x - border.outset.left,
                                              rect.origin.y - border.outset.top);
                 let size = LayerSize::new(rect.size.width + border.outset.left + border.outset.right,
                                           rect.size.height + border.outset.top + border.outset.bottom);
                 let rect = LayerRect::new(origin, size);
@@ -406,17 +525,18 @@ impl FrameBuilder {
                         ImageBorderSegment::new(LayerRect::from_floats(tr_inner.x, tr_inner.y, br_outer.x, br_inner.y),
                                                 TexelRect::new(px2, py1, px3, py2),
                                                 RepeatMode::Stretch,
                                                 border.repeat_vertical),
                     ]);
                 }
 
                 for segment in segments {
-                    self.add_image(segment.geom_rect,
+                    self.add_image(scroll_layer_id,
+                                   segment.geom_rect,
                                    clip_region,
                                    &segment.stretch_size,
                                    &segment.tile_spacing,
                                    Some(segment.sub_rect),
                                    border.image_key,
                                    ImageRendering::Auto,
                                    None);
                 }
@@ -487,17 +607,18 @@ impl FrameBuilder {
                             LayerRect::new(tr_inner,
                                            LayerSize::new(border_item.widths.right,
                                                           rect.size.height - border_item.widths.top - border_item.widths.bottom)),
                             LayerRect::new(LayerPoint::new(bl_outer.x, bl_inner.y),
                                            LayerSize::new(rect.size.width, border_item.widths.bottom))
                         ];
 
                         for rect in &rects {
-                            self.add_solid_rectangle(rect,
+                            self.add_solid_rectangle(scroll_layer_id,
+                                                     rect,
                                                      clip_region,
                                                      &top_color,
                                                      PrimitiveFlags::None);
                         }
 
                         return;
                     }
                 }
@@ -528,24 +649,50 @@ impl FrameBuilder {
                     radii: [
                         radius.top_left,
                         radius.top_right,
                         radius.bottom_right,
                         radius.bottom_left,
                     ],
                 };
 
-                self.add_primitive(&rect,
+                self.add_primitive(scroll_layer_id,
+                                   &rect,
                                    clip_region,
                                    PrimitiveContainer::Border(prim_cpu, prim_gpu));
             }
+            BorderDetails::Gradient(ref border) => {
+                for segment in create_segments(border.outset) {
+                    self.add_gradient(scroll_layer_id,
+                                      segment,
+                                      clip_region,
+                                      border.gradient.start_point,
+                                      border.gradient.end_point,
+                                      border.gradient.stops,
+                                      border.gradient.extend_mode);
+                }
+            }
+            BorderDetails::RadialGradient(ref border) => {
+                for segment in create_segments(border.outset) {
+                    self.add_radial_gradient(scroll_layer_id,
+                                             segment,
+                                             clip_region,
+                                             border.gradient.start_center,
+                                             border.gradient.start_radius,
+                                             border.gradient.end_center,
+                                             border.gradient.end_radius,
+                                             border.gradient.stops,
+                                             border.gradient.extend_mode);
+                }
+            }
         }
     }
 
     pub fn add_gradient(&mut self,
+                        scroll_layer_id: ScrollLayerId,
                         rect: LayerRect,
                         clip_region: &ClipRegion,
                         start_point: LayerPoint,
                         end_point: LayerPoint,
                         stops: ItemRange,
                         extend_mode: ExtendMode) {
         // Fast path for clamped, axis-aligned gradients:
         let aligned = extend_mode == ExtendMode::Clamp &&
@@ -586,20 +733,21 @@ impl FrameBuilder {
         };
 
         let prim = if aligned {
             PrimitiveContainer::AlignedGradient(gradient_cpu, gradient_gpu)
         } else {
             PrimitiveContainer::AngleGradient(gradient_cpu, gradient_gpu)
         };
 
-        self.add_primitive(&rect, clip_region, prim);
+        self.add_primitive(scroll_layer_id, &rect, clip_region, prim);
     }
 
     pub fn add_radial_gradient(&mut self,
+                               scroll_layer_id: ScrollLayerId,
                                rect: LayerRect,
                                clip_region: &ClipRegion,
                                start_center: LayerPoint,
                                start_radius: f32,
                                end_center: LayerPoint,
                                end_radius: f32,
                                stops: ItemRange,
                                extend_mode: ExtendMode) {
@@ -613,22 +761,24 @@ impl FrameBuilder {
             start_center: start_center,
             end_center: end_center,
             start_radius: start_radius,
             end_radius: end_radius,
             extend_mode: pack_as_float(extend_mode as u32),
             padding: [0.0],
         };
 
-        self.add_primitive(&rect,
+        self.add_primitive(scroll_layer_id,
+                           &rect,
                            clip_region,
                            PrimitiveContainer::RadialGradient(radial_gradient_cpu, radial_gradient_gpu));
     }
 
     pub fn add_text(&mut self,
+                    scroll_layer_id: ScrollLayerId,
                     rect: LayerRect,
                     clip_region: &ClipRegion,
                     font_key: FontKey,
                     size: Au,
                     blur_radius: Au,
                     color: &ColorF,
                     glyph_range: ItemRange,
                     glyph_options: Option<GlyphOptions>) {
@@ -680,38 +830,41 @@ impl FrameBuilder {
                 glyph_options: glyph_options,
                 resource_address: GpuStoreAddress(0),
             };
 
             let prim_gpu = TextRunPrimitiveGpu {
                 color: *color,
             };
 
-            self.add_primitive(&rect,
+            self.add_primitive(scroll_layer_id,
+                               &rect,
                                clip_region,
                                PrimitiveContainer::TextRun(prim_cpu, prim_gpu));
         }
     }
 
     pub fn add_box_shadow(&mut self,
+                          scroll_layer_id: ScrollLayerId,
                           box_bounds: &LayerRect,
                           clip_region: &ClipRegion,
                           box_offset: &LayerPoint,
                           color: &ColorF,
                           blur_radius: f32,
                           spread_radius: f32,
                           border_radius: f32,
                           clip_mode: BoxShadowClipMode) {
         if color.a == 0.0 {
             return
         }
 
         // Fast path.
         if blur_radius == 0.0 && spread_radius == 0.0 && clip_mode == BoxShadowClipMode::None {
-            self.add_solid_rectangle(&box_bounds,
+            self.add_solid_rectangle(scroll_layer_id,
+                                     &box_bounds,
                                      clip_region,
                                      color,
                                      PrimitiveFlags::None);
             return;
         }
 
         // The local space box shadow rect. It is the element rect
         // translated by the box shadow offset and inflated by the
@@ -777,67 +930,73 @@ impl FrameBuilder {
                     BoxShadowKind::Shadow(rects)
                 }
             }
         };
 
         match shadow_kind {
             BoxShadowKind::Simple(rects) => {
                 for rect in &rects {
-                    self.add_solid_rectangle(rect,
+                    self.add_solid_rectangle(scroll_layer_id,
+                                             rect,
                                              clip_region,
                                              color,
                                              PrimitiveFlags::None)
                 }
             }
             BoxShadowKind::Shadow(rects) => {
                 let inverted = match clip_mode {
                     BoxShadowClipMode::Outset | BoxShadowClipMode::None => 0.0,
                     BoxShadowClipMode::Inset => 1.0,
                 };
 
+
                 let prim_gpu = BoxShadowPrimitiveGpu {
                     src_rect: *box_bounds,
                     bs_rect: bs_rect,
                     color: *color,
                     blur_radius: blur_radius,
                     border_radius: border_radius,
                     edge_size: edge_size,
                     inverted: inverted,
                 };
 
-                self.add_primitive(&outer_rect,
+                self.add_primitive(scroll_layer_id,
+                                   &outer_rect,
                                    clip_region,
                                    PrimitiveContainer::BoxShadow(prim_gpu, rects));
             }
         }
     }
 
     pub fn add_webgl_rectangle(&mut self,
+                               scroll_layer_id: ScrollLayerId,
                                rect: LayerRect,
                                clip_region: &ClipRegion,
                                context_id: WebGLContextId) {
         let prim_cpu = ImagePrimitiveCpu {
             kind: ImagePrimitiveKind::WebGL(context_id),
             color_texture_id: SourceTexture::Invalid,
             resource_address: GpuStoreAddress(0),
             sub_rect: None,
         };
 
         let prim_gpu = ImagePrimitiveGpu {
             stretch_size: rect.size,
             tile_spacing: LayerSize::zero(),
         };
 
-        self.add_primitive(&rect,
+        self.add_primitive(scroll_layer_id,
+                           &rect,
                            clip_region,
                            PrimitiveContainer::Image(prim_cpu, prim_gpu));
     }
 
     pub fn add_image(&mut self,
+                     scroll_layer_id: ScrollLayerId,
                      rect: LayerRect,
                      clip_region: &ClipRegion,
                      stretch_size: &LayerSize,
                      tile_spacing: &LayerSize,
                      sub_rect: Option<TexelRect>,
                      image_key: ImageKey,
                      image_rendering: ImageRendering,
                      tile: Option<TileOffset>) {
@@ -851,50 +1010,50 @@ impl FrameBuilder {
             sub_rect: sub_rect,
         };
 
         let prim_gpu = ImagePrimitiveGpu {
             stretch_size: *stretch_size,
             tile_spacing: *tile_spacing,
         };
 
-        self.add_primitive(&rect,
+        self.add_primitive(scroll_layer_id,
+                           &rect,
                            clip_region,
                            PrimitiveContainer::Image(prim_cpu, prim_gpu));
     }
 
     pub fn add_yuv_image(&mut self,
+                         scroll_layer_id: ScrollLayerId,
                          rect: LayerRect,
                          clip_region: &ClipRegion,
                          y_image_key: ImageKey,
                          u_image_key: ImageKey,
                          v_image_key: ImageKey,
                          color_space: YuvColorSpace) {
 
         let prim_cpu = YuvImagePrimitiveCpu {
-            y_key: y_image_key,
-            u_key: u_image_key,
-            v_key: v_image_key,
-            y_texture_id: SourceTexture::Invalid,
-            u_texture_id: SourceTexture::Invalid,
-            v_texture_id: SourceTexture::Invalid,
+            yuv_key: [y_image_key, u_image_key, v_image_key],
+            yuv_texture_id: [SourceTexture::Invalid, SourceTexture::Invalid, SourceTexture::Invalid],
+            yuv_resource_address: GpuStoreAddress(0),
         };
 
         let prim_gpu = YuvImagePrimitiveGpu::new(rect.size, color_space);
 
-        self.add_primitive(&rect,
+        self.add_primitive(scroll_layer_id,
+                           &rect,
                            clip_region,
                            PrimitiveContainer::YuvImage(prim_cpu, prim_gpu));
     }
 
     /// Compute the contribution (bounding rectangles, and resources) of layers and their
     /// primitives in screen space.
     fn build_layer_screen_rects_and_cull_layers(&mut self,
                                                 screen_rect: &DeviceIntRect,
-                                                clip_scroll_tree: &ClipScrollTree,
+                                                clip_scroll_tree: &mut ClipScrollTree,
                                                 auxiliary_lists_map: &AuxiliaryListsMap,
                                                 resource_cache: &mut ResourceCache,
                                                 profile_counters: &mut FrameProfileCounters,
                                                 device_pixel_ratio: f32) {
         profile_scope!("cull");
         LayerRectCalculationAndCullingPass::create_and_run(self,
                                                            screen_rect,
                                                            clip_scroll_tree,
@@ -953,105 +1112,118 @@ impl FrameBuilder {
         profile_scope!("build_render_task");
 
         let mut next_z = 0;
         let mut next_task_index = RenderTaskIndex(0);
 
         let mut sc_stack = Vec::new();
         let mut current_task = RenderTask::new_alpha_batch(next_task_index,
                                                            DeviceIntPoint::zero(),
+                                                           false,
                                                            RenderTaskLocation::Fixed);
         next_task_index.0 += 1;
         let mut alpha_task_stack = Vec::new();
 
         for cmd in &self.cmds {
             match *cmd {
                 PrimitiveRunCmd::PushStackingContext(stacking_context_index) => {
                     let stacking_context = &self.stacking_context_store[stacking_context_index.0];
                     sc_stack.push(stacking_context_index);
 
                     if !stacking_context.is_visible {
                         continue;
                     }
 
                     let stacking_context_rect = &stacking_context.bounding_rect;
                     let composite_count = stacking_context.composite_ops.count();
+
+                    if composite_count == 0 && stacking_context.should_isolate {
+                        let location = RenderTaskLocation::Dynamic(None, stacking_context_rect.size);
+                        let new_task = RenderTask::new_alpha_batch(next_task_index,
+                                                                   stacking_context_rect.origin,
+                                                                   stacking_context.should_isolate,
+                                                                   location);
+                        next_task_index.0 += 1;
+                        let prev_task = mem::replace(&mut current_task, new_task);
+                        alpha_task_stack.push(prev_task);
+                    }
+
                     for _ in 0..composite_count {
                         let location = RenderTaskLocation::Dynamic(None, stacking_context_rect.size);
                         let new_task = RenderTask::new_alpha_batch(next_task_index,
                                                                    stacking_context_rect.origin,
+                                                                   stacking_context.should_isolate,
                                                                    location);
                         next_task_index.0 += 1;
                         let prev_task = mem::replace(&mut current_task, new_task);
                         alpha_task_stack.push(prev_task);
                     }
                 }
                 PrimitiveRunCmd::PopStackingContext => {
                     let stacking_context_index = sc_stack.pop().unwrap();
                     let stacking_context = &self.stacking_context_store[stacking_context_index.0];
 
                     if !stacking_context.is_visible {
                         continue;
                     }
 
+                    let composite_count = stacking_context.composite_ops.count();
+
+                    if composite_count == 0 && stacking_context.should_isolate {
+                        let mut prev_task = alpha_task_stack.pop().unwrap();
+                        let item = AlphaRenderItem::HardwareComposite(stacking_context_index,
+                                                                      current_task.id,
+                                                                      HardwareCompositeOp::Alpha,
+                                                                      next_z);
+                        next_z += 1;
+                        prev_task.as_alpha_batch().alpha_items.push(item);
+                        prev_task.children.push(current_task);
+                        current_task = prev_task;
+                    }
+
                     for filter in &stacking_context.composite_ops.filters {
                         let mut prev_task = alpha_task_stack.pop().unwrap();
                         let item = AlphaRenderItem::Blend(stacking_context_index,
                                                           current_task.id,
                                                           *filter,
                                                           next_z);
                         next_z += 1;
                         prev_task.as_alpha_batch().alpha_items.push(item);
                         prev_task.children.push(current_task);
                         current_task = prev_task;
                     }
                     if let Some(mix_blend_mode) = stacking_context.composite_ops.mix_blend_mode {
-                        match HardwareCompositeOp::from_mix_blend_mode(mix_blend_mode) {
-                            Some(op) => {
-                                let mut prev_task = alpha_task_stack.pop().unwrap();
-                                let item = AlphaRenderItem::HardwareComposite(stacking_context_index,
-                                                                              current_task.id,
-                                                                              op,
-                                                                              next_z);
-                                next_z += 1;
-                                prev_task.as_alpha_batch().alpha_items.push(item);
-                                prev_task.children.push(current_task);
-                                current_task = prev_task;
-                            }
-                            None => {
-                                let readback_task =
-                                    RenderTask::new_readback(stacking_context_index,
-                                                             stacking_context.bounding_rect);
+                        let readback_task =
+                            RenderTask::new_readback(stacking_context_index,
+                                                     stacking_context.bounding_rect);
 
-                                let mut prev_task = alpha_task_stack.pop().unwrap();
-                                let item = AlphaRenderItem::Composite(stacking_context_index,
-                                                                      readback_task.id,
-                                                                      current_task.id,
-                                                                      mix_blend_mode,
-                                                                      next_z);
-                                next_z += 1;
-                                prev_task.as_alpha_batch().alpha_items.push(item);
-                                prev_task.children.push(current_task);
-                                prev_task.children.push(readback_task);
-                                current_task = prev_task;
-                            }
-                        }
+                        let mut prev_task = alpha_task_stack.pop().unwrap();
+                        let item = AlphaRenderItem::Composite(stacking_context_index,
+                                                              readback_task.id,
+                                                              current_task.id,
+                                                              mix_blend_mode,
+                                                              next_z);
+                        next_z += 1;
+                        prev_task.as_alpha_batch().alpha_items.push(item);
+                        prev_task.children.push(current_task);
+                        prev_task.children.push(readback_task);
+                        current_task = prev_task;
                     }
                 }
-                PrimitiveRunCmd::PrimitiveRun(first_prim_index, prim_count) => {
+                PrimitiveRunCmd::PrimitiveRun(first_prim_index, prim_count, scroll_layer_id) => {
                     let stacking_context_index = *sc_stack.last().unwrap();
                     let stacking_context = &self.stacking_context_store[stacking_context_index.0];
 
                     if !stacking_context.is_visible {
                         continue;
                     }
 
                     let stacking_context_index = *sc_stack.last().unwrap();
-                    let group_index =
-                        self.stacking_context_store[stacking_context_index.0].clip_scroll_group();
+                    let group_index = self.stacking_context_store[stacking_context_index.0]
+                                          .clip_scroll_group(scroll_layer_id);
                     let clip_scroll_group = &self.clip_scroll_group_store[group_index.0];
 
                     for i in 0..prim_count {
                         let prim_index = PrimitiveIndex(first_prim_index.0 + i);
 
                         if self.prim_store.cpu_bounding_rects[prim_index.0].is_some() {
                             let prim_metadata = self.prim_store.get_metadata(prim_index);
 
@@ -1074,45 +1246,42 @@ impl FrameBuilder {
                             } else {
                                 &mut current_task.as_alpha_batch().opaque_items
                             };
                             items.push(AlphaRenderItem::Primitive(group_index, prim_index, next_z));
                             next_z += 1;
                         }
                     }
                 }
-                PrimitiveRunCmd::PushScrollLayer(_) | PrimitiveRunCmd::PopScrollLayer => { }
             }
         }
 
         debug_assert!(alpha_task_stack.is_empty());
         (current_task, next_task_index.0)
     }
 
     pub fn build(&mut self,
                  resource_cache: &mut ResourceCache,
                  frame_id: FrameId,
-                 clip_scroll_tree: &ClipScrollTree,
+                 clip_scroll_tree: &mut ClipScrollTree,
                  auxiliary_lists_map: &AuxiliaryListsMap,
                  device_pixel_ratio: f32,
                  texture_cache_profile: &mut TextureCacheProfileCounters)
                  -> Frame {
         profile_scope!("build");
 
         let mut profile_counters = FrameProfileCounters::new();
         profile_counters.total_primitives.set(self.prim_store.prim_count());
 
         resource_cache.begin_frame(frame_id);
 
         let screen_rect = DeviceIntRect::new(
             DeviceIntPoint::zero(),
-            DeviceIntSize::from_lengths(device_length(self.screen_rect.size.width as f32,
-                                                      device_pixel_ratio),
-                                        device_length(self.screen_rect.size.height as f32,
-                                                      device_pixel_ratio)));
+            DeviceIntSize::new(self.screen_size.width as i32,
+                               self.screen_size.height as i32));
 
         // Pick a size for the cache render targets to be. The main requirement is that it
         // has to be at least as large as the framebuffer size. This ensures that it will
         // always be able to allocate the worst case render task (such as a clip mask that
         // covers the entire screen).
         let cache_size = DeviceUintSize::new(cmp::max(1024, screen_rect.size.width as u32),
                                              cmp::max(1024, screen_rect.size.height as u32));
 
@@ -1128,19 +1297,21 @@ impl FrameBuilder {
         let (main_render_task, static_render_task_count) = self.build_render_task();
         let mut render_tasks = RenderTaskCollection::new(static_render_task_count);
 
         let mut required_pass_count = 0;
         main_render_task.max_depth(0, &mut required_pass_count);
 
         resource_cache.block_until_all_resources_added(texture_cache_profile);
 
-        for scroll_layer in self.scroll_layer_store.iter() {
-            if let Some(ref clip_info) = scroll_layer.clip_cache_info {
-                self.prim_store.resolve_clip_cache(clip_info, resource_cache);
+        for node in clip_scroll_tree.nodes.values() {
+            if let NodeType::Clip(ref clip_info) = node.node_type {
+                if let Some(ref mask_info) = clip_info.mask_cache_info {
+                    self.prim_store.resolve_clip_cache(mask_info, resource_cache);
+                }
             }
         }
 
         let deferred_resolves = self.prim_store.resolve_primitives(resource_cache,
                                                                    device_pixel_ratio);
 
         let mut passes = Vec::new();
 
@@ -1168,17 +1339,17 @@ impl FrameBuilder {
             profile_counters.targets.add(pass.targets.len());
         }
 
         resource_cache.end_frame();
 
         Frame {
             device_pixel_ratio: device_pixel_ratio,
             background_color: self.background_color,
-            viewport_size: self.screen_rect.size,
+            window_size: self.screen_size,
             profile_counters: profile_counters,
             passes: passes,
             cache_size: cache_size,
             layer_texture_data: self.packed_layers.clone(),
             render_task_data: render_tasks.render_task_data,
             gpu_data16: self.prim_store.gpu_data16.build(),
             gpu_data32: self.prim_store.gpu_data32.build(),
             gpu_data64: self.prim_store.gpu_data64.build(),
@@ -1190,259 +1361,268 @@ impl FrameBuilder {
         }
     }
 
 }
 
 struct LayerRectCalculationAndCullingPass<'a> {
     frame_builder: &'a mut FrameBuilder,
     screen_rect: &'a DeviceIntRect,
-    clip_scroll_tree: &'a ClipScrollTree,
+    clip_scroll_tree: &'a mut ClipScrollTree,
     auxiliary_lists_map: &'a AuxiliaryListsMap,
     resource_cache: &'a mut ResourceCache,
     profile_counters: &'a mut FrameProfileCounters,
     device_pixel_ratio: f32,
     stacking_context_stack: Vec<StackingContextIndex>,
-    scroll_layer_stack: Vec<ScrollLayerIndex>,
 
     /// A cached clip info stack, which should handle the most common situation,
     /// which is that we are using the same clip info stack that we were using
     /// previously.
     current_clip_stack: Vec<(PackedLayerIndex, MaskCacheInfo)>,
 
-    /// The scroll layer that defines the previous scroll layer info stack.
-    current_clip_stack_scroll_layer: Option<ScrollLayerIndex>
+    /// Information about the cached clip stack, which is used to avoid having
+    /// to recalculate it for every primitive.
+    current_clip_info: Option<(ScrollLayerId, DeviceIntRect)>
 }
 
 impl<'a> LayerRectCalculationAndCullingPass<'a> {
     fn create_and_run(frame_builder: &'a mut FrameBuilder,
                       screen_rect: &'a DeviceIntRect,
-                      clip_scroll_tree: &'a ClipScrollTree,
+                      clip_scroll_tree: &'a mut ClipScrollTree,
                       auxiliary_lists_map: &'a AuxiliaryListsMap,
                       resource_cache: &'a mut ResourceCache,
                       profile_counters: &'a mut FrameProfileCounters,
                       device_pixel_ratio: f32) {
 
         let mut pass = LayerRectCalculationAndCullingPass {
             frame_builder: frame_builder,
             screen_rect: screen_rect,
             clip_scroll_tree: clip_scroll_tree,
             auxiliary_lists_map: auxiliary_lists_map,
             resource_cache: resource_cache,
             profile_counters: profile_counters,
             device_pixel_ratio: device_pixel_ratio,
             stacking_context_stack: Vec::new(),
-            scroll_layer_stack: Vec::new(),
             current_clip_stack: Vec::new(),
-            current_clip_stack_scroll_layer: None,
+            current_clip_info: None,
         };
         pass.run();
     }
 
     fn run(&mut self) {
         self.recalculate_clip_scroll_groups();
+        self.recalculate_clip_scroll_nodes();
         self.compute_stacking_context_visibility();
 
         let commands = mem::replace(&mut self.frame_builder.cmds, Vec::new());
         for cmd in &commands {
             match cmd {
                 &PrimitiveRunCmd::PushStackingContext(stacking_context_index) =>
                     self.handle_push_stacking_context(stacking_context_index),
-                &PrimitiveRunCmd::PushScrollLayer(scroll_layer_index) =>
-                    self.handle_push_scroll_layer(scroll_layer_index),
-                &PrimitiveRunCmd::PrimitiveRun(prim_index, prim_count) =>
-                    self.handle_primitive_run(prim_index, prim_count),
+                &PrimitiveRunCmd::PrimitiveRun(prim_index, prim_count, scroll_layer_id) =>
+                    self.handle_primitive_run(prim_index, prim_count, scroll_layer_id),
                 &PrimitiveRunCmd::PopStackingContext => self.handle_pop_stacking_context(),
-                &PrimitiveRunCmd::PopScrollLayer => self.handle_pop_scroll_layer(),
             }
         }
 
         mem::replace(&mut self.frame_builder.cmds, commands);
     }
 
+    fn recalculate_clip_scroll_nodes(&mut self) {
+        for (_, ref mut node) in self.clip_scroll_tree.nodes.iter_mut() {
+            let node_clip_info = match node.node_type {
+                NodeType::Clip(ref mut clip_info) => clip_info,
+                NodeType::ReferenceFrame(_) => continue,
+            };
+
+            let packed_layer_index = node_clip_info.packed_layer_index;
+            let packed_layer = &mut self.frame_builder.packed_layers[packed_layer_index.0];
+
+            // The coordinates of the mask are relative to the origin of the node itself,
+            // so we need to account for that origin in the transformation we assign to
+            // the packed layer.
+            let transform = node.world_viewport_transform
+                                .pre_translated(node.local_viewport_rect.origin.x,
+                                                node.local_viewport_rect.origin.y,
+                                                0.0);
+            packed_layer.set_transform(transform);
+
+            // Meanwhile, the combined viewport rect is relative to the reference frame, so
+            // we move it into the local coordinate system of the node.
+            let local_viewport_rect =
+                node.combined_local_viewport_rect.translate(&-node.local_viewport_rect.origin);
+
+            node_clip_info.xf_rect = packed_layer.set_rect(Some(local_viewport_rect),
+                                                           self.screen_rect,
+                                                           self.device_pixel_ratio);
+
+            let mask_info = match node_clip_info.mask_cache_info {
+                Some(ref mut mask_info) => mask_info,
+                _ => continue,
+            };
+
+            let auxiliary_lists = self.auxiliary_lists_map.get(&node.pipeline_id)
+                                                          .expect("No auxiliary lists?");
+
+            mask_info.update(&node_clip_info.clip_source,
+                             &packed_layer.transform,
+                             &mut self.frame_builder.prim_store.gpu_data32,
+                             self.device_pixel_ratio,
+                             auxiliary_lists);
+
+            if let Some(mask) = node_clip_info.clip_source.image_mask() {
+                // We don't add the image mask for resolution, because
+                // layer masks are resolved later.
+                self.resource_cache.request_image(mask.image, ImageRendering::Auto, None);
+            }
+        }
+    }
+
     fn recalculate_clip_scroll_groups(&mut self) {
         for ref mut group in &mut self.frame_builder.clip_scroll_group_store {
             let stacking_context_index = group.stacking_context_index;
             let stacking_context = &mut self.frame_builder
                                             .stacking_context_store[stacking_context_index.0];
 
-            let scroll_tree_layer = &self.clip_scroll_tree.nodes[&group.scroll_layer_id];
+            let node = &self.clip_scroll_tree.nodes[&group.scroll_layer_id];
             let packed_layer = &mut self.frame_builder.packed_layers[group.packed_layer_index.0];
-            packed_layer.transform = scroll_tree_layer.world_content_transform
-                                                      .with_source::<ScrollLayerPixel>()
-                                                      .pre_mul(&stacking_context.local_transform);
-            packed_layer.inv_transform = packed_layer.transform.inverse().unwrap();
+
+            // The world content transform is relative to the containing reference frame,
+            // so we translate into the origin of the stacking context itself.
+            let transform = node.world_content_transform
+                                .pre_translated(stacking_context.reference_frame_offset.x,
+                                                stacking_context.reference_frame_offset.y,
+                                                0.0);
+            packed_layer.set_transform(transform);
 
             if !stacking_context.can_contribute_to_scene() {
                 return;
             }
 
-            let inv_layer_transform = stacking_context.local_transform.inverse().unwrap();
-            let local_viewport_rect =
-                as_scroll_parent_rect(&scroll_tree_layer.combined_local_viewport_rect);
-            let viewport_rect = inv_layer_transform.transform_rect(&local_viewport_rect);
-            let layer_local_rect = stacking_context.local_rect.intersection(&viewport_rect);
-
-            group.xf_rect = None;
+            // Here we want to find the intersection between the clipping region and the
+            // stacking context content, so we move the viewport rectangle into the coordinate
+            // system of the stacking context content.
+            let viewport_rect =
+                &node.combined_local_viewport_rect
+                     .translate(&-stacking_context.reference_frame_offset)
+                     .translate(&-node.scrolling.offset);
+            let intersected_rect = stacking_context.local_rect.intersection(viewport_rect);
 
-            let layer_local_rect = match layer_local_rect {
-                Some(layer_local_rect) if !layer_local_rect.is_empty() => layer_local_rect,
-                _ => continue,
-            };
-
-            let layer_xf_rect = TransformedRect::new(&layer_local_rect,
-                                                     &packed_layer.transform,
-                                                     self.device_pixel_ratio);
-
-            if layer_xf_rect.bounding_rect.intersects(&self.screen_rect) {
-                packed_layer.screen_vertices = layer_xf_rect.vertices.clone();
-                packed_layer.local_clip_rect = layer_local_rect;
-                group.xf_rect = Some(layer_xf_rect);
-            }
+            group.xf_rect = packed_layer.set_rect(intersected_rect,
+                                                  self.screen_rect,
+                                                  self.device_pixel_ratio);
         }
     }
 
     fn compute_stacking_context_visibility(&mut self) {
         for context_index in 0..self.frame_builder.stacking_context_store.len() {
             let is_visible = {
+                // We don't take into account visibility of children here, so we must
+                // do that later.
                 let stacking_context = &self.frame_builder.stacking_context_store[context_index];
                 stacking_context.clip_scroll_groups.iter().any(|group_index| {
                     self.frame_builder.clip_scroll_group_store[group_index.0].is_visible()
                 })
             };
             self.frame_builder.stacking_context_store[context_index].is_visible = is_visible;
         }
     }
 
     fn handle_pop_stacking_context(&mut self) {
         let stacking_context_index = self.stacking_context_stack.pop().unwrap();
 
-        let bounding_rect = {
+        let (bounding_rect, is_visible) = {
             let stacking_context =
                 &mut self.frame_builder.stacking_context_store[stacking_context_index.0];
             stacking_context.bounding_rect = stacking_context.bounding_rect
                                                              .intersection(self.screen_rect)
                                                              .unwrap_or(DeviceIntRect::zero());
-            stacking_context.bounding_rect.clone()
+            (stacking_context.bounding_rect.clone(), stacking_context.is_visible)
         };
 
         if let Some(ref mut parent_index) = self.stacking_context_stack.last_mut() {
             let parent = &mut self.frame_builder.stacking_context_store[parent_index.0];
             parent.bounding_rect = parent.bounding_rect.union(&bounding_rect);
-        }
-    }
 
-    fn handle_push_scroll_layer(&mut self, scroll_layer_index: ScrollLayerIndex) {
-        self.scroll_layer_stack.push(scroll_layer_index);
-
-        let scroll_layer = &mut self.frame_builder.scroll_layer_store[scroll_layer_index.0];
-        let packed_layer_index = scroll_layer.packed_layer_index;
-        let scroll_tree_layer = &self.clip_scroll_tree.nodes[&scroll_layer.scroll_layer_id];
-        let packed_layer = &mut self.frame_builder.packed_layers[packed_layer_index.0];
-
-        packed_layer.transform = scroll_tree_layer.world_viewport_transform;
-        packed_layer.inv_transform = packed_layer.transform.inverse().unwrap();
-
-        let local_rect = &scroll_tree_layer.combined_local_viewport_rect
-                                           .translate(&scroll_tree_layer.scrolling.offset);
-        if !local_rect.is_empty() {
-            let layer_xf_rect = TransformedRect::new(local_rect,
-                                                     &packed_layer.transform,
-                                                     self.device_pixel_ratio);
-
-            if layer_xf_rect.bounding_rect.intersects(&self.screen_rect) {
-                packed_layer.screen_vertices = layer_xf_rect.vertices.clone();
-                packed_layer.local_clip_rect = *local_rect;
-                scroll_layer.xf_rect = Some(layer_xf_rect);
-            }
-        }
-
-        let clip_info = match scroll_layer.clip_cache_info {
-            Some(ref mut clip_info) => clip_info,
-            None => return,
-        };
-
-        let pipeline_id = scroll_layer.scroll_layer_id.pipeline_id;
-        let auxiliary_lists = self.auxiliary_lists_map.get(&pipeline_id)
-                                                       .expect("No auxiliary lists?");
-        clip_info.update(&scroll_layer.clip_source,
-                         &packed_layer.transform,
-                         &mut self.frame_builder.prim_store.gpu_data32,
-                         self.device_pixel_ratio,
-                         auxiliary_lists);
-
-        if let Some(mask) = scroll_layer.clip_source.image_mask() {
-            // We don't add the image mask for resolution, because layer masks are resolved later.
-            self.resource_cache.request_image(mask.image, ImageRendering::Auto, None);
+            // The previous compute_stacking_context_visibility pass did not take into
+            // account visibility of children, so we do that now.
+            parent.is_visible = parent.is_visible || is_visible;
         }
     }
 
     fn handle_push_stacking_context(&mut self, stacking_context_index: StackingContextIndex) {
         self.stacking_context_stack.push(stacking_context_index);
 
         // Reset bounding rect to zero. We will calculate it as we collect primitives
         // from various scroll layers. In handle_pop_stacking_context , we use this to
         // calculate the device bounding rect. In the future, we could cache this during
         // the initial adding of items for the common case (where there is only a single
         // scroll layer for items in a stacking context).
         let stacking_context = &mut self.frame_builder
                                         .stacking_context_store[stacking_context_index.0];
         stacking_context.bounding_rect = DeviceIntRect::zero();
     }
 
-    fn rebuild_clip_info_stack_if_necessary(&mut self, mut scroll_layer_index: ScrollLayerIndex) {
-        if let Some(previous_scroll_layer) = self.current_clip_stack_scroll_layer {
-            if previous_scroll_layer == scroll_layer_index {
-                return;
+    fn rebuild_clip_info_stack_if_necessary(&mut self, id: ScrollLayerId) -> DeviceIntRect {
+        if let Some((current_scroll_id, bounding_rect)) = self.current_clip_info {
+            if current_scroll_id == id {
+                return bounding_rect;
             }
         }
 
         // TODO(mrobinson): If we notice that this process is expensive, we can special-case
         // more common situations, such as moving from a child or a parent.
-        self.current_clip_stack_scroll_layer = Some(scroll_layer_index);
         self.current_clip_stack.clear();
-        loop {
-            let scroll_layer = &self.frame_builder.scroll_layer_store[scroll_layer_index.0];
-            match scroll_layer.clip_cache_info {
-                Some(ref clip_info) if clip_info.is_masking() =>
-                    self.current_clip_stack.push((scroll_layer.packed_layer_index,
-                                                  clip_info.clone())),
-                _ => {},
+        let mut bounding_rect = None;
+
+        let mut current_id = Some(id);
+        while let Some(id) = current_id {
+            let node = &self.clip_scroll_tree.nodes.get(&id).unwrap();
+            current_id = node.parent;
+
+            let clip_info = match node.node_type {
+                NodeType::Clip(ref clip) if clip.is_masking() => clip,
+                _ => continue,
             };
 
-            if scroll_layer.parent_index == scroll_layer_index {
-                break;
+            if bounding_rect.is_none() {
+                bounding_rect = Some(clip_info.xf_rect.as_ref().unwrap().bounding_rect);
             }
-            scroll_layer_index = scroll_layer.parent_index;
+            self.current_clip_stack.push((clip_info.packed_layer_index,
+                                          clip_info.mask_cache_info.clone().unwrap()))
         }
+        self.current_clip_stack.reverse();
 
-        self.current_clip_stack.reverse();
+        let bounding_rect = bounding_rect.unwrap_or_else(DeviceIntRect::zero);
+        self.current_clip_info = Some((id, bounding_rect));
+        bounding_rect
     }
 
-    fn handle_primitive_run(&mut self, prim_index: PrimitiveIndex, prim_count: usize) {
+    fn handle_primitive_run(&mut self,
+                            prim_index: PrimitiveIndex,
+                            prim_count: usize,
+                            scroll_layer_id: ScrollLayerId) {
         let stacking_context_index = *self.stacking_context_stack.last().unwrap();
         let (packed_layer_index, pipeline_id) = {
             let stacking_context =
                 &self.frame_builder.stacking_context_store[stacking_context_index.0];
 
             if !stacking_context.is_visible {
                 return;
             }
 
-            let group_index = stacking_context.clip_scroll_group();
+            let group_index = stacking_context.clip_scroll_group(scroll_layer_id);
             let clip_scroll_group = &self.frame_builder.clip_scroll_group_store[group_index.0];
             (clip_scroll_group.packed_layer_index, stacking_context.pipeline_id)
         };
 
-        let scroll_layer_index = *self.scroll_layer_stack.last().unwrap();
-        self.rebuild_clip_info_stack_if_necessary(scroll_layer_index);
+        let node_clip_bounds = self.rebuild_clip_info_stack_if_necessary(scroll_layer_id);
 
         let stacking_context =
             &mut self.frame_builder.stacking_context_store[stacking_context_index.0];
-
         let packed_layer = &self.frame_builder.packed_layers[packed_layer_index.0];
         let auxiliary_lists = self.auxiliary_lists_map.get(&pipeline_id)
                                                       .expect("No auxiliary lists?");
 
         for i in 0..prim_count {
             let prim_index = PrimitiveIndex(prim_index.0 + i);
             if self.frame_builder.prim_store.build_bounding_rect(prim_index,
                                                                  self.screen_rect,
@@ -1483,22 +1663,17 @@ impl<'a> LayerRectCalculationAndCullingP
                 // Try to create a mask if we may need to.
                 if !self.current_clip_stack.is_empty() {
                     // If the primitive doesn't have a specific clip, key the task ID off the
                     // stacking context. This means that two primitives which are only clipped
                     // by the stacking context stack can share clip masks during render task
                     // assignment to targets.
                     let (mask_key, mask_rect) = match prim_clip_info {
                         Some(..) => (MaskCacheKey::Primitive(prim_index), prim_bounding_rect),
-                        None => {
-                            let scroll_layer =
-                                &self.frame_builder.scroll_layer_store[scroll_layer_index.0];
-                            (MaskCacheKey::ScrollLayer(scroll_layer_index),
-                             scroll_layer.xf_rect.as_ref().unwrap().bounding_rect)
-                        }
+                        None => (MaskCacheKey::ScrollLayer(scroll_layer_id), node_clip_bounds)
                     };
                     let mask_opt =
                         RenderTask::new_mask(mask_rect, mask_key, &self.current_clip_stack);
                     match mask_opt {
                         MaskResult::Outside => { // Primitive is completely clipped out.
                             prim_metadata.clip_task = None;
                             self.frame_builder.prim_store.cpu_bounding_rects[prim_index.0] = None;
                             visible = false;
@@ -1512,13 +1687,9 @@ impl<'a> LayerRectCalculationAndCullingP
                 }
 
                 if visible {
                     self.profile_counters.visible_primitives.inc();
                 }
             }
         }
     }
-
-    fn handle_pop_scroll_layer(&mut self) {
-        self.scroll_layer_stack.pop();
-    }
 }
--- a/gfx/webrender/src/gpu_store.rs
+++ b/gfx/webrender/src/gpu_store.rs
@@ -1,20 +1,30 @@
 /* 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/. */
 
 use device::TextureFilter;
 use std::marker::PhantomData;
 use std::mem;
+use std::ops::Add;
 use webrender_traits::ImageFormat;
 
 #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
 pub struct GpuStoreAddress(pub i32);
 
+
+impl Add<i32> for GpuStoreAddress {
+    type Output = GpuStoreAddress;
+
+    fn add(self, other: i32) -> GpuStoreAddress {
+        GpuStoreAddress(self.0 + other)
+    }
+}
+
 pub trait GpuStoreLayout {
     fn image_format() -> ImageFormat;
 
     fn texture_width<T>() -> usize;
 
     fn texture_filter() -> TextureFilter;
 
     fn texel_size() -> usize {
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -15,17 +15,17 @@ use std::collections::{HashMap, HashSet}
 use std::f32;
 use std::hash::BuildHasherDefault;
 use std::{i32, usize};
 use std::path::PathBuf;
 use std::sync::Arc;
 use tiling;
 use renderer::BlendMode;
 use webrender_traits::{Epoch, ColorF, PipelineId, DeviceIntSize};
-use webrender_traits::{ImageFormat, NativeFontHandle, MixBlendMode};
+use webrender_traits::{ImageFormat, NativeFontHandle};
 use webrender_traits::{ExternalImageId, ScrollLayerId, WebGLCommand};
 use webrender_traits::{ImageData};
 use webrender_traits::{DeviceUintRect};
 
 // An ID for a texture that is owned by the
 // texture cache module. This can include atlases
 // or standalone textures allocated via the
 // texture cache (e.g. if an image is too large
@@ -487,31 +487,18 @@ pub enum LowLevelFilterOp {
     Invert(Au),
     Opacity(Au),
     Saturate(Au),
     Sepia(Au),
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub enum HardwareCompositeOp {
-    Multiply,
-    Max,
-    Min,
+    Alpha,
 }
 
 impl HardwareCompositeOp {
-    pub fn from_mix_blend_mode(mix_blend_mode: MixBlendMode) -> Option<HardwareCompositeOp> {
-        match mix_blend_mode {
-            MixBlendMode::Multiply => Some(HardwareCompositeOp::Multiply),
-            MixBlendMode::Lighten => Some(HardwareCompositeOp::Max),
-            MixBlendMode::Darken => Some(HardwareCompositeOp::Min),
-            _ => None,
-        }
-    }
-
     pub fn to_blend_mode(&self) -> BlendMode {
         match self {
-            &HardwareCompositeOp::Multiply => BlendMode::Multiply,
-            &HardwareCompositeOp::Max => BlendMode::Max,
-            &HardwareCompositeOp::Min => BlendMode::Min,
+            &HardwareCompositeOp::Alpha => BlendMode::Alpha,
         }
     }
 }
--- a/gfx/webrender/src/platform/macos/font.rs
+++ b/gfx/webrender/src/platform/macos/font.rs
@@ -142,16 +142,30 @@ impl FontContext {
     pub fn add_native_font(&mut self, font_key: &FontKey, native_font_handle: CGFont) {
         if self.cg_fonts.contains_key(font_key) {
             return
         }
 
         self.cg_fonts.insert((*font_key).clone(), native_font_handle);
     }
 
+    pub fn delete_font(&mut self, font_key: &FontKey) {
+        if let Some(cg_font) = self.cg_fonts.remove(font_key) {
+            // Unstable Rust has a retain() method on HashMap that will
+            // let us do this in-place. https://github.com/rust-lang/rust/issues/36648
+            let ct_font_keys = self.ct_fonts.keys()
+                                            .filter(|k| k.0 == *font_key)
+                                            .cloned()
+                                            .collect::<Vec<_>>();
+            for ct_font_key in ct_font_keys {
+                self.ct_fonts.remove(&ct_font_key);
+            }
+        }
+    }
+
     fn get_ct_font(&mut self,
                    font_key: FontKey,
                    size: Au) -> Option<CTFont> {
         match self.ct_fonts.entry(((font_key).clone(), size)) {
             Entry::Occupied(entry) => Some((*entry.get()).clone()),
             Entry::Vacant(entry) => {
                 let cg_font = match self.cg_fonts.get(&font_key) {
                     None => return None,
--- a/gfx/webrender/src/platform/unix/font.rs
+++ b/gfx/webrender/src/platform/unix/font.rs
@@ -8,16 +8,17 @@ use webrender_traits::{NativeFontHandle,
 use webrender_traits::{GlyphKey};
 
 use freetype::freetype::{FT_Render_Mode, FT_Pixel_Mode};
 use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter};
 use freetype::freetype::{FT_Library, FT_Set_Char_Size};
 use freetype::freetype::{FT_Face, FT_Long, FT_UInt, FT_F26Dot6};
 use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph};
 use freetype::freetype::{FT_New_Memory_Face, FT_GlyphSlot, FT_LcdFilter};
+use freetype::freetype::{FT_Done_Face};
 
 use std::{mem, ptr, slice};
 use std::collections::HashMap;
 
 struct Face {
     face: FT_Face,
 }
 
@@ -83,16 +84,25 @@ impl FontContext {
             }
         }
     }
 
     pub fn add_native_font(&mut self, _font_key: &FontKey, _native_font_handle: NativeFontHandle) {
         panic!("TODO: Not supported on Linux");
     }
 
+    pub fn delete_font(&mut self, font_key: &FontKey) {
+        if let Some(face) = self.faces.remove(font_key) {
+            let result = unsafe {
+                FT_Done_Face(face.face)
+            };
+            assert!(result.succeeded());
+        }
+    }
+
     fn load_glyph(&self,
                   font_key: FontKey,
                   size: Au,
                   character: u32) -> Option<FT_GlyphSlot> {
         debug_assert!(self.faces.contains_key(&font_key));
         let face = self.faces.get(&font_key).unwrap();
 
         unsafe {
--- a/gfx/webrender/src/platform/windows/font.rs
+++ b/gfx/webrender/src/platform/windows/font.rs
@@ -138,16 +138,20 @@ impl FontContext {
         }
 
         let system_fc = dwrote::FontCollection::system();
         let font = system_fc.get_font_from_descriptor(&font_handle).unwrap();
         let face = font.create_font_face();
         self.fonts.insert((*font_key).clone(), face);
     }
 
+    pub fn delete_font(&mut self, font_key: &FontKey) {
+        self.fonts.remove(font_key);
+    }
+
     // Assumes RGB format from dwrite, which is 3 bytes per pixel as dwrite
     // doesn't output an alpha value via GlyphRunAnalysis::CreateAlphaTexture
     #[allow(dead_code)]
     fn print_glyph_data(&self, data: &Vec<u8>, width: usize, height: usize) {
         // Rust doesn't have step_by support on stable :(
         for i in 0..height {
             let current_height = i * width * 3;
 
@@ -225,23 +229,18 @@ impl FontContext {
                     rgba_pixels[i*4+3] = pixels[i];
                 }
                 rgba_pixels
             }
             FontRenderMode::Alpha => {
                 let length = pixels.len() / 3;
                 let mut rgba_pixels: Vec<u8> = vec![0; length * 4];
                 for i in 0..length {
-                    // TODO(vlad): we likely need to do something smarter
-                    // This is what skia does
-                    let alpha = ((pixels[i*3+0] as u32 +
-                                pixels[i*3+1] as u32 +
-                                pixels[i*3+2] as u32)
-                                / 3) as u8;
-
+                    // Only take the G channel, as its closest to D2D
+                    let alpha = pixels[i*3 + 1] as u8;
                     rgba_pixels[i*4+0] = alpha;
                     rgba_pixels[i*4+1] = alpha;
                     rgba_pixels[i*4+2] = alpha;
                     rgba_pixels[i*4+3] = alpha;
                 }
                 rgba_pixels
             }
             FontRenderMode::Subpixel => {
@@ -273,32 +272,34 @@ impl FontContext {
         let height = (bounds.bottom - bounds.top) as usize;
 
         // We should not get here since glyph_dimensions would return
         // None for empty glyphs.
         assert!(width > 0 && height > 0);
 
         let mut pixels = analysis.create_alpha_texture(texture_type, bounds);
 
-        let lut_correction = match glyph_options {
-            Some(option) => {
-                if option.force_gdi_rendering {
-                    &self.gdi_gamma_lut
-                } else {
-                    &self.gamma_lut
-                }
-            },
-            None => &self.gamma_lut
-        };
+        if render_mode != FontRenderMode::Mono {
+            let lut_correction = match glyph_options {
+                Some(option) => {
+                    if option.force_gdi_rendering {
+                        &self.gdi_gamma_lut
+                    } else {
+                        &self.gamma_lut
+                    }
+                },
+                None => &self.gamma_lut
+            };
 
-        lut_correction.preblend_rgb(&mut pixels, width, height,
-                                    ColorLut::new(key.color.r,
-                                                  key.color.g,
-                                                  key.color.b,
-                                                  key.color.a));
+            lut_correction.preblend_rgb(&mut pixels, width, height,
+                                        ColorLut::new(key.color.r,
+                                                      key.color.g,
+                                                      key.color.b,
+                                                      key.color.a));
+        }
 
         let rgba_pixels = self.convert_to_rgba(&mut pixels, render_mode);
 
         Some(RasterizedGlyph {
             width: width as u32,
             height: height as u32,
             bytes: rgba_pixels,
         })
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -4,17 +4,17 @@
 
 use app_units::Au;
 use euclid::{Point2D, Size2D};
 use gpu_store::GpuStoreAddress;
 use internal_types::{SourceTexture, PackedTexel};
 use mask_cache::{ClipSource, MaskCacheInfo};
 use renderer::{VertexDataStore, GradientDataStore};
 use render_task::{RenderTask, RenderTaskLocation};
-use resource_cache::{ImageProperties, ResourceCache};
+use resource_cache::{CacheItem, ImageProperties, ResourceCache};
 use std::mem;
 use std::usize;
 use util::TransformedRect;
 use webrender_traits::{AuxiliaryLists, ColorF, ImageKey, ImageRendering, YuvColorSpace};
 use webrender_traits::{ClipRegion, ComplexClipRegion, ItemRange, GlyphKey};
 use webrender_traits::{FontKey, FontRenderMode, WebGLContextId};
 use webrender_traits::{device_length, DeviceIntRect, DeviceIntSize};
 use webrender_traits::{DeviceRect, DevicePoint, DeviceSize};
@@ -152,47 +152,35 @@ pub struct ImagePrimitiveCpu {
 #[repr(C)]
 pub struct ImagePrimitiveGpu {
     pub stretch_size: LayerSize,
     pub tile_spacing: LayerSize,
 }
 
 #[derive(Debug)]
 pub struct YuvImagePrimitiveCpu {
-    pub y_key: ImageKey,
-    pub u_key: ImageKey,
-    pub v_key: ImageKey,
-    pub y_texture_id: SourceTexture,
-    pub u_texture_id: SourceTexture,
-    pub v_texture_id: SourceTexture,
+    pub yuv_key: [ImageKey; 3],
+    pub yuv_texture_id: [SourceTexture; 3],
+
+    // The first address of yuv resource_address. Use "yuv_resource_address + N-th" to get the N-th channel data.
+    // e.g. yuv_resource_address + 0 => y channel resource_address
+    pub yuv_resource_address: GpuStoreAddress,
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 pub struct YuvImagePrimitiveGpu {
-    pub y_uv0: DevicePoint,
-    pub y_uv1: DevicePoint,
-    pub u_uv0: DevicePoint,
-    pub u_uv1: DevicePoint,
-    pub v_uv0: DevicePoint,
-    pub v_uv1: DevicePoint,
     pub size: LayerSize,
     pub color_space: f32,
     pub padding: f32,
 }
 
 impl YuvImagePrimitiveGpu {
     pub fn new(size: LayerSize, color_space: YuvColorSpace) -> Self {
         YuvImagePrimitiveGpu {
-            y_uv0: DevicePoint::zero(),
-            y_uv1: DevicePoint::zero(),
-            u_uv0: DevicePoint::zero(),
-            u_uv1: DevicePoint::zero(),
-            v_uv0: DevicePoint::zero(),
-            v_uv1: DevicePoint::zero(),
             size: size,
             color_space: color_space as u32 as f32,
             padding: 0.0,
         }
     }
 }
 
 #[derive(Debug, Clone)]
@@ -671,18 +659,20 @@ impl PrimitiveStore {
                     gpu_data_count: 0,
                     render_task: None,
                     clip_task: None,
                 };
 
                 self.cpu_images.push(image_cpu);
                 metadata
             }
-            PrimitiveContainer::YuvImage(image_cpu, image_gpu) => {
-                let gpu_address = self.gpu_data64.push(image_gpu);
+            PrimitiveContainer::YuvImage(mut image_cpu, image_gpu) => {
+                image_cpu.yuv_resource_address = self.gpu_resource_rects.alloc(3);
+
+                let gpu_address = self.gpu_data16.push(image_gpu);
 
                 let metadata = PrimitiveMetadata {
                     is_opaque: true,
                     clip_source: clip_source,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::YuvImage,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_yuv_images.len()),
                     gpu_prim_index: gpu_address,
@@ -850,16 +840,45 @@ impl PrimitiveStore {
     }
 
     pub fn resolve_clip_cache(&mut self,
                               clip_info: &MaskCacheInfo,
                               resource_cache: &ResourceCache) {
         Self::resolve_clip_cache_internal(&mut self.gpu_data32, clip_info, resource_cache)
     }
 
+    fn resolve_image(resource_cache: &ResourceCache,
+                     deferred_resolves: &mut Vec<DeferredResolve>,
+                     image_key: ImageKey,
+                     image_uv_address: GpuStoreAddress,
+                     image_rendering: ImageRendering,
+                     tile_offset: Option<TileOffset>) -> (SourceTexture, Option<CacheItem>) {
+        let image_properties = resource_cache.get_image_properties(image_key);
+
+        // Check if an external image that needs to be resolved
+        // by the render thread.
+        match image_properties.external_id {
+            Some(external_id) => {
+                // This is an external texture - we will add it to
+                // the deferred resolves list to be patched by
+                // the render thread...
+                deferred_resolves.push(DeferredResolve {
+                    image_properties: image_properties,
+                    resource_address: image_uv_address,
+                });
+
+                (SourceTexture::External(external_id), None)
+            }
+            None => {
+                let cache_item = resource_cache.get_cached_image(image_key, image_rendering, tile_offset);
+                (cache_item.texture_id, Some(cache_item))
+            }
+        }
+    }
+
     pub fn resolve_primitives(&mut self,
                               resource_cache: &ResourceCache,
                               device_pixel_ratio: f32) -> Vec<DeferredResolve> {
         profile_scope!("resolve_primitives");
         let mut deferred_resolves = Vec::new();
 
         for prim_index in self.prims_to_resolve.drain(..) {
             let metadata = &mut self.cpu_metadata[prim_index.0];
@@ -897,35 +916,22 @@ impl PrimitiveStore {
                 }
                 PrimitiveKind::Image => {
                     let image_cpu = &mut self.cpu_images[metadata.cpu_prim_index.0];
 
                     let (texture_id, cache_item) = match image_cpu.kind {
                         ImagePrimitiveKind::Image(image_key, image_rendering, tile_offset, _) => {
                             // Check if an external image that needs to be resolved
                             // by the render thread.
-                            let image_properties = resource_cache.get_image_properties(image_key);
-
-                            match image_properties.external_id {
-                                Some(external_id) => {
-                                    // This is an external texture - we will add it to
-                                    // the deferred resolves list to be patched by
-                                    // the render thread...
-                                    deferred_resolves.push(DeferredResolve {
-                                        resource_address: image_cpu.resource_address,
-                                        image_properties: image_properties,
-                                    });
-
-                                    (SourceTexture::External(external_id), None)
-                                }
-                                None => {
-                                    let cache_item = resource_cache.get_cached_image(image_key, image_rendering, tile_offset);
-                                    (cache_item.texture_id, Some(cache_item))
-                                }
-                            }
+                            PrimitiveStore::resolve_image(resource_cache,
+                                                          &mut deferred_resolves,
+                                                          image_key,
+                                                          image_cpu.resource_address,
+                                                          image_rendering,
+                                                          tile_offset)
                         }
                         ImagePrimitiveKind::WebGL(context_id) => {
                             let cache_item = resource_cache.get_webgl_texture(&context_id);
                             (cache_item.texture_id, Some(cache_item))
                         }
                     };
 
                     if let Some(cache_item) = cache_item {
@@ -942,39 +948,40 @@ impl PrimitiveStore {
                                 resource_rect.uv1 = cache_item.uv1;
                             }
                         }
                     }
                     image_cpu.color_texture_id = texture_id;
                 }
                 PrimitiveKind::YuvImage => {
                     let image_cpu = &mut self.cpu_yuv_images[metadata.cpu_prim_index.0];
-                    let image_gpu: &mut YuvImagePrimitiveGpu = unsafe {
-                        mem::transmute(self.gpu_data64.get_mut(metadata.gpu_prim_index))
-                    };
 
-                    if image_cpu.y_texture_id == SourceTexture::Invalid {
-                        let y_cache_item = resource_cache.get_cached_image(image_cpu.y_key, ImageRendering::Auto, None);
-                        image_cpu.y_texture_id = y_cache_item.texture_id;
-                        image_gpu.y_uv0 = y_cache_item.uv0;
-                        image_gpu.y_uv1 = y_cache_item.uv1;
-                    }
+                    //yuv
+                    for channel in 0..3 {
+                        if image_cpu.yuv_texture_id[channel] == SourceTexture::Invalid {
+                            // Check if an external image that needs to be resolved
+                            // by the render thread.
+                            let resource_address = image_cpu.yuv_resource_address + channel as i32;
 
-                    if image_cpu.u_texture_id == SourceTexture::Invalid {
-                        let u_cache_item = resource_cache.get_cached_image(image_cpu.u_key, ImageRendering::Auto, None);
-                        image_cpu.u_texture_id = u_cache_item.texture_id;
-                        image_gpu.u_uv0 = u_cache_item.uv0;
-                        image_gpu.u_uv1 = u_cache_item.uv1;
-                    }
-
-                    if image_cpu.v_texture_id == SourceTexture::Invalid {
-                        let v_cache_item = resource_cache.get_cached_image(image_cpu.v_key, ImageRendering::Auto, None);
-                        image_cpu.v_texture_id = v_cache_item.texture_id;
-                        image_gpu.v_uv0 = v_cache_item.uv0;
-                        image_gpu.v_uv1 = v_cache_item.uv1;
+                            let (texture_id, cache_item) =
+                                PrimitiveStore::resolve_image(resource_cache,
+                                                              &mut deferred_resolves,
+                                                              image_cpu.yuv_key[channel],
+                                                              resource_address,
+                                                              ImageRendering::Auto,
+                                                              None);
+                            // texture_id
+                            image_cpu.yuv_texture_id[channel] = texture_id;
+                            // uv coordinates
+                            if let Some(cache_item) = cache_item {
+                                let resource_rect = self.gpu_resource_rects.get_mut(image_cpu.yuv_resource_address + channel as i32);
+                                resource_rect.uv0 = cache_item.uv0;
+                                resource_rect.uv1 = cache_item.uv1;
+                            }
+                        }
                     }
                 }
             }
         }
 
         deferred_resolves
     }
 
@@ -1183,19 +1190,19 @@ impl PrimitiveStore {
                     }
                     ImagePrimitiveKind::WebGL(..) => {}
                 }
             }
             PrimitiveKind::YuvImage => {
                 let image_cpu = &mut self.cpu_yuv_images[metadata.cpu_prim_index.0];
                 prim_needs_resolve = true;
 
-                resource_cache.request_image(image_cpu.y_key, ImageRendering::Auto, None);
-                resource_cache.request_image(image_cpu.u_key, ImageRendering::Auto, None);
-                resource_cache.request_image(image_cpu.v_key, ImageRendering::Auto, None);
+                for channel in 0..3 {
+                    resource_cache.request_image(image_cpu.yuv_key[channel], ImageRendering::Auto, None);
+                }
 
                 // TODO(nical): Currently assuming no tile_spacing for yuv images.
                 metadata.is_opaque = true;
             }
             PrimitiveKind::AlignedGradient => {
                 let gradient = &mut self.cpu_gradients[metadata.cpu_prim_index.0];
                 if gradient.cache_dirty {
                     let src_stops = auxiliary_lists.gradient_stops(&gradient.stops_range);
@@ -1330,20 +1337,20 @@ impl From<GradientStopGpu> for GpuBlock3
 impl From<RadialGradientPrimitiveGpu> for GpuBlock32 {
     fn from(data: RadialGradientPrimitiveGpu) -> GpuBlock32 {
         unsafe {
             mem::transmute::<RadialGradientPrimitiveGpu, GpuBlock32>(data)
         }
     }
 }
 
-impl From<YuvImagePrimitiveGpu> for GpuBlock64 {
-    fn from(data: YuvImagePrimitiveGpu) -> GpuBlock64 {
+impl From<YuvImagePrimitiveGpu> for GpuBlock16 {
+    fn from(data: YuvImagePrimitiveGpu) -> GpuBlock16 {
         unsafe {
-            mem::transmute::<YuvImagePrimitiveGpu, GpuBlock64>(data)
+            mem::transmute::<YuvImagePrimitiveGpu, GpuBlock16>(data)
         }
     }
 }
 
 impl From<ClipRect> for GpuBlock32 {
     fn from(data: ClipRect) -> GpuBlock32 {
         unsafe {
             mem::transmute::<ClipRect, GpuBlock32>(data)
--- a/gfx/webrender/src/profiler.rs
+++ b/gfx/webrender/src/profiler.rs
@@ -63,16 +63,20 @@ impl IntProfileCounter {
     pub fn add(&mut self, amount: usize) {
         self.value += amount;
     }
 
     #[inline(always)]
     pub fn set(&mut self, amount: usize) {
         self.value = amount;
     }
+
+    pub fn get(&self) -> usize {
+        self.value
+    }
 }
 
 impl ProfileCounter for IntProfileCounter {
     fn description(&self) -> &'static str {
         self.description
     }
 
     fn value(&self) -> String {
@@ -148,16 +152,20 @@ impl TimeProfileCounter {
     pub fn profile<T, F>(&mut self, callback: F) -> T where F: FnOnce() -> T {
         let t0 = precise_time_ns();
         let val = callback();
         let t1 = precise_time_ns();
         let ns = t1 - t0;
         self.nanoseconds += ns;
         val
     }
+
+    pub fn get(&self) -> u64 {
+        self.nanoseconds
+    }
 }
 
 impl ProfileCounter for TimeProfileCounter {
     fn description(&self) -> &'static str {
         self.description
     }
 
     fn value(&self) -> String {
--- a/gfx/webrender/src/record.rs
+++ b/gfx/webrender/src/record.rs
@@ -61,16 +61,17 @@ impl ApiRecordingReceiver for BinaryReco
         self.write_length_and_data(data);
     }
 }
 
 pub fn should_record_msg(msg: &ApiMsg) -> bool {
     match msg {
         &ApiMsg::AddRawFont(..) |
         &ApiMsg::AddNativeFont(..) |
+        &ApiMsg::DeleteFont(..) |
         &ApiMsg::AddImage(..) |
         &ApiMsg::GenerateFrame(..) |
         &ApiMsg::UpdateImage(..) |
         &ApiMsg::DeleteImage(..) |
         &ApiMsg::SetRootDisplayList(..) |
         &ApiMsg::SetRootPipeline(..) |
         &ApiMsg::Scroll(..) |
         &ApiMsg::TickScrollingBounce |
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -13,33 +13,40 @@ use resource_cache::ResourceCache;
 use scene::Scene;
 use std::collections::HashMap;
 use std::io::{Cursor, Read};
 use std::sync::{Arc, Mutex};
 use std::sync::mpsc::Sender;
 use texture_cache::TextureCache;
 use thread_profiler::register_thread_with_profiler;
 use threadpool::ThreadPool;
+use webrender_traits::{DeviceIntPoint, DeviceUintPoint, DeviceUintRect, DeviceUintSize, LayerPoint};
 use webrender_traits::{ApiMsg, AuxiliaryLists, BuiltDisplayList, IdNamespace, ImageData};
 use webrender_traits::{PipelineId, RenderNotifier, RenderDispatcher, WebGLCommand, WebGLContextId};
 use webrender_traits::channel::{PayloadHelperMethods, PayloadReceiver, PayloadSender, MsgReceiver};
 use webrender_traits::{BlobImageRenderer, VRCompositorCommand, VRCompositorHandler};
 use offscreen_gl_context::GLContextDispatcher;
 
 /// The render backend is responsible for transforming high level display lists into
 /// GPU-friendly work which is then submitted to the renderer in the form of a frame::Frame.
 ///
 /// The render backend operates on its own thread.
 pub struct RenderBackend {
     api_rx: MsgReceiver<ApiMsg>,
     payload_rx: PayloadReceiver,
     payload_tx: PayloadSender,
     result_tx: Sender<ResultMsg>,
 
-    device_pixel_ratio: f32,
+    // TODO(gw): Consider using strongly typed units here.
+    hidpi_factor: f32,
+    page_zoom_factor: f32,
+    pinch_zoom_factor: f32,
+    pan: DeviceIntPoint,
+    window_size: DeviceUintSize,
+    inner_rect: DeviceUintRect,
     next_namespace_id: IdNamespace,
 
     resource_cache: ResourceCache,
 
     scene: Scene,
     frame: Frame,
 
     notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
@@ -54,50 +61,56 @@ pub struct RenderBackend {
     vr_compositor_handler: Arc<Mutex<Option<Box<VRCompositorHandler>>>>
 }
 
 impl RenderBackend {
     pub fn new(api_rx: MsgReceiver<ApiMsg>,
                payload_rx: PayloadReceiver,
                payload_tx: PayloadSender,
                result_tx: Sender<ResultMsg>,
-               device_pixel_ratio: f32,
+               hidpi_factor: f32,
                texture_cache: TextureCache,
                enable_aa: bool,
                workers: Arc<Mutex<ThreadPool>>,
                notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
                webrender_context_handle: Option<GLContextHandleWrapper>,
                config: FrameBuilderConfig,
                recorder: Option<Box<ApiRecordingReceiver>>,
                main_thread_dispatcher: Arc<Mutex<Option<Box<RenderDispatcher>>>>,
                blob_image_renderer: Option<Box<BlobImageRenderer>>,
-               vr_compositor_handler: Arc<Mutex<Option<Box<VRCompositorHandler>>>>) -> RenderBackend {
+               vr_compositor_handler: Arc<Mutex<Option<Box<VRCompositorHandler>>>>,
+               initial_window_size: DeviceUintSize) -> RenderBackend {
 
         let resource_cache = ResourceCache::new(texture_cache, workers, blob_image_renderer, enable_aa);
 
         register_thread_with_profiler("Backend".to_string());
 
         RenderBackend {
             api_rx: api_rx,
             payload_rx: payload_rx,
             payload_tx: payload_tx,
             result_tx: result_tx,
-            device_pixel_ratio: device_pixel_ratio,
+            hidpi_factor: hidpi_factor,
+            page_zoom_factor: 1.0,
+            pinch_zoom_factor: 1.0,
+            pan: DeviceIntPoint::zero(),
             resource_cache: resource_cache,
             scene: Scene::new(),
             frame: Frame::new(config),
             next_namespace_id: IdNamespace(1),
             notifier: notifier,
             webrender_context_handle: webrender_context_handle,
             webgl_contexts: HashMap::new(),
             current_bound_webgl_context_id: None,
             recorder: recorder,
             main_thread_dispatcher: main_thread_dispatcher,
             next_webgl_id: 0,
-            vr_compositor_handler: vr_compositor_handler
+            vr_compositor_handler: vr_compositor_handler,
+            window_size: initial_window_size,
+            inner_rect: DeviceUintRect::new(DeviceUintPoint::zero(), initial_window_size),
         }
     }
 
     pub fn run(&mut self, mut profile_counters: BackendProfileCounters) {
         let mut frame_counter: u32 = 0;
 
         loop {
             let msg = self.api_rx.recv();
@@ -112,16 +125,19 @@ impl RenderBackend {
                             profile_counters.font_templates.inc(bytes.len());
                             self.resource_cache
                                 .add_font_template(id, FontTemplate::Raw(Arc::new(bytes)));
                         }
                         ApiMsg::AddNativeFont(id, native_font_handle) => {
                             self.resource_cache
                                 .add_font_template(id, FontTemplate::Native(native_font_handle));
                         }
+                        ApiMsg::DeleteFont(id) => {
+                            self.resource_cache.delete_font_template(id);
+                        }
                         ApiMsg::GetGlyphDimensions(glyph_keys, tx) => {
                             let mut glyph_dimensions = Vec::with_capacity(glyph_keys.len());
                             for glyph_key in &glyph_keys {
                                 let glyph_dim = self.resource_cache.get_glyph_dimensions(glyph_key);
                                 glyph_dimensions.push(glyph_dim);
                             };
                             tx.send(glyph_dimensions).unwrap();
                         }
@@ -132,16 +148,29 @@ impl RenderBackend {
                             self.resource_cache.add_image_template(id, descriptor, data, tiling);
                         }
                         ApiMsg::UpdateImage(id, descriptor, bytes) => {
                             self.resource_cache.update_image_template(id, descriptor, bytes);
                         }
                         ApiMsg::DeleteImage(id) => {
                             self.resource_cache.delete_image_template(id);
                         }
+                        ApiMsg::SetPageZoom(factor) => {
+                            self.page_zoom_factor = factor.get();
+                        }
+                        ApiMsg::SetPinchZoom(factor) => {
+                            self.pinch_zoom_factor = factor.get();
+                        }
+                        ApiMsg::SetPan(pan) => {
+                            self.pan = pan;
+                        }
+                        ApiMsg::SetWindowParameters(window_size, inner_rect) => {
+                            self.window_size = window_size;
+                            self.inner_rect = inner_rect;
+                        }
                         ApiMsg::CloneApi(sender) => {
                             let result = self.next_namespace_id;
 
                             let IdNamespace(id_namespace) = self.next_namespace_id;
                             self.next_namespace_id = IdNamespace(id_namespace + 1);
 
                             sender.send(result).unwrap();
                         }
@@ -393,16 +422,20 @@ impl RenderBackend {
             }
         }
     }
 
     fn discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId) {
         self.frame.discard_frame_state_for_pipeline(pipeline_id);
     }
 
+    fn accumulated_scale_factor(&self) -> f32 {
+        self.hidpi_factor * self.page_zoom_factor * self.pinch_zoom_factor
+    }
+
     fn build_scene(&mut self) {
         // Flatten the stacking context hierarchy
         if let Some(id) = self.current_bound_webgl_context_id {
             self.webgl_contexts[&id].unbind();
             self.current_bound_webgl_context_id = None;
         }
 
         // When running in OSMesa mode with texture sharing,
@@ -413,27 +446,35 @@ impl RenderBackend {
         // context at the start of a render frame should
         // incur minimal cost.
         for (_, webgl_context) in &self.webgl_contexts {
             webgl_context.make_current();
             webgl_context.apply_command(WebGLCommand::Flush);
             webgl_context.unbind();
         }
 
-        self.frame.create(&self.scene, &mut self.resource_cache);
+        let accumulated_scale_factor = self.accumulated_scale_factor();
+        self.frame.create(&self.scene,
+                          &mut self.resource_cache,
+                          self.window_size,
+                          self.inner_rect,
+                          accumulated_scale_factor);
     }
 
     fn render(&mut self,
               texture_cache_profile: &mut TextureCacheProfileCounters)
               -> RendererFrame {
+        let accumulated_scale_factor = self.accumulated_scale_factor();
+        let pan = LayerPoint::new(self.pan.x as f32 / accumulated_scale_factor,
+                                  self.pan.y as f32 / accumulated_scale_factor);
         let frame = self.frame.build(&mut self.resource_cache,
                                      &self.scene.pipeline_auxiliary_lists,
-                                     self.device_pixel_ratio,
+                                     accumulated_scale_factor,
+                                     pan,
                                      texture_cache_profile);
-
         frame
     }
 
     fn publish_frame(&mut self,
                      frame: RendererFrame,
                      profile_counters: &mut BackendProfileCounters) {
         let pending_update = self.resource_cache.pending_updates();
         let pending_external_image_update = self.resource_cache.pending_external_image_updates();
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -2,19 +2,19 @@
  * 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/. */
 
 use internal_types::{HardwareCompositeOp, LowLevelFilterOp};
 use mask_cache::MaskCacheInfo;
 use prim_store::{PrimitiveCacheKey, PrimitiveIndex};
 use std::{cmp, f32, i32, mem, usize};
 use tiling::{ClipScrollGroupIndex, PackedLayerIndex, RenderPass, RenderTargetIndex};
-use tiling::{ScrollLayerIndex, StackingContextIndex};
+use tiling::{StackingContextIndex};
 use webrender_traits::{DeviceIntLength, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
-use webrender_traits::MixBlendMode;
+use webrender_traits::{MixBlendMode, ScrollLayerId};
 
 const FLOATS_PER_RENDER_TASK_INFO: usize = 12;
 
 #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
 pub struct RenderTaskIndex(pub usize);
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 pub enum RenderTaskKey {
@@ -28,17 +28,17 @@ pub enum RenderTaskKey {
     HorizontalBlur(i32, PrimitiveIndex),
     /// Allocate a block of space in target for framebuffer copy.
     CopyFramebuffer(StackingContextIndex),
 }
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 pub enum MaskCacheKey {
     Primitive(PrimitiveIndex),
-    ScrollLayer(ScrollLayerIndex),
+    ScrollLayer(ScrollLayerId),
 }
 
 #[derive(Debug, Copy, Clone)]
 pub enum RenderTaskId {
     Static(RenderTaskIndex),
     Dynamic(RenderTaskKey),
 }
 
@@ -57,16 +57,17 @@ pub enum AlphaRenderItem {
     HardwareComposite(StackingContextIndex, RenderTaskId, HardwareCompositeOp, i32),
 }
 
 #[derive(Debug, Clone)]
 pub struct AlphaRenderTask {
     screen_origin: DeviceIntPoint,
     pub opaque_items: Vec<AlphaRenderItem>,
     pub alpha_items: Vec<AlphaRenderItem>,
+    pub isolate_clear: bool,
 }
 
 #[derive(Debug, Copy, Clone)]
 #[repr(C)]
 pub enum MaskSegment {
     // This must match the SEGMENT_ values in clip_shared.glsl!
     All = 0,
     TopLeftCorner,
@@ -139,25 +140,27 @@ pub struct RenderTask {
     pub location: RenderTaskLocation,
     pub children: Vec<RenderTask>,
     pub kind: RenderTaskKind,
 }
 
 impl RenderTask {
     pub fn new_alpha_batch(task_index: RenderTaskIndex,
                            screen_origin: DeviceIntPoint,
+                           isolate_clear: bool,
                            location: RenderTaskLocation) -> RenderTask {
         RenderTask {
             id: RenderTaskId::Static(task_index),
             children: Vec::new(),
             location: location,
             kind: RenderTaskKind::Alpha(AlphaRenderTask {
                 screen_origin: screen_origin,
                 alpha_items: Vec::new(),
                 opaque_items: Vec::new(),
+                isolate_clear: isolate_clear,
             }),
         }
     }
 
     pub fn new_prim_cache(key: PrimitiveCacheKey,
                           size: DeviceIntSize,
                           prim_index: PrimitiveIndex) -> RenderTask {
         RenderTask {
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -6,52 +6,52 @@
 //!
 //! The `webrender::renderer` module provides the interface to webrender, which
 //! is accessible through [`Renderer`][renderer]
 //!
 //! [renderer]: struct.Renderer.html
 
 use debug_colors;
 use debug_render::DebugRenderer;
-use device::{DepthFunction, Device, ProgramId, TextureId, VertexFormat, GpuMarker, GpuProfiler};
-use device::{TextureFilter, VAOId, VertexUsageHint, FileWatcherHandler, TextureTarget, ShaderError};
+use device::{DepthFunction, Device, FrameId, ProgramId, TextureId, VertexFormat, GpuMarker, GpuProfiler};
+use device::{GpuSample, TextureFilter, VAOId, VertexUsageHint, FileWatcherHandler, TextureTarget, ShaderError};
 use euclid::Matrix4D;
 use fnv::FnvHasher;
 use frame_builder::FrameBuilderConfig;
 use gpu_store::{GpuStore, GpuStoreLayout};
 use internal_types::{CacheTextureId, RendererFrame, ResultMsg, TextureUpdateOp};
 use internal_types::{ExternalImageUpdateList, TextureUpdateList, PackedVertex, RenderTargetMode};
 use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, SourceTexture};
 use internal_types::{BatchTextures, TextureSampler, GLContextHandleWrapper};
 use prim_store::GradientData;
 use profiler::{Profiler, BackendProfileCounters};
 use profiler::{GpuProfileTag, RendererProfileTimers, RendererProfileCounters};
 use record::ApiRecordingReceiver;
 use render_backend::RenderBackend;
 use render_task::RenderTaskData;
 use std;
 use std::cmp;
-use std::collections::HashMap;
+use std::collections::{HashMap, VecDeque};
 use std::f32;
 use std::hash::BuildHasherDefault;
 use std::marker::PhantomData;
 use std::mem;
 use std::path::PathBuf;
 use std::sync::{Arc, Mutex};
 use std::sync::mpsc::{channel, Receiver, Sender};
 use std::thread;
 use texture_cache::TextureCache;
 use threadpool::ThreadPool;
 use tiling::{AlphaBatchKind, BlurCommand, Frame, PrimitiveBatch, PrimitiveBatchData};
 use tiling::{CacheClipInstance, PrimitiveInstance, RenderTarget};
 use time::precise_time_ns;
 use thread_profiler::{register_thread_with_profiler, write_profile};
 use util::TransformedRectKind;
 use webrender_traits::{ColorF, Epoch, PipelineId, RenderNotifier, RenderDispatcher};
-use webrender_traits::{ExternalImageId, ImageData, ImageFormat, RenderApiSender, RendererKind};
+use webrender_traits::{ExternalImageId, ImageData, ImageFormat, RenderApiSender};
 use webrender_traits::{DeviceIntRect, DevicePoint, DeviceIntPoint, DeviceIntSize, DeviceUintSize};
 use webrender_traits::{ImageDescriptor, BlobImageRenderer};
 use webrender_traits::channel;
 use webrender_traits::VRCompositorHandler;
 
 pub const GPU_DATA_TEXTURE_POOL: usize = 5;
 pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024;
 
@@ -70,24 +70,65 @@ const GPU_TAG_PRIM_TEXT_RUN: GpuProfileT
 const GPU_TAG_PRIM_GRADIENT: GpuProfileTag = GpuProfileTag { label: "Gradient", color: debug_colors::YELLOW };
 const GPU_TAG_PRIM_ANGLE_GRADIENT: GpuProfileTag = GpuProfileTag { label: "AngleGradient", color: debug_colors::POWDERBLUE };
 const GPU_TAG_PRIM_RADIAL_GRADIENT: GpuProfileTag = GpuProfileTag { label: "RadialGradient", color: debug_colors::LIGHTPINK };
 const GPU_TAG_PRIM_BOX_SHADOW: GpuProfileTag = GpuProfileTag { label: "BoxShadow", color: debug_colors::CYAN };
 const GPU_TAG_PRIM_BORDER: GpuProfileTag = GpuProfileTag { label: "Border", color: debug_colors::ORANGE };
 const GPU_TAG_PRIM_CACHE_IMAGE: GpuProfileTag = GpuProfileTag { label: "CacheImage", color: debug_colors::SILVER };
 const GPU_TAG_BLUR: GpuProfileTag = GpuProfileTag { label: "Blur", color: debug_colors::VIOLET };
 
+#[derive(Debug, Copy, Clone)]
+pub enum RendererKind {
+    Native,
+    OSMesa,
+}
+
+#[derive(Debug)]
+pub struct GpuProfile {
+    pub frame_id: FrameId,
+    pub paint_time_ns: u64,
+}
+
+impl GpuProfile {
+    fn new<T>(frame_id: FrameId, samples: &[GpuSample<T>]) -> GpuProfile {
+        let mut paint_time_ns = 0;
+        for sample in samples {
+            paint_time_ns += sample.time_ns;
+        }
+        GpuProfile {
+            frame_id: frame_id,
+            paint_time_ns: paint_time_ns,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub struct CpuProfile {
+    pub frame_id: FrameId,
+    pub composite_time_ns: u64,
+    pub draw_calls: usize,
+}
+
+impl CpuProfile {
+    fn new(frame_id: FrameId,
+           composite_time_ns: u64,
+           draw_calls: usize) -> CpuProfile {
+        CpuProfile {
+            frame_id: frame_id,
+            composite_time_ns: composite_time_ns,
+            draw_calls: draw_calls,
+        }
+    }
+}
+
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub enum BlendMode {
     None,
     Alpha,
-
-    Multiply,
-    Max,
-    Min,
+    PremultipliedAlpha,
 
     // Use the color of the text itself as a constant color blend factor.
     Subpixel(ColorF),
 }
 
 struct GpuDataTexture<L> {
     id: TextureId,
     layout: PhantomData<L>,
@@ -424,16 +465,17 @@ pub struct Renderer {
 
     ps_blend: LazilyCompiledShader,
     ps_hw_composite: LazilyCompiledShader,
     ps_composite: LazilyCompiledShader,
 
     notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
 
     enable_profiler: bool,
+    max_recorded_profiles: usize,
     clear_framebuffer: bool,
     clear_color: ColorF,
     debug: DebugRenderer,
     render_target_debug: bool,
     backend_profile_counters: BackendProfileCounters,
     profile_counters: RendererProfileCounters,
     profiler: Profiler,
     last_time: u64,
@@ -472,17 +514,22 @@ pub struct Renderer {
     /// application to provide external buffers for image data.
     external_image_handler: Option<Box<ExternalImageHandler>>,
 
     /// Map of external image IDs to native textures.
     external_images: HashMap<ExternalImageId, TextureId, BuildHasherDefault<FnvHasher>>,
 
     // Optional trait object that handles WebVR commands.
     // Some WebVR commands such as SubmitFrame must be synced with the WebGL render thread.
-    vr_compositor_handler: Arc<Mutex<Option<Box<VRCompositorHandler>>>>
+    vr_compositor_handler: Arc<Mutex<Option<Box<VRCompositorHandler>>>>,
+
+    /// List of profile results from previous frames. Can be retrieved
+    /// via get_frame_profiles().
+    cpu_profiles: VecDeque<CpuProfile>,
+    gpu_profiles: VecDeque<GpuProfile>,
 }
 
 #[derive(Debug)]
 pub enum InitError {
     Shader(ShaderError),
     Thread(std::io::Error),
 }
 
@@ -508,17 +555,18 @@ impl Renderer {
     /// let opts = webrender::RendererOptions {
     ///    device_pixel_ratio: 1.0,
     ///    resource_override_path: None,
     ///    enable_aa: false,
     ///    enable_profiler: false,
     /// };
     /// let (renderer, sender) = Renderer::new(opts);
     /// ```
-    pub fn new(mut options: RendererOptions) -> Result<(Renderer, RenderApiSender), InitError> {
+    pub fn new(mut options: RendererOptions,
+               initial_window_size: DeviceUintSize) -> Result<(Renderer, RenderApiSender), InitError> {
         let (api_tx, api_rx) = try!{ channel::msg_channel() };
         let (payload_tx, payload_rx) = try!{ channel::payload_channel() };
         let (result_tx, result_rx) = channel();
 
         register_thread_with_profiler("Compositor".to_owned());
 
         let notifier = Arc::new(Mutex::new(None));
 
@@ -800,17 +848,18 @@ impl Renderer {
                                                  enable_aa,
                                                  workers,
                                                  backend_notifier,
                                                  context_handle,
                                                  config,
                                                  recorder,
                                                  backend_main_thread_dispatcher,
                                                  blob_image_renderer,
-                                                 backend_vr_compositor);
+                                                 backend_vr_compositor,
+                                                 initial_window_size);
             backend.run(backend_profile_counters);
         })};
 
         let renderer = Renderer {
             result_rx: result_rx,
             device: device,
             current_frame: None,
             pending_texture_updates: Vec::new(),
@@ -837,33 +886,36 @@ impl Renderer {
             ps_composite: ps_composite,
             notifier: notifier,
             debug: debug_renderer,
             render_target_debug: render_target_debug,
             backend_profile_counters: BackendProfileCounters::new(),
             profile_counters: RendererProfileCounters::new(),
             profiler: Profiler::new(),
             enable_profiler: options.enable_profiler,
+            max_recorded_profiles: options.max_recorded_profiles,
             clear_framebuffer: options.clear_framebuffer,
             clear_color: options.clear_color,
             last_time: 0,
             render_targets: Vec::new(),
             gpu_profile: GpuProfiler::new(),
             prim_vao_id: prim_vao_id,
             blur_vao_id: blur_vao_id,
             clip_vao_id: clip_vao_id,
             gdt_index: 0,
             gpu_data_textures: gpu_data_textures,
             pipeline_epoch_map: HashMap::with_hasher(Default::default()),
             main_thread_dispatcher: main_thread_dispatcher,
             cache_texture_id_map: Vec::new(),
             dummy_cache_texture_id: dummy_cache_texture_id,
             external_image_handler: None,
             external_images: HashMap::with_hasher(Default::default()),
-            vr_compositor_handler: vr_compositor
+            vr_compositor_handler: vr_compositor,
+            cpu_profiles: VecDeque::new(),
+            gpu_profiles: VecDeque::new(),
         };
 
         let sender = RenderApiSender::new(api_tx, payload_tx);
         Ok((renderer, sender))
     }
 
     /// Sets the new RenderNotifier.
     ///
@@ -955,56 +1007,80 @@ impl Renderer {
         }
     }
 
     /// Set a callback for handling external images.
     pub fn set_external_image_handler(&mut self, handler: Box<ExternalImageHandler>) {
         self.external_image_handler = Some(handler);
     }
 
+    /// Retrieve (and clear) the current list of recorded frame profiles.
+    pub fn get_frame_profiles(&mut self) -> (Vec<CpuProfile>, Vec<GpuProfile>) {
+        let cpu_profiles = self.cpu_profiles.drain(..).collect();
+        let gpu_profiles = self.gpu_profiles.drain(..).collect();
+        (cpu_profiles, gpu_profiles)
+    }
+
     /// Renders the current frame.
     ///
     /// A Frame is supplied by calling [set_root_stacking_context()][newframe].
     /// [newframe]: ../../webrender_traits/struct.RenderApi.html#method.set_root_stacking_context
     pub fn render(&mut self, framebuffer_size: DeviceUintSize) {
         profile_scope!("render");
 
         if let Some(mut frame) = self.current_frame.take() {
             if let Some(ref mut frame) = frame.frame {
                 let mut profile_timers = RendererProfileTimers::new();
 
                 // Block CPU waiting for last frame's GPU profiles to arrive.
                 // In general this shouldn't block unless heavily GPU limited.
-                if let Some(samples) = self.gpu_profile.build_samples() {
+                if let Some((gpu_frame_id, samples)) = self.gpu_profile.build_samples() {
+                    if self.max_recorded_profiles > 0 {
+                        while self.gpu_profiles.len() >= self.max_recorded_profiles {
+                            self.gpu_profiles.pop_front();
+                        }
+                        self.gpu_profiles.push_back(GpuProfile::new(gpu_frame_id, &samples));
+                    }
                     profile_timers.gpu_samples = samples;
                 }
 
-                profile_timers.cpu_time.profile(|| {
-                    self.device.begin_frame(frame.device_pixel_ratio);
-                    self.gpu_profile.begin_frame();
+                let cpu_frame_id = profile_timers.cpu_time.profile(|| {
+                    let cpu_frame_id = self.device.begin_frame(frame.device_pixel_ratio);
+                    self.gpu_profile.begin_frame(cpu_frame_id);
                     {
                         let _gm = self.gpu_profile.add_marker(GPU_TAG_INIT);
 
                         self.device.disable_scissor();
                         self.device.disable_depth();
                         self.device.set_blend(false);
 
                         //self.update_shaders();
                         self.update_texture_cache();
                     }
 
                     self.draw_tile_frame(frame, &framebuffer_size);
 
                     self.gpu_profile.end_frame();
+                    cpu_frame_id
                 });
 
                 let current_time = precise_time_ns();
                 let ns = current_time - self.last_time;
                 self.profile_counters.frame_time.set(ns);
 
+                if self.max_recorded_profiles > 0 {
+                    while self.cpu_profiles.len() >= self.max_recorded_profiles {
+                        self.cpu_profiles.pop_front();
+                    }
+                    let cpu_profile = CpuProfile::new(cpu_frame_id,
+                                                      profile_timers.cpu_time.get(),
+                                                      self.profile_counters.draw_calls.get());
+                    self.cpu_profiles.push_back(cpu_profile);
+                }
+
                 if self.enable_profiler {
                     self.profiler.draw_profile(&frame.profile_counters,
                                                &self.backend_profile_counters,
                                                &self.profile_counters,
                                                &mut profile_timers,
                                                &mut self.debug);
                 }
 
@@ -1175,17 +1251,19 @@ impl Renderer {
                     batch: &PrimitiveBatch,
                     projection: &Matrix4D<f32>,
                     render_task_data: &Vec<RenderTaskData>,
                     cache_texture: TextureId,
                     render_target: Option<(TextureId, i32)>,
                     target_dimensions: DeviceUintSize) {
         let transform_kind = batch.key.flags.transform_kind();
         let needs_clipping = batch.key.flags.needs_clipping();
-        debug_assert!(!needs_clipping || batch.key.blend_mode == BlendMode::Alpha);
+        debug_assert!(!needs_clipping ||
+                      batch.key.blend_mode == BlendMode::Alpha ||
+                      batch.key.blend_mode == BlendMode::PremultipliedAlpha);
 
         match batch.data {
             PrimitiveBatchData::Instances(ref data) => {
                 let (marker, shader) = match batch.key.kind {
                     AlphaBatchKind::Composite => unreachable!(),
                     AlphaBatchKind::HardwareComposite => {
                         let shader = self.ps_hw_composite.get(&mut self.device);
                         (GPU_TAG_PRIM_HW_COMPOSITE, shader)
@@ -1200,18 +1278,17 @@ impl Renderer {
                         } else {
                             self.ps_rectangle.get(&mut self.device, transform_kind)
                         };
                         (GPU_TAG_PRIM_RECT, shader)
                     }
                     AlphaBatchKind::TextRun => {
                         let shader = match batch.key.blend_mode {
                             BlendMode::Subpixel(..) => self.ps_text_run_subpixel.get(&mut self.device, transform_kind),
-                            BlendMode::Alpha | BlendMode::None => self.ps_text_run.get(&mut self.device, transform_kind),
-                            _ => unreachable!(),
+                            BlendMode::Alpha | BlendMode::PremultipliedAlpha | BlendMode::None => self.ps_text_run.get(&mut self.device, transform_kind),
                         };
                         (GPU_TAG_PRIM_TEXT_RUN, shader)
                     }
                     AlphaBatchKind::Image => {
                         let shader = self.ps_image.get(&mut self.device, transform_kind);
                         (GPU_TAG_PRIM_IMAGE, shader)
                     }
                     AlphaBatchKind::YuvImage => {
@@ -1285,30 +1362,28 @@ impl Renderer {
                 let src_y = backdrop.data[1] - backdrop.data[5] + source.data[5];
 
                 let dest_x = readback.data[0];
                 let dest_y = readback.data[1];
 
                 let width = readback.data[2];
                 let height = readback.data[3];
 
-                // Need to invert the y coordinates when reading back from
-                // the framebuffer.
-                let y0 = if render_target.is_some() {
-                    src_y as i32
-                } else {
-                    target_dimensions.height as i32 - height as i32 - src_y as i32
-                };
+                let mut src = DeviceIntRect::new(DeviceIntPoint::new(src_x as i32, src_y as i32),
+                                                 DeviceIntSize::new(width as i32, height as i32));
+                let mut dest = DeviceIntRect::new(DeviceIntPoint::new(dest_x as i32, dest_y as i32),
+                                                  DeviceIntSize::new(width as i32, height as i32));
 
-                let src = DeviceIntRect::new(DeviceIntPoint::new(src_x as i32,
-                                                                 y0),
-                                             DeviceIntSize::new(width as i32, height as i32));
-                let dest = DeviceIntRect::new(DeviceIntPoint::new(dest_x as i32,
-                                                                  dest_y as i32),
-                                              DeviceIntSize::new(width as i32, height as i32));
+                // Need to invert the y coordinates and flip the image vertically when
+                // reading back from the framebuffer.
+                if render_target.is_none() {
+                    src.origin.y = target_dimensions.height as i32 - src.size.height - src.origin.y;
+                    dest.origin.y += dest.size.height;
+                    dest.size.height = -dest.size.height;
+                }
 
                 self.device.blit_render_target(render_target,
                                                Some(src),
                                                dest);
 
                 // Restore draw target to current pass render target + layer.
                 self.device.bind_draw_target(render_target, Some(target_dimensions));
 
@@ -1376,16 +1451,21 @@ impl Renderer {
             let clear_color = if should_clear {
                 Some(color)
             } else {
                 None
             };
 
             self.device.clear_target(clear_color, clear_depth);
 
+            let isolate_clear_color = Some([0.0, 0.0, 0.0, 0.0]);
+            for isolate_clear in &target.isolate_clears {
+                self.device.clear_target_rect(isolate_clear_color, None, *isolate_clear);
+            }
+
             projection
         };
 
         self.device.disable_depth_write();
 
         // Draw any blurs for this target.
         // Blurs are rendered as a standard 2-pass
         // separable implementation.
@@ -1496,32 +1576,24 @@ impl Renderer {
         self.device.disable_depth_write();
 
         for batch in &target.alpha_batcher.alpha_batches {
             if batch.key.blend_mode != prev_blend_mode {
                 match batch.key.blend_mode {
                     BlendMode::None => {
                         self.device.set_blend(false);
                     }
-                    BlendMode::Multiply => {
-                        self.device.set_blend(true);
-                        self.device.set_blend_mode_multiply();
-                    }
-                    BlendMode::Max => {
-                        self.device.set_blend(true);
-                        self.device.set_blend_mode_max();
-                    }
-                    BlendMode::Min => {
-                        self.device.set_blend(true);
-                        self.device.set_blend_mode_min();
-                    }
                     BlendMode::Alpha => {
                         self.device.set_blend(true);
                         self.device.set_blend_mode_alpha();
                     }
+                    BlendMode::PremultipliedAlpha => {
+                        self.device.set_blend(true);
+                        self.device.set_blend_mode_premultiplied_alpha();
+                    }
                     BlendMode::Subpixel(color) => {
                         self.device.set_blend(true);
                         self.device.set_blend_mode_subpixel(color);
                     }
                 }
                 prev_blend_mode = batch.key.blend_mode;
             }
 
@@ -1596,20 +1668,18 @@ impl Renderer {
                        frame: &mut Frame,
                        framebuffer_size: &DeviceUintSize) {
         let _gm = GpuMarker::new("tile frame draw");
         self.update_deferred_resolves(frame);
 
         // Some tests use a restricted viewport smaller than the main screen size.
         // Ensure we clear the framebuffer in these tests.
         // TODO(gw): Find a better solution for this?
-        let viewport_size = DeviceIntSize::new((frame.viewport_size.width * frame.device_pixel_ratio) as i32,
-                                               (frame.viewport_size.height * frame.device_pixel_ratio) as i32);
-        let needs_clear = viewport_size.width < framebuffer_size.width as i32 ||
-                          viewport_size.height < framebuffer_size.height as i32;
+        let needs_clear = frame.window_size.width < framebuffer_size.width ||
+                          frame.window_size.height < framebuffer_size.height;
 
         self.device.disable_depth_write();
         self.device.disable_stencil();
         self.device.set_blend(false);
 
         if frame.passes.is_empty() {
             self.device.clear_target(Some(self.clear_color.to_array()), Some(1.0));
         } else {
@@ -1773,16 +1843,17 @@ pub trait ExternalImageHandler {
     fn release(&mut self, key: ExternalImageId);
 }
 
 pub struct RendererOptions {
     pub device_pixel_ratio: f32,
     pub resource_override_path: Option<PathBuf>,
     pub enable_aa: bool,
     pub enable_profiler: bool,
+    pub max_recorded_profiles: usize,
     pub debug: bool,
     pub enable_scrollbars: bool,
     pub precache_shaders: bool,
     pub renderer_kind: RendererKind,
     pub enable_subpixel_aa: bool,
     pub clear_framebuffer: bool,
     pub clear_color: ColorF,
     pub render_target_debug: bool,
@@ -1794,16 +1865,17 @@ pub struct RendererOptions {
 
 impl Default for RendererOptions {
     fn default() -> RendererOptions {
         RendererOptions {
             device_pixel_ratio: 1.0,
             resource_override_path: None,
             enable_aa: true,
             enable_profiler: false,
+            max_recorded_profiles: 0,
             debug: false,
             enable_scrollbars: false,
             precache_shaders: false,
             renderer_kind: RendererKind::Native,
             enable_subpixel_aa: false,
             clear_framebuffer: true,
             clear_color: ColorF::new(1.0, 1.0, 1.0, 1.0),
             render_target_debug: false,
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -36,16 +36,18 @@ type GlyphCache = ResourceClassCache<Ren
 /// Message sent from the resource cache to the glyph cache thread.
 enum GlyphCacheMsg {
     /// Begin the frame - pass ownership of the glyph cache to the thread.
     BeginFrame(FrameId, GlyphCache),
     /// Add a new font.
     AddFont(FontKey, FontTemplate),
     /// Request glyphs for a text run.
     RequestGlyphs(FontKey, Au, ColorF, Vec<GlyphInstance>, FontRenderMode, Option<GlyphOptions>),
+    // Remove an existing font.
+    DeleteFont(FontKey),
     /// Finished requesting glyphs. Reply with new glyphs.
     EndFrame,
 }
 
 /// Results send from glyph cache thread back to main resource cache.
 enum GlyphCacheResultMsg {
     /// Return the glyph cache, and a list of newly rasterized glyphs.
     EndFrame(GlyphCache, Vec<GlyphRasterJob>),
@@ -255,16 +257,23 @@ impl ResourceCache {
         // Push the new font to the glyph cache thread, and also store
         // it locally for glyph metric requests.
         self.glyph_cache_tx
             .send(GlyphCacheMsg::AddFont(font_key, template.clone()))
             .unwrap();
         self.font_templates.insert(font_key, template);
     }
 
+    pub fn delete_font_template(&mut self, font_key: FontKey) {
+        self.glyph_cache_tx
+            .send(GlyphCacheMsg::DeleteFont(font_key))
+            .unwrap();
+        self.font_templates.remove(&font_key);
+    }
+
     pub fn add_image_template(&mut self,
                               image_key: ImageKey,
                               descriptor: ImageDescriptor,
                               data: ImageData,
                               mut tiling: Option<TileSize>) {
         if descriptor.width > self.max_texture_size() || descriptor.height > self.max_texture_size() {
             // We aren't going to be able to upload a texture this big, so tile it, even
             // if tiling was not requested.
@@ -834,16 +843,33 @@ fn spawn_glyph_cache_thread(workers: Arc
                                     }
                                 }
                             });
 
                             barrier.wait();
                         });
                     }
                 }
+                GlyphCacheMsg::DeleteFont(font_key) => {
+                    profile_scope!("DeleteFont");
+
+                    // Delete a font from the font context in each worker thread.
+                    let barrier = Arc::new(Barrier::new(worker_count));
+                    for _ in 0..worker_count {
+                        let barrier = barrier.clone();
+                        workers.lock().unwrap().execute(move || {
+                            FONT_CONTEXT.with(|font_context| {
+                                let mut font_context = font_context.borrow_mut();
+                                font_context.delete_font(&font_key);
+                            });
+                            barrier.wait();
+                        });
+                    }
+
+                }
                 GlyphCacheMsg::RequestGlyphs(key, size, color, glyph_instances, render_mode, glyph_options) => {
                     profile_scope!("RequestGlyphs");
 
                     // Request some glyphs for a text run.
                     // For any glyph that isn't currently in the cache,
                     // immeediately push a job to the worker thread pool
                     // to start rasterizing this glyph now!
                     let glyph_cache = glyph_cache.as_mut().unwrap();
--- a/gfx/webrender/src/scene.rs
+++ b/gfx/webrender/src/scene.rs
@@ -36,17 +36,24 @@ impl SceneProperties {
         }
 
         for property in properties.floats {
             self.float_properties.insert(property.key.id, property.value);
         }
     }
 
     /// Get the current value for a transform property.
-    pub fn resolve_layout_transform(&self, property: &PropertyBinding<LayoutTransform>) -> LayoutTransform {
+    pub fn resolve_layout_transform(&self,
+                                    property: Option<&PropertyBinding<LayoutTransform>>)
+                                    -> LayoutTransform {
+        let property = match property {
+            Some(property) => property,
+            None => return LayoutTransform::identity(),
+        };
+
         match *property {
             PropertyBinding::Value(matrix) => matrix,
             PropertyBinding::Binding(ref key) => {
                 self.transform_properties
                     .get(&key.id)
                     .cloned()
                     .unwrap_or_else(|| {
                         warn!("Property binding {:?} has an invalid value.", key);
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -2,35 +2,35 @@
  * 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/. */
 
 use app_units::Au;
 use fnv::FnvHasher;
 use gpu_store::GpuStoreAddress;
 use internal_types::{ANGLE_FLOAT_TO_FIXED, BatchTextures, CacheTextureId, LowLevelFilterOp};
 use internal_types::SourceTexture;
-use mask_cache::{ClipSource, MaskCacheInfo};
+use mask_cache::MaskCacheInfo;
 use prim_store::{CLIP_DATA_GPU_SIZE, DeferredResolve, GpuBlock128, GpuBlock16, GpuBlock32};
 use prim_store::{GpuBlock64, GradientData, PrimitiveCacheKey, PrimitiveGeometry, PrimitiveIndex};
 use prim_store::{PrimitiveKind, PrimitiveMetadata, PrimitiveStore, TexelRect};
 use profiler::FrameProfileCounters;
 use render_task::{AlphaRenderItem, MaskGeometryKind, MaskSegment, RenderTask, RenderTaskData};
 use render_task::{RenderTaskId, RenderTaskIndex, RenderTaskKey, RenderTaskKind};
 use render_task::RenderTaskLocation;
 use renderer::BlendMode;
 use resource_cache::ResourceCache;
 use std::{f32, i32, mem, usize};
 use std::collections::HashMap;
 use std::hash::BuildHasherDefault;
 use texture_cache::TexturePage;
 use util::{TransformedRect, TransformedRectKind};
 use webrender_traits::{AuxiliaryLists, ColorF, DeviceIntPoint, DeviceIntRect, DeviceUintPoint};
-use webrender_traits::{DeviceUintSize, FontRenderMode, ImageRendering, LayerRect, LayerSize};
-use webrender_traits::{LayerToScrollTransform, LayerToWorldTransform, MixBlendMode, PipelineId};
-use webrender_traits::{ScrollLayerId, WorldPoint4D, WorldToLayerTransform};
+use webrender_traits::{DeviceUintSize, FontRenderMode, ImageRendering, LayerPoint, LayerRect};
+use webrender_traits::{LayerToWorldTransform, MixBlendMode, PipelineId, ScrollLayerId};
+use webrender_traits::{WorldPoint4D, WorldToLayerTransform};
 
 // Special sentinel value recognized by the shader. It is considered to be
 // a dummy task that doesn't mask out anything.
 const OPAQUE_TASK_INDEX: RenderTaskIndex = RenderTaskIndex(i32::MAX as usize);
 
 
 pub type AuxiliaryListsMap = HashMap<PipelineId,
                                      AuxiliaryLists,
@@ -100,17 +100,17 @@ impl AlphaBatchHelpers for PrimitiveStor
             PrimitiveKind::AngleGradient |
             PrimitiveKind::RadialGradient => [invalid; 3],
             PrimitiveKind::Image => {
                 let image_cpu = &self.cpu_images[metadata.cpu_prim_index.0];
                 [image_cpu.color_texture_id, invalid, invalid]
             }
             PrimitiveKind::YuvImage => {
                 let image_cpu = &self.cpu_yuv_images[metadata.cpu_prim_index.0];
-                [image_cpu.y_texture_id, image_cpu.u_texture_id, image_cpu.v_texture_id]
+                image_cpu.yuv_texture_id
             }
             PrimitiveKind::TextRun => {
                 let text_run_cpu = &self.cpu_text_runs[metadata.cpu_prim_index.0];
                 [text_run_cpu.color_texture_id, invalid, invalid]
             }
         }
     }
 
@@ -123,16 +123,23 @@ impl AlphaBatchHelpers for PrimitiveStor
                         FontRenderMode::Subpixel => BlendMode::Subpixel(text_run_cpu.color),
                         FontRenderMode::Alpha | FontRenderMode::Mono => BlendMode::Alpha,
                     }
                 } else {
                     // Text runs drawn to blur never get drawn with subpixel AA.
                     BlendMode::Alpha
                 }
             }
+            PrimitiveKind::Image => {
+                if needs_blending {
+                    BlendMode::PremultipliedAlpha
+                } else {
+                    BlendMode::None
+                }
+            }
             _ => {
                 if needs_blending {
                     BlendMode::Alpha
                 } else {
                     BlendMode::None
                 }
             }
         }
@@ -272,24 +279,26 @@ impl AlphaBatchHelpers for PrimitiveStor
                             global_prim_id: global_prim_id,
                             prim_address: prim_address,
                             sub_index: 0,
                             user_data: [ image_cpu.resource_address.0, 0 ],
                             z_sort_index: z_sort_index,
                         });
                     }
                     AlphaBatchKind::YuvImage => {
+                        let image_yuv_cpu = &self.cpu_yuv_images[metadata.cpu_prim_index.0];
+
                         data.push(PrimitiveInstance {
                             task_index: task_index,
                             clip_task_index: clip_task_index,
                             layer_index: packed_layer_index,
                             global_prim_id: global_prim_id,
                             prim_address: prim_address,
                             sub_index: 0,
-                            user_data: [ 0, 0 ],
+                            user_data: [ image_yuv_cpu.yuv_resource_address.0, 0 ],
                             z_sort_index: z_sort_index,
                         });
                     }
                     AlphaBatchKind::Border => {
                         for border_segment in 0..8 {
                             data.push(PrimitiveInstance {
                                 task_index: task_index,
                                 clip_task_index: clip_task_index,
@@ -386,24 +395,21 @@ impl AlphaBatchHelpers for PrimitiveStor
 
 #[derive(Debug)]
 pub struct ScrollbarPrimitive {
     pub scroll_layer_id: ScrollLayerId,
     pub prim_index: PrimitiveIndex,
     pub border_radius: f32,
 }
 
+#[derive(Debug)]
 pub enum PrimitiveRunCmd {
     PushStackingContext(StackingContextIndex),
     PopStackingContext,
-
-    PushScrollLayer(ScrollLayerIndex),
-    PopScrollLayer,
-
-    PrimitiveRun(PrimitiveIndex, usize),
+    PrimitiveRun(PrimitiveIndex, usize, ScrollLayerId),
 }
 
 #[derive(Debug, Copy, Clone)]
 pub enum PrimitiveFlags {
     None,
     Scrollbar(ScrollLayerId, f32)
 }
 
@@ -865,30 +871,32 @@ pub struct RenderTarget {
     //           cache changes land, this restriction will
     //           be removed anyway.
     pub text_run_cache_prims: Vec<PrimitiveInstance>,
     pub text_run_textures: BatchTextures,
     // List of blur operations to apply for this render target.
     pub vertical_blurs: Vec<BlurCommand>,
     pub horizontal_blurs: Vec<BlurCommand>,
     pub readbacks: Vec<DeviceIntRect>,
+    pub isolate_clears: Vec<DeviceIntRect>,
     page_allocator: TexturePage,
 }
 
 impl RenderTarget {
     fn new(size: DeviceUintSize) -> RenderTarget {
         RenderTarget {
             alpha_batcher: AlphaBatcher::new(),
             clip_batcher: ClipBatcher::new(),
             box_shadow_cache_prims: Vec::new(),
             text_run_cache_prims: Vec::new(),
             text_run_textures: BatchTextures::no_texture(),
             vertical_blurs: Vec::new(),
             horizontal_blurs: Vec::new(),
             readbacks: Vec::new(),
+            isolate_clears: Vec::new(),
             page_allocator: TexturePage::new(CacheTextureId(0), size),
         }
     }
 
     fn build(&mut self,
              ctx: &RenderTargetContext,
              render_tasks: &mut RenderTaskCollection,
              child_pass_index: RenderPassIndex) {
@@ -904,16 +912,26 @@ impl RenderTarget {
                 pass_index: RenderPassIndex) {
         match task.kind {
             RenderTaskKind::Alpha(info) => {
                 self.alpha_batcher.add_task(AlphaBatchTask {
                     task_id: task.id,
                     opaque_items: info.opaque_items,
                     alpha_items: info.alpha_items,
                 });
+
+                if info.isolate_clear {
+                    let location = match task.location {
+                        RenderTaskLocation::Dynamic(origin, size) => {
+                            DeviceIntRect::new(origin.unwrap().0, size)
+                        }
+                        RenderTaskLocation::Fixed => panic!()
+                    };
+                    self.isolate_clears.push(location);
+                }
             }
             RenderTaskKind::VerticalBlur(_, prim_index) => {
                 // Find the child render task that we are applying
                 // a vertical blur on.
                 // TODO(gw): Consider a simpler way for render tasks to find
                 //           their child tasks than having to construct the
                 //           correct id here.
                 let child_pass_index = RenderPassIndex(pass_index.0 - 1);
@@ -1296,84 +1314,98 @@ impl PrimitiveBatch {
 pub struct PackedLayerIndex(pub usize);