Merge m-c to autoland, a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 21 Feb 2017 17:40:31 -0800
changeset 373141 0fc829f4ae4a2894bf3feb97219c9db64e2923b0
parent 373140 5ffd6c7264ce71b06645dadab81de19afb7eeb6d (current diff)
parent 373118 9f871c40b36f164a3413c5aea5c4434f080a7bf0 (diff)
child 373142 b6e9e58ac9d735861ad91f6b419b66b3f06d0f48
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone54.0a1
Merge m-c to autoland, a=merge MozReview-Commit-ID: E64mguPbLgc
gfx/layers/wr/WebRenderCompositorOGL.cpp
gfx/layers/wr/WebRenderCompositorOGL.h
third_party/rust/byteorder-0.5.3/.cargo-checksum.json
third_party/rust/byteorder-0.5.3/.cargo-ok
third_party/rust/byteorder-0.5.3/.gitignore
third_party/rust/byteorder-0.5.3/.travis.yml
third_party/rust/byteorder-0.5.3/COPYING
third_party/rust/byteorder-0.5.3/Cargo.toml
third_party/rust/byteorder-0.5.3/LICENSE-MIT
third_party/rust/byteorder-0.5.3/Makefile
third_party/rust/byteorder-0.5.3/README.md
third_party/rust/byteorder-0.5.3/UNLICENSE
third_party/rust/byteorder-0.5.3/benches/bench.rs
third_party/rust/byteorder-0.5.3/session.vim
third_party/rust/byteorder-0.5.3/src/lib.rs
third_party/rust/byteorder-0.5.3/src/new.rs
third_party/rust/euclid-0.10.3/.cargo-checksum.json
third_party/rust/euclid-0.10.3/.cargo-ok
third_party/rust/euclid-0.10.3/.gitignore
third_party/rust/euclid-0.10.3/.travis.yml
third_party/rust/euclid-0.10.3/COPYRIGHT
third_party/rust/euclid-0.10.3/Cargo.toml
third_party/rust/euclid-0.10.3/LICENSE-APACHE
third_party/rust/euclid-0.10.3/LICENSE-MIT
third_party/rust/euclid-0.10.3/README.md
third_party/rust/euclid-0.10.3/src/approxeq.rs
third_party/rust/euclid-0.10.3/src/length.rs
third_party/rust/euclid-0.10.3/src/lib.rs
third_party/rust/euclid-0.10.3/src/macros.rs
third_party/rust/euclid-0.10.3/src/matrix2d.rs
third_party/rust/euclid-0.10.3/src/matrix4d.rs
third_party/rust/euclid-0.10.3/src/num.rs
third_party/rust/euclid-0.10.3/src/point.rs
third_party/rust/euclid-0.10.3/src/rect.rs
third_party/rust/euclid-0.10.3/src/scale_factor.rs
third_party/rust/euclid-0.10.3/src/side_offsets.rs
third_party/rust/euclid-0.10.3/src/size.rs
third_party/rust/euclid-0.10.3/src/trig.rs
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -406,18 +406,23 @@ DocAccessibleParent::RecvBindChildDoc(PD
 
 ipc::IPCResult
 DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
                                  uint64_t aParentID, bool aCreating)
 {
   // We do not use GetAccessible here because we want to be sure to not get the
   // document it self.
   ProxyEntry* e = mAccessibles.GetEntry(aParentID);
-  if (!e)
+  if (!e) {
+#ifdef DEBUG
     return IPC_FAIL(this, "binding to nonexistant proxy!");
+#else
+    return IPC_OK();
+#endif
+  }
 
   ProxyAccessible* outerDoc = e->mProxy;
   MOZ_ASSERT(outerDoc);
 
   // OuterDocAccessibles are expected to only have a document as a child.
   // However for compatibility we tolerate replacing one document with another
   // here.
   if (outerDoc->ChildrenCount() > 1 ||
--- a/browser/components/sessionstore/test/browser_crashedTabs.js
+++ b/browser/components/sessionstore/test/browser_crashedTabs.js
@@ -8,17 +8,16 @@ requestLongerTimeout(10);
 const PAGE_1 = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page.";
 const PAGE_2 = "data:text/html,<html><body>Another%20regular,%20everyday,%20normal%20page.";
 
 // Turn off tab animations for testing and use a single content process
 // for these tests since we want to test tabs within the crashing process here.
 add_task(function* test_initialize() {
   yield SpecialPowers.pushPrefEnv({
     set: [
-      [ "dom.ipc.processCount", 1 ],
       [ "browser.tabs.animate", false]
   ] });
 });
 
 // Allow tabs to restore on demand so we can test pending states
 Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
 
 function clickButton(browser, id) {
@@ -236,17 +235,17 @@ add_task(function* test_revive_tab_from_
   gBrowser.selectedTab = newTab;
   let browser = newTab.linkedBrowser;
   ok(browser.isRemoteBrowser, "Should be a remote browser");
   yield promiseBrowserLoaded(browser);
 
   browser.loadURI(PAGE_1);
   yield promiseBrowserLoaded(browser);
 
-  let newTab2 = gBrowser.addTab();
+  let newTab2 = gBrowser.addTab("about:blank", { sameProcessAsFrameLoader: browser.frameLoader });
   let browser2 = newTab2.linkedBrowser;
   ok(browser2.isRemoteBrowser, "Should be a remote browser");
   yield promiseBrowserLoaded(browser2);
 
   browser.loadURI(PAGE_1);
   yield promiseBrowserLoaded(browser);
 
   browser.loadURI(PAGE_2);
@@ -293,17 +292,17 @@ add_task(function* test_revive_all_tabs_
 
   browser.loadURI(PAGE_1);
   yield promiseBrowserLoaded(browser);
 
   // In order to see a second about:tabcrashed page, we'll need
   // a second window, since only selected tabs will show
   // about:tabcrashed.
   let win2 = yield BrowserTestUtils.openNewBrowserWindow();
-  let newTab2 = win2.gBrowser.addTab(PAGE_1);
+  let newTab2 = win2.gBrowser.addTab(PAGE_1, { sameProcessAsFrameLoader: browser.frameLoader });
   win2.gBrowser.selectedTab = newTab2;
   let browser2 = newTab2.linkedBrowser;
   ok(browser2.isRemoteBrowser, "Should be a remote browser");
   yield promiseBrowserLoaded(browser2);
 
   browser.loadURI(PAGE_1);
   yield promiseBrowserLoaded(browser);
 
@@ -400,17 +399,17 @@ add_task(function* test_hide_restore_all
   gBrowser.selectedTab = newTab;
 
   browser.loadURI(PAGE_2);
   yield promiseBrowserLoaded(browser);
 
   // Load up a second window so we can get another tab to show
   // about:tabcrashed
   let win2 = yield BrowserTestUtils.openNewBrowserWindow();
-  let newTab3 = win2.gBrowser.addTab(PAGE_2);
+  let newTab3 = win2.gBrowser.addTab(PAGE_2, { sameProcessAsFrameLoader: browser.frameLoader });
   win2.gBrowser.selectedTab = newTab3;
   let otherWinBrowser = newTab3.linkedBrowser;
   yield promiseBrowserLoaded(otherWinBrowser);
   // We'll need to make sure the second tab's browser has finished
   // sending its AboutTabCrashedReady event before we know for
   // sure whether or not we're showing the right Restore buttons.
   let otherBrowserReady = promiseTabCrashedReady(otherWinBrowser);
 
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -89,16 +89,17 @@
 #if BUILD_FASTER
     locale/browser/searchplugins/               (searchplugins/*.xml)
     locale/browser/searchplugins/list.json      (search/list.json)
 #else
     locale/browser/searchplugins/               (.deps/generated_@AB_CD@/*.xml)
     locale/browser/searchplugins/list.json      (.deps/generated_@AB_CD@/list.json)
 #endif
     locale/browser/searchplugins/images/wikipedia.ico  (searchplugins/images/wikipedia.ico)
+    locale/browser/searchplugins/images/yandex-en.ico  (searchplugins/images/yandex-en.ico)
 % locale browser-region @AB_CD@ %locale/browser-region/
     locale/browser-region/region.properties        (%chrome/browser-region/region.properties)
 # the following files are browser-specific overrides
     locale/browser/netError.dtd                (%chrome/overrides/netError.dtd)
     locale/browser/appstrings.properties       (%chrome/overrides/appstrings.properties)
     locale/browser/downloads/settingsChange.dtd  (%chrome/overrides/settingsChange.dtd)
 % override chrome://global/locale/netError.dtd chrome://browser/locale/netError.dtd
 % override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties
--- a/browser/locales/search/list.json
+++ b/browser/locales/search/list.json
@@ -40,17 +40,17 @@
     "en-US": {
       "default": {
         "visibleDefaultEngines": [
           "google", "yahoo", "amazondotcom", "bing", "ddg", "twitter", "wikipedia"
         ]
       },
       "experimental-hidden": {
         "visibleDefaultEngines": [
-          "yahoo-en-CA"
+          "yahoo-en-CA", "yandex-en"
         ]
       }
     },
     "ach": {
       "default": {
         "visibleDefaultEngines": [
           "google", "yahoo", "bing", "amazondotcom", "ddg", "twitter", "wikipedia"
         ]
@@ -189,16 +189,21 @@
         ]
       }
     },
     "en-GB": {
       "default": {
         "visibleDefaultEngines": [
           "google", "yahoo-en-GB", "bing", "amazon-en-GB", "chambers-en-GB", "ddg", "twitter", "wikipedia"
         ]
+      },
+      "experimental-hidden": {
+        "visibleDefaultEngines": [
+          "yandex-en"
+        ]
       }
     },
     "en-ZA": {
       "default": {
         "visibleDefaultEngines": [
           "google", "bing", "amazondotcom", "ddg", "twitter", "wikipedia"
         ]
       }
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6398f30e9df15714b3c5007977ecd2b9b0032852
GIT binary patch
literal 1691
zc$@*724wjF0096303aX$0096X0A&OK044wc01yxW0096X01yHI0E+|w0EtjeM-2)Z
z3IG5A4M|8uQUCw|AOHXWAP5Ek0047(dh`GQ1T0BJK~z|UwU=K^TXhu2KesI)&;lB$
z(>Xvg0jJr_GGq#S(3ctKlVNB`qo}YYE=wi`Lc)Uy8HR)o{L4gOLu4-5B9ZvOi$1sq
z9b`rlW0r|44yvqd;09P|p*=p_+tNaBX|X#u=jEPrf8X={p7Z<Ndlgi(01pA_fPuu?
z@4$jy&<L1;LLdcD5)2NI49rGLC<68k4-YpkFE7tX;>_RD(ozNNLseMU_5vkduXiqi
z;36X8a=Cs0N`PWLa06-^NJ=u@55iV`>HF_+<Qs70D@+v@0)o|=eW10q^>Iysh$<LR
z`>u$<|MB^*&$VK}Qd_ItSCs1N>LVJ)_NWD{4@{SrGqrOk&Y~i`V`IAg1Mtnv;2s@P
z7gQ`*YHETptJPZ8*4Fj}kO^c&DnPSvN$-39IaYlRR=nff{h(t*Jk%Ha`VhP-`-&qN
zvNFTsHk++h1Bs}@eqg`f=aW!hak<2imnzru5Jz6JtT>&rKF9C#iGOufCJP_a@{Ho0
zn24BXx7%wptS}YUW?&sJLv}XiLvLu>c$x3*jmUrL(j`_r;{c>R{W8Yf+=zKLo2^E}
z+7YpUNYZB3y@R4|5*E&WhIl<2au&L~gNtRHIHB97oSf{^Q>WT?qs~I+Mg<~FwlV)v
z(w?aRz$erEd*NbO+;@8x_m6`Br0ggoxwJIuPN}N0y|rG2s0x6PQD0ABlPq+0hQ$^K
z1`s?d`?@+^_8}^iw70kK0kQ%rtQDa1T=MQcq!u3pz;km1&*hO|Y_X>Y0F$YJv_mh&
zbY{I;Ss73vFT4flUm1-J!9LG_^dW-a869E8eH8%9@#Ba=iL%1lmGhu=Z%2pyDNTi}
zO%*^OrL3Hk?au<>9vNhHatimwuhkwZnV4UDJ+70xvf`CD11iL%fFM}v>opC0EPV1Y
z?r+rLGgrM0=Jc4@2QI(5kOC;14q?DkUwnZi^OFE94tBCCH&95%eDrAS)HOeU{#>7i
z`)5ooK-7XW>fZ&_g9#w*z+sHrw#9|-a^3o+p`r0t;4W|{<`xJ->fs8E$|J#nOh0xk
zF6jlKzrX(r4Ra!F7sSNCXu|YxZm<ATK|!4CXMS#KYWfYh1N;MEBKRs6wq~(EcebbZ
zOAXHvBoQb;Vq7OQEpWufVt-HHyz*Xi^HoCFhZV3j1%ea0shv5~GoWEz552#pz&(U_
zI-OTeo_zlrD)YIB1>DOK=<4b|r{VmuK4yw*r44zTYvBCLIcm4tuc0!ZTOSXZff&sg
z_>5Fg492LbA|jogU0<QY9xZSXFx_&w`qRzn55!f>CN_Cva<q8Yu4dF<v&)fk3|f$(
zZ-=@)o~8MC&viW)@yn;DPoL9p#`TI+P`^ydNc>-Mz-ED;^nymf1KbAEfK<YNRubW3
zz>;18{{eGJdDVo-K575}002ovPDHLkV1kKIPDc$28VUda01Zh<L{b0%01yBG01yZU
z0000V^Z#K000GxYL_t(Ijg6AAPg7A8#((bxjYcek5rQ#kH&GhnVr+~tF-A8Q7XJVS
zhO$GPstaRDyFfxjLuAm+e?cQLQE?Pfprlg!T3bYlvF*F}bEvO*mPYw*zTup6@9&(P
zTn9J@Tm<?!o;HAkaN=66R$J3CR4$j_0++gp*Cnfexp?os3h}rkt86wq0h|p3Zdhw&
zt<{%ALZ$I>1+leO&0J2KQAhg=3$oVA^SpP!<uD*&jgd7*`}6bKj5=zpuPew^XJ(X-
z4a?N)om?i9nFJyM;HELYjPd0k)|HP9t1>m!jvK#}j}58%_>qD;#`y9)?>#UY0B-qy
zOMz>%v)YQDQS1Aje!P64t>|erOJ5bf<NGb8)9EBI0C2n2YASH!TSeQk0oCTxDvpiG
zo0w3z9(Z@Rum)W116)a+g*cH2{SPKNm`h_+Vd&K=;d&rz0dQTwMI!9}WD+BfQ=@k{
zF)~7T9f_5d<!8XokqH3Y!9iT-Jbj~AdUh{)-lys5hp&OXo`8--Pr%aB(`8^A+W+4b
zy#e}j{_0*VeN3fNZ-80=aOgRK+g?#uZSl$Cb08Pa-7W0oKmR<e>wnH|Y<vI`;l~be
l0f+-9doSXMR)J~|{03;Py=C@Zxi$a*002ovPDHLkV1kK%5B>lE
new file mode 100644
--- /dev/null
+++ b/browser/locales/searchplugins/yandex-en.xml
@@ -0,0 +1,17 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Yandex</ShortName>
+<Description>Use Yandex to search the Internet.</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/yandex-en.ico</Image>
+<Url type="application/x-suggestions+json" method="GET" template="https://suggest.yandex.com/suggest-ff.cgi">
+  <Param name="part" value="{searchTerms}"/>
+</Url>
+<Url type="text/html" method="GET" template="https://www.yandex.com/search">
+  <Param name="text" value="{searchTerms}"/>
+</Url>
+<SearchForm>https://www.yandex.com/</SearchForm>
+</SearchPlugin>
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -826,16 +826,17 @@ def compiler(language, host_or_target, c
 c_compiler = compiler('C', target)
 cxx_compiler = compiler('C++', target, c_compiler=c_compiler)
 host_c_compiler = compiler('C', host, other_compiler=c_compiler)
 host_cxx_compiler = compiler('C++', host, c_compiler=host_c_compiler,
                              other_compiler=cxx_compiler,
                              other_c_compiler=c_compiler)
 
 # Generic compiler-based conditions.
+building_with_msvc = depends(c_compiler)(lambda info: info.type == 'msvc')
 non_msvc_compiler = depends(c_compiler)(lambda info: info.type != 'msvc')
 building_with_gcc = depends(c_compiler)(lambda info: info.type == 'gcc')
 
 include('compile-checks.configure')
 
 @depends(have_64_bit,
          try_compile(body='static_assert(sizeof(void *) == 8, "")',
                      check_msg='for 64-bit OS'))
@@ -962,13 +963,43 @@ def wrap_system_includes(target, visibil
 
 set_define('HAVE_VISIBILITY_HIDDEN_ATTRIBUTE',
            depends(visibility_flags)(lambda v: bool(v) or None))
 set_define('HAVE_VISIBILITY_ATTRIBUTE',
            depends(visibility_flags)(lambda v: bool(v) or None))
 set_config('WRAP_SYSTEM_INCLUDES', wrap_system_includes)
 set_config('VISIBILITY_FLAGS', visibility_flags)
 
+@depends(c_compiler, using_sccache)
+def depend_cflags(c_compiler, using_sccache):
+    if c_compiler.type in ('gcc', 'clang'):
+        return '-MD -MP -MF $(MDDEPDIR)/$(@F).pp'
+    elif c_compiler.type == 'msvc' and using_sccache:
+        # sccache supports a special flag to create depfiles
+        # by parsing MSVC's -showIncludes output.
+        return '-deps$(MDDEPDIR)/$(@F).pp'
+
+set_config('_DEPEND_CFLAGS', depend_cflags)
+
+@depends(c_compiler, when=building_with_msvc)
+@imports(_from='re', _import='compile', _as='re_compile')
+def msvc_showincludes_prefix(c_compiler):
+    pattern = re_compile(r'^([^:]*:.*[ :] )(.*\\stdio.h)$')
+    output = try_invoke_compiler([c_compiler.compiler], 'C', '#include <stdio.h>\n',
+                                 ['-nologo', '-c', '-Fonul', '-showIncludes'])
+    for line in output.splitlines():
+        if line.endswith('\\stdio.h'):
+            m = pattern.match(line)
+            if m:
+                if not m.group(2):
+                    die("Unable to parse cl -showIncludes prefix. " +
+                        "This compiler's locale has an unsupported formatting.")
+                return m.group(1)
+    # We should have found the prefix and returned earlier
+    die('Cannot find cl -showIncludes prefix.')
+
+set_config('CL_INCLUDES_PREFIX', msvc_showincludes_prefix)
+
 @depends(target)
 def is_windows(target):
     return target.kernel == 'WINNT'
 
 include('windows.configure', when=is_windows)
--- a/build/mozconfig.cache
+++ b/build/mozconfig.cache
@@ -108,36 +108,16 @@ if test -z "$bucket"; then
         ac_add_options --with-ccache
     esac
 else
     if ! test -e $topsrcdir/sccache2/sccache${suffix}; then
         echo "sccache2 missing in the tooltool manifest" >&2
         exit 1
     fi
     mk_add_options "export SCCACHE_BUCKET=$bucket"
-    case "$master" in
-    *us[ew][12].mozilla.com*|*euc1.mozilla.com*)
-        mk_add_options "export SCCACHE_NAMESERVER=169.254.169.253"
-        ;;
-    esac
     ac_add_options "--with-ccache=$topsrcdir/sccache2/sccache${suffix}"
     export SCCACHE_VERBOSE_STATS=1
     mk_add_options MOZ_PREFLIGHT_ALL+=build/sccache.mk
     mk_add_options MOZ_POSTFLIGHT_ALL+=build/sccache.mk
     mk_add_options "UPLOAD_EXTRA_FILES+=sccache.log.gz"
-    case "$platform" in
-    win*)
-        # sccache supports a special flag to create depfiles.
-        #TODO: bug 1318370 - move this all into toolchain.configure
-        export _DEPEND_CFLAGS='-deps$(MDDEPDIR)/$(@F).pp'
-        # Windows builds have a default wrapper that needs to be overridden
-        mk_add_options "export CC_WRAPPER="
-        mk_add_options "export CXX_WRAPPER="
-        # For now, sccache doesn't support separate PDBs so force debug info to be
-        # in object files.
-        mk_add_options "export COMPILE_PDB_FLAG="
-        mk_add_options "export HOST_PDB_FLAG="
-        mk_add_options "export MOZ_DEBUG_FLAGS=-Z7"
-        ;;
-    esac
 fi
 
 fi
--- a/config/config.mk
+++ b/config/config.mk
@@ -115,18 +115,20 @@ else
   win_srcdir := $(srcdir)
   BUILD_TOOLS = $(MOZILLA_DIR)/build/unix
 endif
 
 CONFIG_TOOLS	= $(MOZ_BUILD_ROOT)/config
 AUTOCONF_TOOLS	= $(MOZILLA_DIR)/build/autoconf
 
 ifdef _MSC_VER
+ifndef MOZ_USING_SCCACHE
 CC_WRAPPER ?= $(call py_action,cl)
 CXX_WRAPPER ?= $(call py_action,cl)
+endif
 endif # _MSC_VER
 
 CC := $(CC_WRAPPER) $(CC)
 CXX := $(CXX_WRAPPER) $(CXX)
 MKDIR ?= mkdir
 SLEEP ?= sleep
 TOUCH ?= touch
 
@@ -618,12 +620,8 @@ ifdef GNU_CC
 OBJ_SUFFIX := i_o
 endif
 endif
 endif
 
 PLY_INCLUDE = -I$(MOZILLA_DIR)/other-licenses/ply
 
 export CL_INCLUDES_PREFIX
-# Make sure that the build system can handle non-ASCII characters
-# in environment variables to prevent it from breking silently on
-# non-English systems.
-export NONASCII
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -151,16 +151,20 @@ endif # FORCE_SHARED_LIB
 endif # LIBRARY
 
 ifeq ($(OS_ARCH),WINNT)
 ifndef GNU_CC
 
 COMPILE_PDB_FLAG ?= -Fd$(basename $(@F)).pdb
 COMPILE_CFLAGS += $(COMPILE_PDB_FLAG)
 COMPILE_CXXFLAGS += $(COMPILE_PDB_FLAG)
+ifdef MOZ_USING_SCCACHE
+# We remove the PDB file before compilation so that sccache knows it's safe to cache.
+RM_PDB_FILE = -$(RM) $(basename $(@F)).pdb
+endif
 
 LINK_PDBFILE ?= $(basename $(@F)).pdb
 ifdef MOZ_DEBUG
 CODFILE=$(basename $(@F)).cod
 endif
 
 ifdef DEFFILE
 OS_LDFLAGS += -DEF:$(call normalizepath,$(DEFFILE))
@@ -845,33 +849,38 @@ crate_src_libdep = $(call mk_global_crat
 $(foreach f,$(RSSRCS),$(eval $(call src_libdep,$(f))))
 $(foreach f,$(RS_STATICLIB_CRATE_SRC),$(eval $(call crate_src_libdep,$(f))))
 
 $(OBJS) $(HOST_OBJS) $(PROGOBJS) $(HOST_PROGOBJS): $(GLOBAL_DEPS)
 
 # Rules for building native targets must come first because of the host_ prefix
 $(HOST_COBJS):
 	$(REPORT_BUILD_VERBOSE)
+	$(RM_PDB_FILE)
 	$(ELOG) $(HOST_CC) $(HOST_OUTOPTION)$@ -c $(HOST_CPPFLAGS) $(HOST_CFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS)
 
 $(HOST_CPPOBJS):
 	$(REPORT_BUILD_VERBOSE)
 	$(call BUILDSTATUS,OBJECT_FILE $@)
+	$(RM_PDB_FILE)
 	$(ELOG) $(HOST_CXX) $(HOST_OUTOPTION)$@ -c $(HOST_CPPFLAGS) $(HOST_CXXFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS)
 
 $(HOST_CMOBJS):
 	$(REPORT_BUILD_VERBOSE)
+	$(RM_PDB_FILE)
 	$(ELOG) $(HOST_CC) $(HOST_OUTOPTION)$@ -c $(HOST_CPPFLAGS) $(HOST_CFLAGS) $(HOST_CMFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS)
 
 $(HOST_CMMOBJS):
 	$(REPORT_BUILD_VERBOSE)
+	$(RM_PDB_FILE)
 	$(ELOG) $(HOST_CXX) $(HOST_OUTOPTION)$@ -c $(HOST_CPPFLAGS) $(HOST_CXXFLAGS) $(HOST_CMMFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS)
 
 $(COBJS):
 	$(REPORT_BUILD_VERBOSE)
+	$(RM_PDB_FILE)
 	$(ELOG) $(CC) $(OUTOPTION)$@ -c $(COMPILE_CFLAGS) $($(notdir $<)_FLAGS) $(_VPATH_SRCS)
 
 # DEFINES and ACDEFINES are needed here to enable conditional compilation of Q_OBJECTs:
 # 'moc' only knows about #defines it gets on the command line (-D...), not in
 # included headers like mozilla-config.h
 $(filter moc_%.cpp,$(CPPSRCS)): moc_%.cpp: %.h
 	$(REPORT_BUILD_VERBOSE)
 	$(ELOG) $(MOC) $(DEFINES) $(ACDEFINES) $< $(OUTOPTION)$@
@@ -993,24 +1002,27 @@ endif # MOZ_RUST
 
 $(SOBJS):
 	$(REPORT_BUILD)
 	$(AS) -o $@ $(DEFINES) $(ASFLAGS) $($(notdir $<)_FLAGS) $(LOCAL_INCLUDES) -c $<
 
 $(CPPOBJS):
 	$(REPORT_BUILD_VERBOSE)
 	$(call BUILDSTATUS,OBJECT_FILE $@)
+	$(RM_PDB_FILE)
 	$(ELOG) $(CCC) $(OUTOPTION)$@ -c $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(_VPATH_SRCS)
 
 $(CMMOBJS):
 	$(REPORT_BUILD_VERBOSE)
+	$(RM_PDB_FILE)
 	$(ELOG) $(CCC) -o $@ -c $(COMPILE_CXXFLAGS) $(COMPILE_CMMFLAGS) $($(notdir $<)_FLAGS) $(_VPATH_SRCS)
 
 $(CMOBJS):
 	$(REPORT_BUILD_VERBOSE)
+	$(RM_PDB_FILE)
 	$(ELOG) $(CC) -o $@ -c $(COMPILE_CFLAGS) $(COMPILE_CMFLAGS) $($(notdir $<)_FLAGS) $(_VPATH_SRCS)
 
 $(filter %.s,$(CPPSRCS:%.cpp=%.s)): %.s: %.cpp $(call mkdir_deps,$(MDDEPDIR))
 	$(REPORT_BUILD_VERBOSE)
 	$(CCC) -S $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(_VPATH_SRCS)
 
 $(filter %.s,$(CPPSRCS:%.cc=%.s)): %.s: %.cc $(call mkdir_deps,$(MDDEPDIR))
 	$(REPORT_BUILD_VERBOSE)
--- a/config/system-headers
+++ b/config/system-headers
@@ -1226,16 +1226,17 @@ X11/cursorfont.h
 X11/extensions/Print.h
 X11/extensions/shape.h
 X11/extensions/scrnsaver.h
 X11/extensions/XShm.h
 X11/extensions/Xrender.h
 X11/extensions/Xfixes.h
 X11/extensions/Xdamage.h
 X11/extensions/Xcomposite.h
+X11/ImUtil.h
 X11/Intrinsic.h
 X11/keysymdef.h
 X11/keysym.h
 X11/Shell.h
 X11/StringDefs.h
 X11/Xatom.h
 X11/Xft/Xft.h
 X11/Xfuncproto.h
--- a/devtools/client/locales/en-US/netmonitor.properties
+++ b/devtools/client/locales/en-US/netmonitor.properties
@@ -142,22 +142,22 @@ networkMenu.sortedAsc=Sorted ascending
 # LOCALIZATION NOTE (networkMenu.sortedDesc): This is the tooltip displayed
 # in the network table toolbar, for any column that is sorted descending.
 networkMenu.sortedDesc=Sorted descending
 
 # LOCALIZATION NOTE (networkMenu.empty): This is the label displayed
 # in the network table footer when there are no requests available.
 networkMenu.empty=No requests
 
-# LOCALIZATION NOTE (networkMenu.summary2): Semi-colon list of plural forms.
+# LOCALIZATION NOTE (networkMenu.summary3): Semi-colon list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # This label is displayed in the network table footer providing concise
 # information about all requests. Parameters: #1 is the number of requests,
 # #2 is the size, #3 is the transferred size, #4 is the number of seconds.
-networkMenu.summary2=One request, #2 KB (transferred: #3 KB), #4 s;#1 requests, #2 KB (transferred: #3 KB), #4 s
+networkMenu.summary3=One request, #2 (transferred: #3), #4;#1 requests, #2 (transferred: #3), #4
 
 # LOCALIZATION NOTE (networkMenu.sizeB): This is the label displayed
 # in the network menu specifying the size of a request (in bytes).
 networkMenu.sizeB=%S B
 
 # LOCALIZATION NOTE (networkMenu.sizeKB): This is the label displayed
 # in the network menu specifying the size of a request (in kilobytes).
 networkMenu.sizeKB=%S KB
--- a/devtools/client/netmonitor/components/toolbar.js
+++ b/devtools/client/netmonitor/components/toolbar.js
@@ -16,18 +16,18 @@ const Actions = require("../actions/inde
 const { L10N } = require("../utils/l10n");
 const { Prefs } = require("../utils/prefs");
 const {
   getDisplayedRequestsSummary,
   getRequestFilterTypes,
   isNetworkDetailsToggleButtonDisabled,
 } = require("../selectors/index");
 const {
-  getSizeWithDecimals,
-  getTimeWithDecimals,
+  getFormattedSize,
+  getFormattedTime
 } = require("../utils/format-utils");
 const { FILTER_SEARCH_DELAY } = require("../constants");
 
 // Components
 const SearchBox = createFactory(require("devtools/client/shared/components/search-box"));
 
 const { button, div, span } = DOM;
 
@@ -84,21 +84,21 @@ const Toolbar = createClass({
       "devtools-button",
     ];
     if (!networkDetailsOpen) {
       toggleButtonClassName.push("pane-collapsed");
     }
 
     let { count, contentSize, transferredSize, millis } = summary;
     let text = (count === 0) ? L10N.getStr("networkMenu.empty") :
-      PluralForm.get(count, L10N.getStr("networkMenu.summary2"))
+      PluralForm.get(count, L10N.getStr("networkMenu.summary3"))
       .replace("#1", count)
-      .replace("#2", getSizeWithDecimals(contentSize / 1024))
-      .replace("#3", getSizeWithDecimals(transferredSize / 1024))
-      .replace("#4", getTimeWithDecimals(millis / 1000));
+      .replace("#2", getFormattedSize(contentSize))
+      .replace("#3", getFormattedSize(transferredSize))
+      .replace("#4", getFormattedTime(millis));
 
     let buttons = requestFilterTypes.map(([type, checked]) => {
       let classList = ["devtools-button"];
       checked && classList.push("checked");
 
       return (
         button({
           id: `requests-menu-filter-${type}-button`,
--- a/devtools/client/netmonitor/test/browser_net_footer-summary.js
+++ b/devtools/client/netmonitor/test/browser_net_footer-summary.js
@@ -3,16 +3,21 @@
 
 "use strict";
 
 /**
  * Test if the summary text displayed in the network requests menu footer is correct.
  */
 
 add_task(function* () {
+  const {
+    getFormattedSize,
+    getFormattedTime
+  } = require("devtools/client/netmonitor/utils/format-utils");
+
   requestLongerTimeout(2);
 
   let { tab, monitor } = yield initNetMonitor(FILTERING_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
   let { getDisplayedRequestsSummary } =
@@ -56,16 +61,16 @@ add_task(function* () {
       is(value, L10N.getStr("networkMenu.empty"),
         "The current summary text is incorrect, expected an 'empty' label.");
       return;
     }
 
     info(`Computed total bytes: ${requestsSummary.bytes}`);
     info(`Computed total millis: ${requestsSummary.millis}`);
 
-    is(value, PluralForm.get(requestsSummary.count, L10N.getStr("networkMenu.summary2"))
+    is(value, PluralForm.get(requestsSummary.count, L10N.getStr("networkMenu.summary3"))
       .replace("#1", requestsSummary.count)
-      .replace("#2", L10N.numberWithDecimals(requestsSummary.contentSize / 1024, 2))
-      .replace("#3", L10N.numberWithDecimals(requestsSummary.transferredSize / 1024, 2))
-      .replace("#4", L10N.numberWithDecimals(requestsSummary.millis / 1000, 2))
+      .replace("#2", getFormattedSize(requestsSummary.contentSize))
+      .replace("#3", getFormattedSize(requestsSummary.transferredSize))
+      .replace("#4", getFormattedTime(requestsSummary.millis))
     , "The current summary text is correct.");
   }
 });
--- a/devtools/client/netmonitor/utils/format-utils.js
+++ b/devtools/client/netmonitor/utils/format-utils.js
@@ -13,59 +13,72 @@ const BYTES_IN_GB = Math.pow(BYTES_IN_KB
 const MAX_BYTES_SIZE = 1000;
 const MAX_KB_SIZE = 1000 * BYTES_IN_KB;
 const MAX_MB_SIZE = 1000 * BYTES_IN_MB;
 
 // Constants for formatting time.
 const MAX_MILLISECOND = 1000;
 const MAX_SECOND = 60 * MAX_MILLISECOND;
 
-const CONTENT_SIZE_DECIMALS = 2;
-const REQUEST_TIME_DECIMALS = 2;
+const REQUEST_DECIMALS = 2;
 
-function getSizeWithDecimals(size, decimals = REQUEST_TIME_DECIMALS) {
-  return L10N.numberWithDecimals(size, CONTENT_SIZE_DECIMALS);
+function getSizeWithDecimals(size, decimals = REQUEST_DECIMALS) {
+  return L10N.numberWithDecimals(size, decimals);
 }
 
 function getTimeWithDecimals(time) {
-  return L10N.numberWithDecimals(time, REQUEST_TIME_DECIMALS);
+  return L10N.numberWithDecimals(time, REQUEST_DECIMALS);
+}
+
+function formatDecimals(size, decimals) {
+  return (size % 1 > 0) ? decimals : 0;
 }
 
 /**
  * Get a human-readable string from a number of bytes, with the B, KB, MB, or
  * GB value. Note that the transition between abbreviations is by 1000 rather
  * than 1024 in order to keep the displayed digits smaller as "1016 KB" is
  * more awkward than 0.99 MB"
  */
-function getFormattedSize(bytes, decimals = REQUEST_TIME_DECIMALS) {
+function getFormattedSize(bytes, decimals = REQUEST_DECIMALS) {
   if (bytes < MAX_BYTES_SIZE) {
     return L10N.getFormatStr("networkMenu.sizeB", bytes);
-  } else if (bytes < MAX_KB_SIZE) {
-    let kb = bytes / BYTES_IN_KB;
-    return L10N.getFormatStr("networkMenu.sizeKB", getSizeWithDecimals(kb, decimals));
-  } else if (bytes < MAX_MB_SIZE) {
-    let mb = bytes / BYTES_IN_MB;
-    return L10N.getFormatStr("networkMenu.sizeMB", getSizeWithDecimals(mb, decimals));
+  }
+  if (bytes < MAX_KB_SIZE) {
+    const kb = bytes / BYTES_IN_KB;
+    const formattedDecimals = formatDecimals(kb, decimals);
+
+    return L10N.getFormatStr("networkMenu.sizeKB",
+      getSizeWithDecimals(kb, formattedDecimals));
   }
-  let gb = bytes / BYTES_IN_GB;
-  return L10N.getFormatStr("networkMenu.sizeGB", getSizeWithDecimals(gb, decimals));
+  if (bytes < MAX_MB_SIZE) {
+    const mb = bytes / BYTES_IN_MB;
+    const formattedDecimals = formatDecimals(mb, decimals);
+    return L10N.getFormatStr("networkMenu.sizeMB",
+      getSizeWithDecimals(mb, formattedDecimals));
+  }
+  const gb = bytes / BYTES_IN_GB;
+  const formattedDecimals = formatDecimals(gb, decimals);
+  return L10N.getFormatStr("networkMenu.sizeGB",
+    getSizeWithDecimals(gb, formattedDecimals));
 }
 
 /**
  * Get a human-readable string from a number of time, with the ms, s, or min
  * value.
  */
 function getFormattedTime(ms) {
   if (ms < MAX_MILLISECOND) {
     return L10N.getFormatStr("networkMenu.millisecond", ms | 0);
-  } else if (ms < MAX_SECOND) {
-    let sec = ms / MAX_MILLISECOND;
+  }
+  if (ms < MAX_SECOND) {
+    const sec = ms / MAX_MILLISECOND;
     return L10N.getFormatStr("networkMenu.second", getTimeWithDecimals(sec));
   }
-  let min = ms / MAX_SECOND;
+  const min = ms / MAX_SECOND;
   return L10N.getFormatStr("networkMenu.minute", getTimeWithDecimals(min));
 }
 
 module.exports = {
   getFormattedSize,
   getFormattedTime,
   getSizeWithDecimals,
   getTimeWithDecimals,
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -30,17 +30,17 @@
 #include "nsIDOMClassInfo.h"
 #include "xpcpublic.h"
 #include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/IntentionalCrash.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MessagePort.h"
-#include "mozilla/dom/nsIContentParent.h"
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/ProcessGlobal.h"
 #include "mozilla/dom/SameProcessMessageQueue.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/dom/DOMStringList.h"
@@ -773,16 +773,22 @@ nsFrameMessageManager::GetChildAt(uint32
 {
   *aMM = nullptr;
   nsCOMPtr<nsIMessageListenerManager> mm =
     do_QueryInterface(mChildManagers.SafeObjectAt(static_cast<uint32_t>(aIndex)));
   mm.swap(*aMM);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsFrameMessageManager::ReleaseCachedProcesses()
+{
+  ContentParent::ReleaseCachedProcesses();
+  return NS_OK;
+}
 
 // nsIContentFrameMessageManager
 
 NS_IMETHODIMP
 nsFrameMessageManager::Dump(const nsAString& aStr)
 {
 #ifdef ANDROID
   __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", NS_ConvertUTF16toUTF8(aStr).get());
--- a/dom/base/nsIMessageManager.idl
+++ b/dom/base/nsIMessageManager.idl
@@ -322,16 +322,22 @@ interface nsIMessageBroadcaster : nsIMes
    * Number of subordinate message managers.
    */
   readonly attribute unsigned long childCount;
 
   /**
    * Return a single subordinate message manager.
    */
   nsIMessageListenerManager getChildAt(in unsigned long aIndex);
+
+  /**
+   * Some processes are kept alive after their last tab/window are closed for testing
+   * (see dom.ipc.keepProcessesAlive). This function releases those.
+   */
+   void releaseCachedProcesses();
 };
 
 [scriptable, builtinclass, uuid(0e602c9e-1977-422a-a8e4-fe0d4a4f78d0)]
 interface nsISyncMessageSender : nsIMessageSender
 {
   /**
    * Like |sendAsyncMessage()|, except blocks the sender until all
    * listeners of the message have been invoked.  Returns an array
--- a/dom/base/test/browser.ini
+++ b/dom/base/test/browser.ini
@@ -19,17 +19,16 @@ support-files =
   plugin.js
 
 [browser_bug593387.js]
 [browser_bug902350.js]
 tags = mcb
 [browser_bug1011748.js]
 [browser_bug1058164.js]
 [browser_messagemanager_loadprocessscript.js]
-skip-if = e10s # Bug 1315042
 [browser_messagemanager_targetframeloader.js]
 [browser_messagemanager_unload.js]
 [browser_pagehide_on_tab_close.js]
 skip-if = e10s # this tests non-e10s behavior. it's not expected to work in e10s.
 [browser_state_notifications.js]
 skip-if = true # Bug 1271028
 [browser_use_counters.js]
 [browser_bug1307747.js]
--- a/dom/base/test/browser_messagemanager_loadprocessscript.js
+++ b/dom/base/test/browser_messagemanager_loadprocessscript.js
@@ -44,71 +44,94 @@ function promiseMessage(messageManager, 
       messageManager.removeMessageListener(message, listener);
       resolve(msg);
     };
 
     messageManager.addMessageListener(message, listener);
   })
 }
 
+add_task(function*(){
+  // This test is only relevant in e10s.
+  if (!gMultiProcessBrowser)
+    return;
+
+  ppmm.releaseCachedProcesses();
+
+  yield SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", 5]]})
+  yield SpecialPowers.pushPrefEnv({"set": [["dom.ipc.keepProcessesAlive.web", 5]]})
+
+  let tabs = [];
+  for (let i = 0; i < 3; i++) {
+    tabs[i] = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+  }
+
+  for (let i = 0; i < 3; i++) {
+    yield BrowserTestUtils.removeTab(tabs[i]);
+  }
+
+  ppmm.releaseCachedProcesses();
+  is(ppmm.childCount, 3, "Should get back to 3 processes at this point.");
+})
+
 // Test that loading a process script loads in all existing processes
 add_task(function*() {
   let checks = [];
   for (let i = 0; i < ppmm.childCount; i++)
     checks.push(checkProcess(ppmm.getChildAt(i)));
 
   ppmm.loadProcessScript(processScriptURL, false);
   yield Promise.all(checks);
 });
 
 // Test that loading a process script loads in new processes
 add_task(function*() {
   // This test is only relevant in e10s
   if (!gMultiProcessBrowser)
     return;
 
-  is(ppmm.childCount, 2, "Should be two processes at this point");
+  is(ppmm.childCount, 3, "Should be three processes at this point");
 
   // Load something in the main process
   gBrowser.selectedBrowser.loadURI("about:robots");
   yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
 
   let init = ppmm.initialProcessData;
   init.test123 = "hello";
   init.test456 = new Map();
   init.test456.set("hi", "bye");
 
   // With no remote frames left we should be down to one process.
   // However, stuff like remote thumbnails can cause a content
   // process to exist nonetheless. This should be rare, though,
   // so the test is useful most of the time.
-  if (ppmm.childCount == 1) {
+  if (ppmm.childCount == 2) {
     let mainMM = ppmm.getChildAt(0);
 
     let check = checkProcess(ppmm);
     ppmm.loadProcessScript(processScriptURL, true);
 
     // The main process should respond
     yield check;
 
     check = checkProcess(ppmm);
     // Reset the default browser to start a new child process
     gBrowser.updateBrowserRemoteness(gBrowser.selectedBrowser, true);
     gBrowser.selectedBrowser.loadURI("about:blank");
     yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
 
-    is(ppmm.childCount, 2, "Should be back to two processes at this point");
+    is(ppmm.childCount, 3, "Should be back to three processes at this point");
 
     // The new process should have responded
     yield check;
 
     ppmm.removeDelayedProcessScript(processScriptURL);
 
     let childMM;
-    childMM = ppmm.getChildAt(0) == mainMM ? ppmm.getChildAt(1) : ppmm.getChildAt(0);
+    childMM = ppmm.getChildAt(2);
 
     childMM.loadProcessScript(initTestScriptURL, false);
     let msg = yield promiseMessage(childMM, "ProcessTest:InitGood");
     is(msg.data, "bye", "initial process data was correct");
   } else {
     info("Unable to finish test entirely");
   }
 });
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -2559,17 +2559,19 @@ Migrate(mozIStorageConnection* aConn)
 #if defined(DEBUG) || !defined(RELEASE_OR_BETA)
     int32_t lastVersion = currentVersion;
 #endif
     rv = aConn->GetSchemaVersion(&currentVersion);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     MOZ_DIAGNOSTIC_ASSERT(currentVersion > lastVersion);
   }
 
-  MOZ_DIAGNOSTIC_ASSERT(currentVersion == kLatestSchemaVersion);
+  // Don't release assert this since people do sometimes share profiles
+  // across schema versions.  Our check in Validate() will catch it.
+  MOZ_ASSERT(currentVersion == kLatestSchemaVersion);
 
   if (rewriteSchema) {
     // Now overwrite the master SQL for the entries table to remove the column
     // default value.  This is also necessary for our Validate() method to
     // pass on this database.
     rv = RewriteEntriesSchema(aConn);
   }
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -645,16 +645,42 @@ ContentParent::GetMaxProcessCount(const 
 }
 
 /*static*/ bool
 ContentParent::IsMaxProcessCountReached(const nsAString& aContentProcessType)
 {
   return GetPoolSize(aContentProcessType) >= GetMaxProcessCount(aContentProcessType);
 }
 
+/*static*/ void
+ContentParent::ReleaseCachedProcesses()
+{
+  // We might want to extend this for other process types as well in the future...
+  nsTArray<ContentParent*>& contentParents = GetOrCreatePool(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE));
+  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+  nsTArray<ContentParent*> toRelease;
+
+  // Shuting down these processes will change the array so let's use another array for the removal.
+  for (auto* cp : contentParents) {
+    nsTArray<TabId> tabIds = cpm->GetTabParentsByProcessId(cp->mChildID);
+    if (!tabIds.Length()) {
+      toRelease.AppendElement(cp);
+    }
+  }
+
+  for (auto* cp : toRelease) {
+    // Start a soft shutdown.
+    cp->ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
+    // Make sure we don't select this process for new tabs.
+    cp->MarkAsDead();
+    // Make sure that this process is no longer accessible from JS by its message manager.
+    cp->ShutDownMessageManager();
+  }
+}
+
 /*static*/ already_AddRefed<ContentParent>
 ContentParent::RandomSelect(const nsTArray<ContentParent*>& aContentParents,
                             ContentParent* aOpener, int32_t aMaxContentParents)
 {
   uint32_t maxSelectable = std::min(static_cast<uint32_t>(aContentParents.Length()),
                                     static_cast<uint32_t>(aMaxContentParents));
   uint32_t startIdx = rand() % maxSelectable;
   uint32_t currIdx = startIdx;
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -148,16 +148,18 @@ public:
   static void JoinAllSubprocesses();
 
   static uint32_t GetPoolSize(const nsAString& aContentProcessType);
 
   static uint32_t GetMaxProcessCount(const nsAString& aContentProcessType);
 
   static bool IsMaxProcessCountReached(const nsAString& aContentProcessType);
 
+  static void ReleaseCachedProcesses();
+
   /**
    * Picks a random content parent from |aContentParents| with a given |aOpener|
    * respecting the index limit set by |aMaxContentParents|.
    * Returns null if non available.
    */
   static already_AddRefed<ContentParent>
   RandomSelect(const nsTArray<ContentParent*>& aContentParents,
                ContentParent* aOpener,
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2609,17 +2609,17 @@ TabChild::InitRenderingState(const Textu
     ShadowLayerForwarder* lf =
         mPuppetWidget->GetLayerManager(
             nullptr, mTextureFactoryIdentifier.mParentBackend)
                 ->AsShadowForwarder();
 
     LayerManager* lm = mPuppetWidget->GetLayerManager();
     if (lm->AsWebRenderLayerManager()) {
       lm->AsWebRenderLayerManager()->Initialize(compositorChild,
-                                                aLayersId,
+                                                wr::AsPipelineId(aLayersId),
                                                 &mTextureFactoryIdentifier);
       ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier);
       gfx::VRManagerChild::IdentifyTextureHost(mTextureFactoryIdentifier);
       InitAPZState();
     }
 
     if (lf) {
       nsTArray<LayersBackend> backends;
--- a/dom/plugins/test/reftest/reftest.list
+++ b/dom/plugins/test/reftest/reftest.list
@@ -16,12 +16,12 @@ fails-if(!haveTestPlugin) fuzzy-if(skiaC
 fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,160000) fails-if(webrender) == plugin-transform-alpha-zindex.html div-alpha-zindex.html
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,160000) fails-if(webrender) == plugin-busy-alpha-zindex.html div-alpha-zindex.html
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) fails-if(webrender) == plugin-background.html plugin-background-ref.html
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) fails-if(webrender) == plugin-background-1-step.html plugin-background-ref.html
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) fails-if(webrender) == plugin-background-2-step.html plugin-background-ref.html
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) fails-if(webrender) == plugin-background-5-step.html plugin-background-ref.html
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) fails-if(webrender) == plugin-background-10-step.html plugin-background-ref.html
 random-if(!haveTestPlugin) == plugin-transform-1.html plugin-transform-1-ref.html
-fails-if(!haveTestPlugin) fails-if(webrender) == plugin-transform-2.html plugin-transform-2-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/dom/tests/browser/browser_localStorage_e10s.js
+++ b/dom/tests/browser/browser_localStorage_e10s.js
@@ -236,18 +236,26 @@ add_task(function*() {
   //
   // The first option turns out to be hard to get right.  Specifically,
   // although one can set the keepalive and process counts to 1 and open and
   // close tabs to try and trigger process termination down to 1, since we don't
   // know how many processes might exist, we can't reliably listen for observer
   // notifications of their shutdown to ensure we're avoiding shutdown races.
   // (If there are races then the processes won't actually be shut down.)  So
   // it's easiest to just boost the limit.
-  let keepAliveCount =
-    SpecialPowers.getIntPref("dom.ipc.keepProcessesAlive.web", 1);
+  let keepAliveCount = 0;
+  try {
+    // This will throw if the preference is not defined, leaving our value at 0.
+    // Alternately, we could use Preferences.jsm's Preferences.get() API which
+    // supports default values, but we're sticking with SpecialPowers here for
+    // consistency.
+    keepAliveCount = SpecialPowers.getIntPref("dom.ipc.keepProcessesAlive.web");
+  } catch (ex) {
+    // Then zero is correct.
+  }
   let safeProcessCount = keepAliveCount + 6;
   info("dom.ipc.keepProcessesAlive.web is " + keepAliveCount + ", boosting " +
        "process count temporarily to " + safeProcessCount);
 
   // (There's already one about:blank page open and we open 5 new tabs, so 6
   // processes.  Actually, 7, just in case.)
   yield SpecialPowers.pushPrefEnv({
     set: [
--- a/gfx/2d/ScaledFontMac.h
+++ b/gfx/2d/ScaledFontMac.h
@@ -45,16 +45,17 @@ public:
   virtual ~ScaledFontMac();
 
   virtual FontType GetType() const { return FontType::MAC; }
 #ifdef USE_SKIA
   virtual SkTypeface* GetSkTypeface();
 #endif
   virtual already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
   virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton);
+  virtual bool CanSerialize() { return true; }
 
 #ifdef USE_CAIRO_SCALED_FONT
   cairo_font_face_t* GetCairoFontFace();
 #endif
 
 private:
   friend class DrawTargetSkia;
   CGFontRef mFont;
--- a/gfx/doc/README.displayitem
+++ b/gfx/doc/README.displayitem
@@ -2,26 +2,27 @@ How to add a new WebRender display item 
 
 (1) Force layout to create a new active layer for the gecko display item.
 (2) Plumb the data needed for the display item from content through WebRenderBridgeParent on the parent side.
 (3) From WebRenderBridgeParent, call out into bindings.rs and implement the appropriate WR calls.
 
 More detailed steps are:
 
 1) Force layout to create an active layer for the gecko display item.
-See http://searchfox.org/mozilla-central/rev/0f254a30d684796bcc8b6e2a102a0095d25842bb/layout/generic/nsTextFrame.cpp#4983
-as an example for text layers. Ping Matt Woodrow or Markus Stange for help.
+See http://searchfox.org/mozilla-central/source/layout/painting/nsDisplayList.h#1850
 
-The Active layer part comes from nsDisplayText::GetLayerState
+For most items, we should just be creating a DisplayItemLayer. The DisplayItemLayer has a pointer to the nsDisplayItem. To layerize, just return an active layer for the current item.
 
-2) Create the new display item layer:
+See https://hg.mozilla.org/projects/graphics/file/c8873c351679e4a394170cd899e8b5a5fb2a00e7/layout/painting/nsDisplayList.cpp#l4403
 
-See text layer:
-http://searchfox.org/mozilla-central/rev/0f254a30d684796bcc8b6e2a102a0095d25842bb/gfx/layers/Layers.h#2403
+Thus for most display items, it's just implementing:
+GetLayerState,
+BuildLayer (which should just call BuildDisplayItemLayer)
+CreateWebRenderCommands.
 
-The layer should have all the information to display the item.
+2) When implementing CreateWebRenderCommands, take a look at the nsDisplayItem::Paint method and reproduce the logic in webrender display items.
 
-3) Create the WebRender equivalent layer. In YourLayerType::RenderLayer, serialize the data needed for the layer type.
-4) If you need to add a custom IPC serialization mechanism, do it in WebRenderMessageUtils.h
-5) Create a WebRender command to process the new layer type in WebRenderMessages.ipdlh. These are the struct OpDPPushYourLayerTypeHere
-6) Add a new function in WebRender.h that will call out into webrender to render your display item.
-7) In WebRenderBridgeParent::ProcessWebRenderCommands, call out to the new function in (6).
-8) Fill out the function in (6) in bindings.rs to make webrender do the right thing. Generally, it's just push a display item.
+If you need to implement a new WebRender display item (generally shouldn't be needed):
+1) If you need to pipe in a new WR display item type, do it in WebRenderMessages.ipdl.
+2) If you need to add a custom IPC serialization mechanism, do it in WebRenderMessageUtils.h
+3) Add a new function in WebRender.h that will call out into webrender for a new WR display item.
+4) In WebRenderBridgeParent::ProcessWebRenderCommands, call out to the new function in (3).
+5) Fill out the function in (3) in bindings.rs to make webrender do the right thing.
--- 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: bcf3c371086894f5e1d098ee60f0592abf01f6b3
+Latest Commit: 938b32ca93bf5e878422ac4bafcdd53f8058f880
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -130,17 +130,16 @@ class CompositingRenderTarget;
 class CompositorBridgeParent;
 class LayerManagerComposite;
 class CompositorOGL;
 class CompositorD3D9;
 class CompositorD3D11;
 class BasicCompositor;
 class TextureHost;
 class TextureReadLock;
-class WebRenderCompositorOGL;
 
 enum SurfaceInitMode
 {
   INIT_MODE_NONE,
   INIT_MODE_CLEAR
 };
 
 /**
@@ -469,17 +468,16 @@ public:
 #endif // MOZ_DUMP_PAINTING
 
   virtual LayersBackend GetBackendType() const = 0;
 
   virtual CompositorOGL* AsCompositorOGL() { return nullptr; }
   virtual CompositorD3D9* AsCompositorD3D9() { return nullptr; }
   virtual CompositorD3D11* AsCompositorD3D11() { return nullptr; }
   virtual BasicCompositor* AsBasicCompositor() { return nullptr; }
-  virtual WebRenderCompositorOGL* AsWebRenderCompositorOGL() { return nullptr; }
 
   /**
    * Each Compositor has a unique ID.
    * This ID is used to keep references to each Compositor in a map accessed
    * from the compositor thread only, so that async compositables can find
    * the right compositor parent and schedule compositing even if the compositor
    * changed.
    */
--- a/gfx/layers/LayerTreeInvalidation.cpp
+++ b/gfx/layers/LayerTreeInvalidation.cpp
@@ -732,16 +732,17 @@ CloneLayerTreePropertiesInternal(Layer* 
     case Layer::TYPE_IMAGE:
       return MakeUnique<ImageLayerProperties>(static_cast<ImageLayer*>(aRoot), aIsMask);
     case Layer::TYPE_CANVAS:
       return MakeUnique<CanvasLayerProperties>(static_cast<CanvasLayer*>(aRoot));
     case Layer::TYPE_BORDER:
       return MakeUnique<BorderLayerProperties>(static_cast<BorderLayer*>(aRoot));
     case Layer::TYPE_TEXT:
       return MakeUnique<TextLayerProperties>(static_cast<TextLayer*>(aRoot));
+    case Layer::TYPE_DISPLAYITEM:
     case Layer::TYPE_READBACK:
     case Layer::TYPE_SHADOW:
     case Layer::TYPE_PAINTED:
       return MakeUnique<LayerPropertiesBase>(aRoot);
   }
 
   MOZ_ASSERT_UNREACHABLE("Unexpected root layer type");
   return MakeUnique<LayerPropertiesBase>(aRoot);
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -38,16 +38,17 @@
 #include "mozilla/layers/LayerManagerComposite.h"  // for LayerComposite
 #include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
 #include "mozilla/layers/LayersMessages.h"  // for TransformFunction, etc
 #include "mozilla/layers/LayersTypes.h"  // for TextureDumpMode
 #include "mozilla/layers/PersistentBufferProvider.h"
 #include "mozilla/layers/ShadowLayers.h"  // for ShadowableLayer
 #include "nsAString.h"
 #include "nsCSSValue.h"                 // for nsCSSValue::Array, etc
+#include "nsDisplayList.h"              // for nsDisplayItem
 #include "nsPrintfCString.h"            // for nsPrintfCString
 #include "nsStyleStruct.h"              // for nsTimingFunction, etc
 #include "protobuf/LayerScopePacket.pb.h"
 #include "mozilla/Compression.h"
 #include "TreeTraversal.h"              // for ForEachNode
 
 #include <deque>
 #include <set>
@@ -2116,16 +2117,44 @@ ContainerLayer::DumpPacket(layerscope::L
   Layer::DumpPacket(aPacket, aParent);
   // Get this layer data
   using namespace layerscope;
   LayersPacket::Layer* layer = aPacket->mutable_layer(aPacket->layer_size()-1);
   layer->set_type(LayersPacket::Layer::ContainerLayer);
 }
 
 void
+DisplayItemLayer::EndTransaction() {
+  mItem = nullptr;
+  mBuilder = nullptr;
+}
+
+void
+DisplayItemLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+  Layer::PrintInfo(aStream, aPrefix);
+  const char* type = "TYPE_UNKNOWN";
+  if (mItem) {
+    type = mItem->Name();
+  }
+
+  aStream << " [itype type=" << type << "]";
+}
+
+void
+DisplayItemLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+  Layer::DumpPacket(aPacket, aParent);
+  // Get this layer data
+  using namespace layerscope;
+  LayersPacket::Layer* layer = aPacket->mutable_layer(aPacket->layer_size()-1);
+  layer->set_type(LayersPacket::Layer::DisplayItemLayer);
+}
+
+void
 ColorLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   Layer::PrintInfo(aStream, aPrefix);
   AppendToString(aStream, mColor, " [color=", "]");
   AppendToString(aStream, mBounds, " [bounds=", "]");
 }
 
 void
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -48,16 +48,18 @@
 #include "nsTArray.h"                   // for nsTArray
 #include "nsTArrayForwardDeclare.h"     // for InfallibleTArray
 #include "nscore.h"                     // for nsACString, nsAString
 #include "mozilla/Logging.h"                      // for PRLogModuleInfo
 #include "nsIWidget.h"                  // For plugin window configuration information structs
 #include "ImageContainer.h"
 
 class gfxContext;
+class nsDisplayListBuilder;
+class nsDisplayItem;
 
 extern uint8_t gLayerManagerLayerBuilder;
 
 namespace mozilla {
 
 class ComputedTimingFunction;
 class FrameLayerBuilder;
 class StyleAnimationValue;
@@ -79,16 +81,17 @@ class AsyncPanZoomController;
 class BasicLayerManager;
 class ClientLayerManager;
 class HostLayerManager;
 class Layer;
 class LayerMetricsWrapper;
 class PaintedLayer;
 class ContainerLayer;
 class ImageLayer;
+class DisplayItemLayer;
 class ColorLayer;
 class CompositorBridgeChild;
 class TextLayer;
 class CanvasLayer;
 class BorderLayer;
 class ReadbackLayer;
 class ReadbackProcessor;
 class RefLayer;
@@ -437,18 +440,21 @@ public:
    * Create a ReadbackLayer for this manager's layer tree.
    */
   virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() { return nullptr; }
   /**
    * CONSTRUCTION PHASE ONLY
    * Create a RefLayer for this manager's layer tree.
    */
   virtual already_AddRefed<RefLayer> CreateRefLayer() { return nullptr; }
-
-
+  /**
+   * CONSTRUCTION PHASE ONLY
+   * Create a DisplayItemLayer for this manager's layer tree.
+   */
+  virtual already_AddRefed<DisplayItemLayer> CreateDisplayItemLayer() { return nullptr; }
   /**
    * Can be called anytime, from any thread.
    *
    * Creates an Image container which forwards its images to the compositor within
    * layer transactions on the main thread or asynchronously using the ImageBridge IPDL protocol.
    * In the case of asynchronous, If the protocol is not available, the returned ImageContainer
    * will forward images within layer transactions.
    */
@@ -780,16 +786,17 @@ class Layer {
   typedef InfallibleTArray<Animation> AnimationArray;
 
 public:
   // Keep these in alphabetical order
   enum LayerType {
     TYPE_CANVAS,
     TYPE_COLOR,
     TYPE_CONTAINER,
+    TYPE_DISPLAYITEM,
     TYPE_IMAGE,
     TYPE_TEXT,
     TYPE_BORDER,
     TYPE_READBACK,
     TYPE_REF,
     TYPE_SHADOW,
     TYPE_PAINTED
   };
@@ -1569,16 +1576,22 @@ public:
   virtual HostLayer* AsHostLayer() { return nullptr; }
 
   /**
    * Dynamic cast to a ShadowableLayer.  Return null if this is not a
    * ShadowableLayer.  Can be used anytime.
    */
   virtual ShadowableLayer* AsShadowableLayer() { return nullptr; }
 
+  /**
+   * Dynamic cast as a DisplayItemLayer. Return null if not a
+   * DisplayItemLayer. Can be used anytime.
+   */
+  virtual DisplayItemLayer* AsDisplayItemLayer() { return nullptr; }
+
   // These getters can be used anytime.  They return the effective
   // values that should be used when drawing this layer to screen,
   // accounting for this layer possibly being a shadow.
   const Maybe<ParentLayerIntRect>& GetLocalClipRect();
   const LayerIntRegion& GetLocalVisibleRegion();
 
   bool Extend3DContext() {
     return GetContentFlags() & CONTENT_EXTEND_3D_CONTEXT;
@@ -2304,16 +2317,71 @@ protected:
   bool mMayHaveReadbackChild;
   // This is updated by ComputeDifferences. This will be true if we need to invalidate
   // the intermediate surface.
   bool mChildrenChanged;
   EventRegionsOverride mEventRegionsOverride;
 };
 
 /**
+ * A generic layer that references back to its display item.
+ *
+ * In order to not throw away information early in the pipeline from layout -> webrender,
+ * we'd like a generic layer type that can represent all the nsDisplayItems instead of
+ * creating a new layer type for each nsDisplayItem for Webrender. Another option
+ * is to break down nsDisplayItems into smaller nsDisplayItems early in the pipeline.
+ * The problem with this is that the whole pipeline would have to deal with more
+ * display items, which is slower.
+ *
+ * An alternative is to create a DisplayItemLayer, but the wrinkle with this is that
+ * it has a pointer to its nsDisplayItem. Managing the lifetime is key as display items
+ * only live as long as their display list builder, which goes away at the end of a paint.
+ * Layers however, are retained between paints.
+ * It's ok to recycle a DisplayItemLayer for a different display item since its just a pointer.
+ * Instead, when a layer transaction is completed, it is up to the layer manager to tell
+ * DisplayItemLayers that the display item pointer is no longer valid.
+ */
+class DisplayItemLayer : public Layer {
+  public:
+    virtual DisplayItemLayer* AsDisplayItemLayer() override { return this; }
+    void EndTransaction();
+
+    MOZ_LAYER_DECL_NAME("DisplayItemLayer", TYPE_DISPLAYITEM)
+
+    void SetDisplayItem(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder) {
+      mItem = aItem;
+      mBuilder = aBuilder;
+    }
+
+    nsDisplayItem* GetDisplayItem() { return mItem; }
+    nsDisplayListBuilder* GetDisplayListBuilder() { return mBuilder; }
+
+    virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+    {
+      gfx::Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
+      mEffectiveTransform = SnapTransformTranslation(idealTransform, nullptr);
+      ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+    }
+
+  protected:
+    DisplayItemLayer(LayerManager* aManager, void* aImplData)
+      : Layer(aManager, aImplData)
+      , mItem(nullptr)
+  {}
+
+  virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+  virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
+
+  // READ COMMENT ABOVE TO ENSURE WE DON'T HAVE A DANGLING POINTER
+  nsDisplayItem* mItem;
+  nsDisplayListBuilder* mBuilder;
+};
+
+/**
  * A Layer which just renders a solid color in its visible region. It actually
  * can fill any area that contains the visible region, so if you need to
  * restrict the area filled, set a clip region on this layer.
  */
 class ColorLayer : public Layer {
 public:
   virtual ColorLayer* AsColorLayer() override { return this; }
 
new file mode 100644
--- /dev/null
+++ b/gfx/layers/basic/BasicDisplayItemLayer.cpp
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BasicLayersImpl.h"            // for FillRectWithMask, etc
+#include "Layers.h"                     // for Layer, etc
+#include "BasicImplData.h"              // for BasicImplData
+#include "BasicLayers.h"                // for BasicLayerManager
+#include "gfxContext.h"                 // for gfxContext, etc
+#include "gfxRect.h"                    // for gfxRect
+#include "gfx2DGlue.h"
+#include "mozilla/mozalloc.h"           // for operator new
+#include "nsCOMPtr.h"                   // for already_AddRefed
+#include "nsDebug.h"                    // for NS_ASSERTION
+#include "nsISupportsImpl.h"            // for Layer::AddRef, etc
+#include "nsRect.h"                     // for mozilla::gfx::IntRect
+#include "nsRegion.h"                   // for nsIntRegion
+#include "mozilla/gfx/PathHelpers.h"
+#include "nsDisplayList.h"              // for nsDisplayItem
+#include "nsCaret.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+class BasicDisplayItemLayer : public DisplayItemLayer, public BasicImplData {
+public:
+  explicit BasicDisplayItemLayer(BasicLayerManager* aLayerManager) :
+    DisplayItemLayer(aLayerManager, static_cast<BasicImplData*>(this))
+  {
+    MOZ_COUNT_CTOR(BasicDisplayItemLayer);
+  }
+
+protected:
+  virtual ~BasicDisplayItemLayer()
+  {
+    MOZ_COUNT_DTOR(BasicDisplayItemLayer);
+  }
+
+public:
+  virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+  {
+    NS_ASSERTION(BasicManager()->InConstruction(),
+                 "Can only set properties in construction phase");
+    DisplayItemLayer::SetVisibleRegion(aRegion);
+  }
+
+  virtual void Paint(DrawTarget* aDT,
+                     const gfx::Point& aDeviceOffset,
+                     Layer* aMaskLayer) override
+  {
+    if (IsHidden() || !mItem || !mBuilder) {
+      return;
+    }
+
+    AutoRestoreTransform autoRestoreTransform(aDT);
+    Matrix transform = aDT->GetTransform();
+    RefPtr<gfxContext> context = gfxContext::CreateOrNull(aDT, aDeviceOffset);
+    context->SetMatrix(ThebesMatrix(transform));
+
+    nsRenderingContext ctx(context);
+    mItem->Paint(mBuilder, &ctx);
+  }
+
+protected:
+  BasicLayerManager* BasicManager()
+  {
+    return static_cast<BasicLayerManager*>(mManager);
+  }
+};
+
+already_AddRefed<DisplayItemLayer>
+BasicLayerManager::CreateDisplayItemLayer()
+{
+  NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+  RefPtr<DisplayItemLayer> layer = new BasicDisplayItemLayer(this);
+  mDisplayItemLayers.AppendElement(layer);
+  return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -541,16 +541,28 @@ ApplyDoubleBuffering(Layer* aLayer, cons
 void
 BasicLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
                                   void* aCallbackData,
                                   EndTransactionFlags aFlags)
 {
   mInTransaction = false;
 
   EndTransactionInternal(aCallback, aCallbackData, aFlags);
+
+  ClearDisplayItemLayers();
+}
+
+void
+BasicLayerManager::ClearDisplayItemLayers()
+{
+  for (uint32_t i = 0; i < mDisplayItemLayers.Length(); i++) {
+    mDisplayItemLayers[i]->EndTransaction();
+  }
+
+  mDisplayItemLayers.Clear();
 }
 
 void
 BasicLayerManager::AbortTransaction()
 {
   NS_ASSERTION(InConstruction(), "Should be in construction phase");
   mPhase = PHASE_NONE;
   mUsingDefaultTarget = false;
@@ -729,17 +741,17 @@ BasicLayerManager::PaintSelfOrChildren(P
       if (layer->IsBackfaceHidden()) {
         continue;
       }
       if (!layer->AsContainerLayer() && !layer->IsVisible()) {
         continue;
       }
 
       PaintLayer(aGroupTarget, layer, aPaintContext.mCallback,
-          aPaintContext.mCallbackData);
+                aPaintContext.mCallbackData);
       if (mTransactionIncomplete)
         break;
     }
   }
 }
 
 void
 BasicLayerManager::FlushGroup(PaintLayerContext& aPaintContext, bool aNeedsClipToVisibleRegion)
@@ -811,17 +823,18 @@ BasicLayerManager::PaintLayer(gfxContext
                               DrawPaintedLayerCallback aCallback,
                               void* aCallbackData)
 {
   MOZ_ASSERT(aTarget);
 
   PROFILER_LABEL("BasicLayerManager", "PaintLayer",
     js::ProfileEntry::Category::GRAPHICS);
 
-  PaintLayerContext paintLayerContext(aTarget, aLayer, aCallback, aCallbackData);
+  PaintLayerContext paintLayerContext(aTarget, aLayer,
+                                      aCallback, aCallbackData);
 
   // Don't attempt to paint layers with a singular transform, cairo will
   // just throw an error.
   if (aLayer->GetEffectiveTransform().IsSingular()) {
     return;
   }
 
   RenderTraceScope trace("BasicLayerManager::PaintLayer", "707070");
--- a/gfx/layers/basic/BasicLayers.h
+++ b/gfx/layers/basic/BasicLayers.h
@@ -19,16 +19,17 @@
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nscore.h"                     // for nsAString, etc
 
 class nsIWidget;
 
 namespace mozilla {
 namespace layers {
 
+class DisplayItemLayer;
 class ImageFactory;
 class ImageLayer;
 class PaintLayerContext;
 class ReadbackLayer;
 
 /**
  * This is a cairo/Thebes-only, main-thread-only implementation of layers.
  * 
@@ -112,16 +113,17 @@ public:
   virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() override;
   virtual already_AddRefed<ContainerLayer> CreateContainerLayer() override;
   virtual already_AddRefed<ImageLayer> CreateImageLayer() override;
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
   virtual already_AddRefed<ColorLayer> CreateColorLayer() override;
   virtual already_AddRefed<TextLayer> CreateTextLayer() override;
   virtual already_AddRefed<BorderLayer> CreateBorderLayer() override;
   virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() override;
+  virtual already_AddRefed<DisplayItemLayer> CreateDisplayItemLayer() override;
   virtual ImageFactory *GetImageFactory();
 
   virtual LayersBackend GetBackendType() override { return LayersBackend::LAYERS_BASIC; }
   virtual void GetBackendName(nsAString& name) override { name.AssignLiteral("Basic"); }
 
   bool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
 #ifdef DEBUG
   bool InDrawing() { return mPhase == PHASE_DRAWING; }
@@ -206,14 +208,22 @@ protected:
   // Image factory we use.
   RefPtr<ImageFactory> mFactory;
 
   BufferMode mDoubleBuffering;
   BasicLayerManagerType mType;
   bool mUsingDefaultTarget;
   bool mTransactionIncomplete;
   bool mCompositorMightResample;
+
+private:
+  // Display items are only valid during this transaction.
+  // At the end of the transaction, we have to go and clear out
+  // DisplayItemLayer's and null their display item. See comment
+  // above DisplayItemLayer declaration.
+  void ClearDisplayItemLayers();
+  nsTArray<RefPtr<DisplayItemLayer>> mDisplayItemLayers;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif /* GFX_BASICLAYERS_H */
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -21,16 +21,18 @@
 #include "nsIObserver.h"                // for nsIObserver
 #include "nsISupportsImpl.h"            // for Layer::Release, etc
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
 #include "nsTArray.h"                   // for nsTArray
 #include "nscore.h"                     // for nsAString
 #include "mozilla/layers/TransactionIdAllocator.h"
 #include "nsIWidget.h"                  // For plugin window configuration information structs
 
+class nsDisplayListBuilder;
+
 namespace mozilla {
 namespace layers {
 
 class ClientPaintedLayer;
 class CompositorBridgeChild;
 class ImageLayer;
 class FrameUniformityData;
 
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -29,16 +29,17 @@
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/gfx/2D.h"          // for DrawTarget
 #include "mozilla/gfx/GPUChild.h"       // for GfxPrefValue
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/gfx/Rect.h"          // for IntSize
 #include "mozilla/gfx/gfxVars.h"        // for gfxVars
 #include "VRManager.h"                  // for VRManager
 #include "mozilla/ipc/Transport.h"      // for Transport
+#include "mozilla/gfx/gfxVars.h"
 #include "mozilla/layers/APZCTreeManager.h"  // for APZCTreeManager
 #include "mozilla/layers/APZCTreeManagerParent.h"  // for APZCTreeManagerParent
 #include "mozilla/layers/APZThreadUtils.h"  // for APZCTreeManager
 #include "mozilla/layers/AsyncCompositionManager.h"
 #include "mozilla/layers/BasicCompositor.h"  // for BasicCompositor
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorOGL.h"  // for CompositorOGL
 #include "mozilla/layers/CompositorThread.h"
@@ -49,17 +50,16 @@
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayerTreeOwnerTracker.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/layers/PLayerTransactionParent.h"
 #include "mozilla/layers/RemoteContentController.h"
 #include "mozilla/layers/WebRenderBridgeParent.h"
 #include "mozilla/layers/WebRenderCompositableHolder.h"
-#include "mozilla/layers/WebRenderCompositorOGL.h"
 #include "mozilla/layout/RenderFrameParent.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 #include "mozilla/media/MediaSystemResourceService.h" // for MediaSystemResourceService
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "mozilla/Telemetry.h"
 #ifdef MOZ_WIDGET_GTK
 #include "basic/X11BasicCompositor.h" // for X11BasicCompositor
 #endif
--- a/gfx/layers/ipc/PWebRenderBridge.ipdl
+++ b/gfx/layers/ipc/PWebRenderBridge.ipdl
@@ -32,18 +32,17 @@ parent:
 
   sync Create(IntSize aSize);
   sync AddImage(IntSize aSize, uint32_t aStride,
                 SurfaceFormat aFormat, ByteBuffer aBytes)
     returns (ImageKey aOutImageKey);
   sync UpdateImage(ImageKey aImageKey, IntSize aSize,
                    SurfaceFormat aFormat, ByteBuffer aBytes);
   sync DeleteImage(ImageKey aImageKey);
-  sync DPBegin(IntSize aSize)
-    returns (bool aOutSuccess);
+  async DPBegin(IntSize aSize);
   async DPEnd(WebRenderCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId);
   sync DPSyncEnd(WebRenderCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId);
   sync DPGetSnapshot(PTexture texture);
   async AddExternalImageId(uint64_t aImageId, CompositableHandle aHandle);
   async AddExternalImageIdForCompositable(uint64_t aImageId, CompositableHandle aHandle);
   async RemoveExternalImageId(uint64_t aImageId);
   async SetLayerObserverEpoch(uint64_t layerObserverEpoch);
   async ClearCachedResources();
--- a/gfx/layers/ipc/UiCompositorControllerChild.cpp
+++ b/gfx/layers/ipc/UiCompositorControllerChild.cpp
@@ -5,19 +5,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "UiCompositorControllerChild.h"
 #include "UiCompositorControllerParent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/StaticPtr.h"
 #include "nsThreadUtils.h"
+#include "mozilla/gfx/GPUProcessManager.h"
 
 namespace mozilla {
 namespace layers {
+using namespace gfx;
 
 static bool sInitialized = false;
 static StaticRefPtr<UiCompositorControllerChild> sChild;
 static StaticRefPtr<UiCompositorControllerParent> sParent;
 
 namespace {
 
 struct SurfaceResizeCache {
--- a/gfx/layers/ipc/WebRenderMessages.ipdlh
+++ b/gfx/layers/ipc/WebRenderMessages.ipdlh
@@ -7,26 +7,30 @@
 
 include LayersSurfaces;
 include LayersMessages;
 include protocol PTexture;
 
 using WrBorderRadius from "mozilla/webrender/webrender_ffi.h";
 using WrBorderSide from "mozilla/webrender/webrender_ffi.h";
 using WrColor from "mozilla/webrender/webrender_ffi.h";
-using WrImageKey from "mozilla/webrender/webrender_ffi.h";
-using WrTextureFilter from "mozilla/webrender/webrender_ffi.h";
 using WrLayoutSize from "mozilla/webrender/webrender_ffi.h";
 using WrRect from "mozilla/webrender/webrender_ffi.h";
+using 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";
 using mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
 using mozilla::wr::ByteBuffer from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::ImageRendering from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::ImageKey from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::LayerIntRegion from "Units.h";
 
 namespace mozilla {
 namespace layers {
 
 struct OpDPPushStackingContext {
   WrRect bounds;
   WrRect overflow;
@@ -35,46 +39,75 @@ struct OpDPPushStackingContext {
   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;
-  WrTextureFilter filter;
-  WrImageKey key;
+  ImageRendering filter;
+  ImageKey key;
 };
 
 struct OpDPPushExternalImageId {
   LayerIntRegion validBufferRegion;
   WrRect bounds;
   WrRect clip;
   MaybeImageMask mask;
-  WrTextureFilter filter;
+  ImageRendering filter;
   uint64_t externalImageId;
 };
 
 struct OpDPPushIframe {
   WrRect bounds;
   WrRect clip;
   PipelineId pipelineId;
 };
@@ -84,22 +117,39 @@ struct OpDPPushText {
   WrRect clip;
   WrGlyphArray[] glyph_array;
   uint32_t font_index;
   float glyph_size;
   ByteBuffer font_buffer;
   uint32_t font_buffer_length;
 };
 
+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;
   OpDPPushExternalImageId;
   OpDPPushIframe;
   OpDPPushText;
+  OpDPPushBoxShadow;
   CompositableOperation;
 };
 
 } // namespace
 } // namespace
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -204,17 +204,17 @@ EXPORTS.mozilla.layers += [
     'RenderTrace.h',
     'SourceSurfaceSharedData.h',
     'SourceSurfaceVolatileData.h',
     'TextureWrapperImage.h',
     'TransactionIdAllocator.h',
     'wr/WebRenderBridgeChild.h',
     'wr/WebRenderBridgeParent.h',
     'wr/WebRenderCompositableHolder.h',
-    'wr/WebRenderCompositorOGL.h',
+    'wr/WebRenderDisplayItemLayer.h',
     'wr/WebRenderImageHost.h',
     'wr/WebRenderLayerManager.h',
     'wr/WebRenderLayersLogging.h',
     'wr/WebRenderMessageUtils.h',
 ]
 
 if CONFIG['MOZ_X11']:
     EXPORTS.mozilla.layers += [
@@ -287,16 +287,17 @@ UNIFIED_SOURCES += [
     'AsyncCanvasRenderer.cpp',
     'AxisPhysicsModel.cpp',
     'AxisPhysicsMSDModel.cpp',
     'basic/BasicBorderLayer.cpp',
     'basic/BasicCanvasLayer.cpp',
     'basic/BasicColorLayer.cpp',
     'basic/BasicCompositor.cpp',
     'basic/BasicContainerLayer.cpp',
+    'basic/BasicDisplayItemLayer.cpp',
     'basic/BasicImages.cpp',
     'basic/BasicLayerManager.cpp',
     'basic/BasicLayersImpl.cpp',
     'basic/BasicPaintedLayer.cpp',
     'basic/BasicTextLayer.cpp',
     'basic/TextureHostBasic.cpp',
     'BSPTree.cpp',
     'BufferTexture.cpp',
@@ -389,18 +390,18 @@ UNIFIED_SOURCES += [
     'SourceSurfaceVolatileData.cpp',
     'TextureWrapperImage.cpp',
     'wr/WebRenderBorderLayer.cpp',
     'wr/WebRenderBridgeChild.cpp',
     'wr/WebRenderBridgeParent.cpp',
     'wr/WebRenderCanvasLayer.cpp',
     'wr/WebRenderColorLayer.cpp',
     'wr/WebRenderCompositableHolder.cpp',
-    'wr/WebRenderCompositorOGL.cpp',
     'wr/WebRenderContainerLayer.cpp',
+    'wr/WebRenderDisplayItemLayer.cpp',
     'wr/WebRenderImageHost.cpp',
     'wr/WebRenderImageLayer.cpp',
     'wr/WebRenderLayerManager.cpp',
     'wr/WebRenderLayersLogging.cpp',
     'wr/WebRenderPaintedLayer.cpp',
     'wr/WebRenderTextLayer.cpp',
 ]
 
--- a/gfx/layers/protobuf/LayerScopePacket.pb.h
+++ b/gfx/layers/protobuf/LayerScopePacket.pb.h
@@ -69,17 +69,18 @@ enum LayersPacket_Layer_LayerType {
   LayersPacket_Layer_LayerType_LayerManager = 1,
   LayersPacket_Layer_LayerType_ContainerLayer = 2,
   LayersPacket_Layer_LayerType_PaintedLayer = 3,
   LayersPacket_Layer_LayerType_CanvasLayer = 4,
   LayersPacket_Layer_LayerType_ImageLayer = 5,
   LayersPacket_Layer_LayerType_ColorLayer = 6,
   LayersPacket_Layer_LayerType_TextLayer = 7,
   LayersPacket_Layer_LayerType_RefLayer = 8,
-  LayersPacket_Layer_LayerType_ReadbackLayer = 9
+  LayersPacket_Layer_LayerType_ReadbackLayer = 9,
+  LayersPacket_Layer_LayerType_DisplayItemLayer = 10
 };
 bool LayersPacket_Layer_LayerType_IsValid(int value);
 const LayersPacket_Layer_LayerType LayersPacket_Layer_LayerType_LayerType_MIN = LayersPacket_Layer_LayerType_UnknownLayer;
 const LayersPacket_Layer_LayerType LayersPacket_Layer_LayerType_LayerType_MAX = LayersPacket_Layer_LayerType_ReadbackLayer;
 const int LayersPacket_Layer_LayerType_LayerType_ARRAYSIZE = LayersPacket_Layer_LayerType_LayerType_MAX + 1;
 
 enum LayersPacket_Layer_ScrollingDirect {
   LayersPacket_Layer_ScrollingDirect_VERTICAL = 1,
@@ -1669,16 +1670,17 @@ class LayersPacket_Layer : public ::goog
   typedef LayersPacket_Layer_LayerType LayerType;
   static const LayerType UnknownLayer = LayersPacket_Layer_LayerType_UnknownLayer;
   static const LayerType LayerManager = LayersPacket_Layer_LayerType_LayerManager;
   static const LayerType ContainerLayer = LayersPacket_Layer_LayerType_ContainerLayer;
   static const LayerType PaintedLayer = LayersPacket_Layer_LayerType_PaintedLayer;
   static const LayerType CanvasLayer = LayersPacket_Layer_LayerType_CanvasLayer;
   static const LayerType ImageLayer = LayersPacket_Layer_LayerType_ImageLayer;
   static const LayerType ColorLayer = LayersPacket_Layer_LayerType_ColorLayer;
+  static const LayerType DisplayItemLayer = LayersPacket_Layer_LayerType_DisplayItemLayer;
   static const LayerType TextLayer = LayersPacket_Layer_LayerType_TextLayer;
   static const LayerType RefLayer = LayersPacket_Layer_LayerType_RefLayer;
   static const LayerType ReadbackLayer = LayersPacket_Layer_LayerType_ReadbackLayer;
   static inline bool LayerType_IsValid(int value) {
     return LayersPacket_Layer_LayerType_IsValid(value);
   }
   static const LayerType LayerType_MIN =
     LayersPacket_Layer_LayerType_LayerType_MIN;
--- a/gfx/layers/wr/WebRenderBorderLayer.cpp
+++ b/gfx/layers/wr/WebRenderBorderLayer.cpp
@@ -1,43 +1,53 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebRenderBorderLayer.h"
 
-#include "WebRenderLayersLogging.h"
 #include "gfxPrefs.h"
+#include "LayersLogging.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/webrender/webrender_ffi.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
 void
 WebRenderBorderLayer::RenderLayer()
 {
   WrScrollFrameStackingContextGenerator scrollFrames(this);
 
-  Rect rect = RelativeToVisible(mRect.ToUnknownRect());
+  LayerIntRect bounds = GetVisibleRegion().GetBounds();
+  Rect rect(0, 0, bounds.width, bounds.height);
   Rect clip;
+  Matrix4x4 transform = GetTransform();
   if (GetClipRect().isSome()) {
-    clip = RelativeToTransformedVisible(IntRectToRect(GetClipRect().ref().ToUnknownRect()));
+    clip = RelativeToVisible(transform.Inverse().TransformBounds(IntRectToRect(GetClipRect().ref().ToUnknownRect())));
   } else {
     clip = rect;
   }
 
-  Rect relBounds = TransformedVisibleBoundsRelativeToParent();
+  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);
-  Matrix4x4 transform;// = GetTransform();
 
   if (gfxPrefs::LayersDump()) {
     printf_stderr("BorderLayer %p using bounds=%s, overflow=%s, transform=%s, rect=%s, clip=%s\n",
                   this->GetLayer(),
                   Stringify(relBounds).c_str(),
                   Stringify(overflow).c_str(),
                   Stringify(transform).c_str(),
                   Stringify(rect).c_str(),
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -43,28 +43,31 @@ WebRenderBridgeChild::ActorDestroy(Actor
 
 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);
+}
+
 bool
 WebRenderBridgeChild::DPBegin(const gfx::IntSize& aSize)
 {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(!mIsInTransaction);
-  bool success = false;
+
   UpdateFwdTransactionId();
-  this->SendDPBegin(aSize, &success);
-  if (!success) {
-    return false;
-  }
-
+  this->SendDPBegin(aSize);
   mIsInTransaction = true;
   return true;
 }
 
 void
 WebRenderBridgeChild::DPEnd(bool aIsSync, uint64_t aTransactionId)
 {
   MOZ_ASSERT(!mDestroyed);
--- a/gfx/layers/wr/WebRenderBridgeChild.h
+++ b/gfx/layers/wr/WebRenderBridgeChild.h
@@ -26,16 +26,17 @@ class WebRenderBridgeChild final : publi
                                  , 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);
 
   bool DPBegin(const  gfx::IntSize& aSize);
   void DPEnd(bool aIsSync, uint64_t aTransactionId);
 
   CompositorBridgeChild* GetCompositorBridgeChild();
 
   // KnowsCompositor
   TextureForwarder* GetTextureForwarder() override;
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -13,17 +13,16 @@
 #include "mozilla/layers/Compositor.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/CompositorVsyncScheduler.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/ImageDataSerializer.h"
 #include "mozilla/layers/TextureHost.h"
 #include "mozilla/layers/WebRenderCompositableHolder.h"
-#include "mozilla/layers/WebRenderCompositorOGL.h"
 #include "mozilla/webrender/RenderThread.h"
 #include "mozilla/widget/CompositorWidget.h"
 
 bool is_in_compositor_thread()
 {
   return mozilla::layers::CompositorThreadHolder::IsInCompositorThread();
 }
 
@@ -145,59 +144,58 @@ WebRenderBridgeParent::RecvAddImage(cons
                                     const gfx::SurfaceFormat& aFormat,
                                     const ByteBuffer& aBuffer,
                                     wr::ImageKey* aOutImageKey)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
   MOZ_ASSERT(mApi);
-  *aOutImageKey = mApi->AddImageBuffer(aSize, aStride, aFormat,
+  wr::ImageDescriptor descriptor(aSize, aStride, aFormat);
+  *aOutImageKey = mApi->AddImageBuffer(descriptor,
                                        aBuffer.AsSlice());
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvUpdateImage(const wr::ImageKey& aImageKey,
                                        const gfx::IntSize& aSize,
                                        const gfx::SurfaceFormat& aFormat,
                                        const ByteBuffer& aBuffer)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
   MOZ_ASSERT(mApi);
-  mApi->UpdateImageBuffer(aImageKey, aSize, aFormat, aBuffer.AsSlice());
+  wr::ImageDescriptor descriptor(aSize, aFormat);
+  mApi->UpdateImageBuffer(aImageKey, descriptor, aBuffer.AsSlice());
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvDeleteImage(const wr::ImageKey& aImageKey)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
   MOZ_ASSERT(mApi);
   mKeysToDelete.push_back(aImageKey);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-WebRenderBridgeParent::RecvDPBegin(const gfx::IntSize& aSize,
-                                   bool* aOutSuccess)
+WebRenderBridgeParent::RecvDPBegin(const gfx::IntSize& aSize)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
   MOZ_ASSERT(mBuilder.isSome());
   mBuilder.ref().Begin(LayerIntSize(aSize.width, aSize.height));
-  *aOutSuccess = true;
-
   return IPC_OK();
 }
 
 void
 WebRenderBridgeParent::HandleDPEnd(InfallibleTArray<WebRenderCommand>&& aCommands,
                                  InfallibleTArray<OpDestroy>&& aToDestroy,
                                  const uint64_t& aFwdTransactionId,
                                  const uint64_t& aTransactionId)
@@ -209,17 +207,17 @@ WebRenderBridgeParent::HandleDPEnd(Infal
       DestroyActor(op);
     }
     return;
   }
   // This ensures that destroy operations are always processed. It is not safe
   // to early-return from RecvDPEnd without doing so.
   AutoWebRenderBridgeParentAsyncMessageSender autoAsyncMessageSender(this, &aToDestroy);
 
-  ProcessWebrenderCommands(aCommands, wr::Epoch(aTransactionId));
+  ProcessWebrenderCommands(aCommands, wr::NewEpoch(aTransactionId));
 
   // The transaction ID might get reset to 1 if the page gets reloaded, see
   // https://bugzilla.mozilla.org/show_bug.cgi?id=1145295#c41
   // Otherwise, it should be continually increasing.
   MOZ_ASSERT(aTransactionId == 1 || aTransactionId > mPendingTransactionId);
   mPendingTransactionId = aTransactionId;
 }
 
@@ -259,28 +257,52 @@ WebRenderBridgeParent::ProcessWebrenderC
         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::TOpDPPushExternalImageId: {
         const OpDPPushExternalImageId& op = cmd.get_OpDPPushExternalImageId();
@@ -316,19 +338,20 @@ WebRenderBridgeParent::ProcessWebrenderC
           dSurf = surf->GetDataSurface();
         }
 
         DataSourceSurface::MappedSurface map;
         if (!dSurf->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
           break;
         }
 
+        wr::ImageDescriptor descriptor(validRect.Size(), map.mStride, SurfaceFormat::B8G8R8A8);
         wr::ImageKey key;
         auto slice = Range<uint8_t>(map.mData, validRect.height * map.mStride);
-        key = mApi->AddImageBuffer(validRect.Size(), map.mStride, SurfaceFormat::B8G8R8A8, slice);
+        key = mApi->AddImageBuffer(descriptor, slice);
 
         builder.PushImage(op.bounds(), op.clip(), op.mask().ptrOr(nullptr), op.filter(), key);
         keysToDelete.push_back(key);
         dSurf->Unmap();
         // XXX workaround for releasing Readlock. See Bug 1339625
         if(host->GetType() == CompositableType::CONTENT_SINGLE) {
           host->CleanupResources();
         }
@@ -344,49 +367,62 @@ WebRenderBridgeParent::ProcessWebrenderC
           NS_ERROR("ReceiveCompositableUpdate failed");
         }
         break;
       }
       case WebRenderCommand::TOpDPPushText: {
         const OpDPPushText& op = cmd.get_OpDPPushText();
         const nsTArray<WrGlyphArray>& glyph_array = op.glyph_array();
 
+        // TODO: We are leaking the key
+        wr::FontKey fontKey;
+        auto slice = Range<uint8_t>(op.font_buffer().mData, op.font_buffer_length());
+        fontKey = mApi->AddRawFont(slice);
+
         for (size_t i = 0; i < glyph_array.Length(); i++) {
           const nsTArray<WrGlyphInstance>& glyphs = glyph_array[i].glyphs;
-
-          // TODO: We are leaking the key
-          wr::FontKey fontKey;
-          auto slice = Range<uint8_t>(op.font_buffer().mData, op.font_buffer_length());
-          fontKey = mApi->AddRawFont(slice);
           builder.PushText(op.bounds(),
                            op.clip(),
                            glyph_array[i].color,
                            fontKey,
                            Range<const WrGlyphInstance>(glyphs.Elements(), glyphs.Length()),
                            op.glyph_size());
         }
 
         break;
       }
+      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(*mApi, aEpoch);
 
   ScheduleComposition();
   DeleteOldImages();
 
   // XXX remove it when external image key is used.
   if (!keysToDelete.empty()) {
     mKeysToDelete.swap(keysToDelete);
   }
 
   if (ShouldParentObserveEpoch()) {
-    mCompositorBridge->ObserveLayerUpdate(mPipelineId.mHandle, GetChildLayerObserverEpoch(), true);
+    mCompositorBridge->ObserveLayerUpdate(wr::AsUint64(mPipelineId), GetChildLayerObserverEpoch(), true);
   }
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvDPGetSnapshot(PTextureParent* aTexture)
 {
   if (mDestroyed) {
     return IPC_OK();
@@ -502,17 +538,17 @@ WebRenderBridgeParent::RecvSetLayerObser
 {
   mChildLayerObserverEpoch = aLayerObserverEpoch;
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvClearCachedResources()
 {
-  mCompositorBridge->ObserveLayerUpdate(mPipelineId.mHandle, GetChildLayerObserverEpoch(), false);
+  mCompositorBridge->ObserveLayerUpdate(wr::AsUint64(mPipelineId), GetChildLayerObserverEpoch(), false);
   return IPC_OK();
 }
 
 void
 WebRenderBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   Destroy();
 }
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -65,18 +65,17 @@ public:
                                        const gfx::SurfaceFormat& aFormat,
                                        const ByteBuffer& aBuffer,
                                        wr::ImageKey* aOutImageKey) override;
   mozilla::ipc::IPCResult RecvUpdateImage(const wr::ImageKey& aImageKey,
                                           const gfx::IntSize& aSize,
                                           const gfx::SurfaceFormat& aFormat,
                                           const ByteBuffer& aBuffer) override;
   mozilla::ipc::IPCResult RecvDeleteImage(const wr::ImageKey& a1) override;
-  mozilla::ipc::IPCResult RecvDPBegin(const gfx::IntSize& aSize,
-                                      bool* aOutSuccess) override;
+  mozilla::ipc::IPCResult RecvDPBegin(const gfx::IntSize& aSize) override;
   mozilla::ipc::IPCResult RecvDPEnd(InfallibleTArray<WebRenderCommand>&& aCommands,
                                     InfallibleTArray<OpDestroy>&& aToDestroy,
                                     const uint64_t& aFwdTransactionId,
                                     const uint64_t& aTransactionId) override;
   mozilla::ipc::IPCResult RecvDPSyncEnd(InfallibleTArray<WebRenderCommand>&& aCommands,
                                         InfallibleTArray<OpDestroy>&& aToDestroy,
                                         const uint64_t& aFwdTransactionId,
                                         const uint64_t& aTransactionId) override;
--- a/gfx/layers/wr/WebRenderCanvasLayer.cpp
+++ b/gfx/layers/wr/WebRenderCanvasLayer.cpp
@@ -5,17 +5,17 @@
 
 #include "WebRenderCanvasLayer.h"
 
 #include "AsyncCanvasRenderer.h"
 #include "gfxPrefs.h"
 #include "gfxUtils.h"
 #include "GLContext.h"
 #include "GLScreenBuffer.h"
-#include "WebRenderLayersLogging.h"
+#include "LayersLogging.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/layers/TextureClientSharedSurface.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "PersistentBufferProvider.h"
 #include "SharedSurface.h"
 #include "SharedSurfaceGL.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 
@@ -51,35 +51,44 @@ WebRenderCanvasLayer::RenderLayer()
   UpdateCompositableClient();
 
   if (!mExternalImageId) {
     mExternalImageId = WrBridge()->AllocExternalImageIdForCompositable(mCanvasClient);
   }
 
   MOZ_ASSERT(mExternalImageId);
 
-  gfx::Matrix4x4 transform;// = GetTransform();
+  gfx::Matrix4x4 transform = GetTransform();
   const bool needsYFlip = (mOriginPos == gl::OriginPos::BottomLeft);
   if (needsYFlip) {
     transform.PreTranslate(0, mBounds.height, 0).PreScale(1, -1, 1);
   }
   gfx::Rect rect(0, 0, mBounds.width, mBounds.height);
-  rect = RelativeToTransformedVisible(GetTransform().TransformBounds(rect));
+  rect = RelativeToVisible(rect);
 
   gfx::Rect clip;
   if (GetClipRect().isSome()) {
-      clip = RelativeToTransformedVisible(IntRectToRect(GetClipRect().ref().ToUnknownRect()));
+      clip = RelativeToVisible(transform.Inverse().TransformBounds(IntRectToRect(GetClipRect().ref().ToUnknownRect())));
   } else {
       clip = rect;
   }
 
-  gfx::Rect relBounds = TransformedVisibleBoundsRelativeToParent();
+  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 overflow(0, 0, relBounds.width, relBounds.height);
   Maybe<WrImageMask> mask = buildMaskLayer();
-  WrTextureFilter filter = (mSamplingFilter == gfx::SamplingFilter::POINT) ? WrTextureFilter::Point : WrTextureFilter::Linear;
+  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(),
--- a/gfx/layers/wr/WebRenderColorLayer.cpp
+++ b/gfx/layers/wr/WebRenderColorLayer.cpp
@@ -1,41 +1,52 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebRenderColorLayer.h"
 
-#include "WebRenderLayersLogging.h"
 #include "gfxPrefs.h"
+#include "LayersLogging.h"
 #include "mozilla/webrender/webrender_ffi.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 
 namespace mozilla {
 namespace layers {
 
+using namespace mozilla::gfx;
+
 void
 WebRenderColorLayer::RenderLayer()
 {
   WrScrollFrameStackingContextGenerator scrollFrames(this);
 
+  gfx::Matrix4x4 transform = GetTransform();
   LayerIntRegion visibleRegion = GetVisibleRegion();
   LayerIntRect bounds = visibleRegion.GetBounds();
-  Rect rect = RelativeToVisible(IntRectToRect(bounds.ToUnknownRect()));
+  Rect rect(0, 0, bounds.width, bounds.height);
   Rect clip;
   if (GetClipRect().isSome()) {
-      clip = RelativeToTransformedVisible(IntRectToRect(GetClipRect().ref().ToUnknownRect()));
+      clip = RelativeToVisible(transform.Inverse().TransformBounds(IntRectToRect(GetClipRect().ref().ToUnknownRect())));
   } else {
       clip = rect;
   }
 
-  gfx::Matrix4x4 transform;// = GetTransform();
-  gfx::Rect relBounds = TransformedVisibleBoundsRelativeToParent();
+  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 overflow(0, 0, relBounds.width, relBounds.height);
   WrMixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
 
   Maybe<WrImageMask> mask = buildMaskLayer();
 
   if (gfxPrefs::LayersDump()) {
     printf_stderr("ColorLayer %p using bounds=%s, overflow=%s, transform=%s, rect=%s, clip=%s, mix-blend-mode=%s\n",
                   this->GetLayer(),
deleted file mode 100644
--- a/gfx/layers/wr/WebRenderCompositorOGL.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "WebRenderCompositorOGL.h"
-
-#include "CompositableHost.h"
-#include "GLContext.h"                  // for GLContext
-#include "GLUploadHelpers.h"
-#include "mozilla/layers/CompositorBridgeParent.h"
-#include "mozilla/layers/TextureHost.h"  // for TextureSource, etc
-#include "mozilla/layers/TextureHostOGL.h"  // for TextureSourceOGL, etc
-
-namespace mozilla {
-
-using namespace gfx;
-using namespace gl;
-
-namespace layers {
-
-WebRenderCompositorOGL::WebRenderCompositorOGL(CompositorBridgeParent* aCompositorBridge,
-                                               GLContext* aGLContext)
-  : Compositor(nullptr, nullptr)
-  , mCompositorBridge(aCompositorBridge)
-  , mGLContext(aGLContext)
-  , mDestroyed(false)
-{
-  MOZ_COUNT_CTOR(WebRenderCompositorOGL);
-}
-
-WebRenderCompositorOGL::~WebRenderCompositorOGL()
-{
-  MOZ_COUNT_DTOR(WebRenderCompositorOGL);
-  Destroy();
-}
-
-void
-WebRenderCompositorOGL::Destroy()
-{
-  Compositor::Destroy();
-
-  mCompositableHosts.Clear();
-  mCompositorBridge = nullptr;
-
-  if (!mDestroyed) {
-    mDestroyed = true;
-    CleanupResources();
-  }
-}
-
-void
-WebRenderCompositorOGL::CleanupResources()
-{
-  if (!mGLContext) {
-    return;
-  }
-
-  // On the main thread the Widget will be destroyed soon and calling MakeCurrent
-  // after that could cause a crash (at least with GLX, see bug 1059793), unless
-  // context is marked as destroyed.
-  // There may be some textures still alive that will try to call MakeCurrent on
-  // the context so let's make sure it is marked destroyed now.
-  mGLContext->MarkDestroyed();
-
-  mGLContext = nullptr;
-}
-
-bool
-WebRenderCompositorOGL::Initialize(nsCString* const out_failureReason)
-{
-  MOZ_ASSERT(mGLContext);
-  return true;
-}
-
-already_AddRefed<DataTextureSource>
-WebRenderCompositorOGL::CreateDataTextureSource(TextureFlags aFlags)
-{
-  return nullptr;
-}
-
-bool
-WebRenderCompositorOGL::SupportsPartialTextureUpdate()
-{
-  return CanUploadSubTextures(mGLContext);
-}
-
-int32_t
-WebRenderCompositorOGL::GetMaxTextureSize() const
-{
-  MOZ_ASSERT(mGLContext);
-  GLint texSize = 0;
-  mGLContext->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE,
-                            &texSize);
-  MOZ_ASSERT(texSize != 0);
-  return texSize;
-}
-
-void
-WebRenderCompositorOGL::MakeCurrent(MakeCurrentFlags aFlags) {
-  if (mDestroyed) {
-    NS_WARNING("Call on destroyed layer manager");
-    return;
-  }
-  mGLContext->MakeCurrent(aFlags & ForceMakeCurrent);
-}
-
-void
-WebRenderCompositorOGL::AddExternalImageId(uint64_t aExternalImageId, CompositableHost* aHost)
-{
-  MOZ_ASSERT(!mCompositableHosts.Get(aExternalImageId));
-  mCompositableHosts.Put(aExternalImageId, aHost);
-}
-
-void
-WebRenderCompositorOGL::RemoveExternalImageId(uint64_t aExternalImageId)
-{
-  MOZ_ASSERT(mCompositableHosts.Get(aExternalImageId));
-  mCompositableHosts.Remove(aExternalImageId);
-}
-
-void
-WebRenderCompositorOGL::UpdateExternalImages()
-{
-  for (auto iter = mCompositableHosts.Iter(); !iter.Done(); iter.Next()) {
-    RefPtr<CompositableHost>& host = iter.Data();
-    // XXX Change to correct TextrueSource handling here.
-    host->BindTextureSource();
-  }
-}
-
-void
-WebRenderCompositorOGL::ScheduleComposition()
-{
-  MOZ_ASSERT(mCompositorBridge);
-  mCompositorBridge->ScheduleComposition();
-}
-
-} // namespace layers
-} // namespace mozilla
deleted file mode 100644
--- a/gfx/layers/wr/WebRenderCompositorOGL.h
+++ /dev/null
@@ -1,136 +0,0 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef MOZILLA_GFX_WEBRENDERCOMPOSITOROGL_H
-#define MOZILLA_GFX_WEBRENDERCOMPOSITOROGL_H
-
-#include "GLContextTypes.h"             // for GLContext, etc
-#include "GLDefs.h"                     // for GLuint, LOCAL_GL_TEXTURE_2D, etc
-#include "mozilla/layers/Compositor.h"  // for SurfaceInitMode, Compositor, etc
-#include "nsDataHashtable.h"
-
-namespace mozilla {
-namespace layers {
-
-class CompositableHost;
-class CompositorBridgeParent;
-
-class WebRenderCompositorOGL final : public Compositor
-{
-  typedef mozilla::gl::GLContext GLContext;
-
-public:
-  explicit WebRenderCompositorOGL(CompositorBridgeParent* aCompositorBridge, GLContext* aGLContext);
-
-protected:
-  virtual ~WebRenderCompositorOGL();
-
-public:
-  virtual WebRenderCompositorOGL* AsWebRenderCompositorOGL() override { return this; }
-
-  virtual already_AddRefed<DataTextureSource>
-  CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
-
-  virtual bool Initialize(nsCString* const out_failureReason) override;
-
-  virtual void Destroy() override;
-
-  virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override
-  {
-    TextureFactoryIdentifier result =
-      TextureFactoryIdentifier(LayersBackend::LAYERS_WR,
-                               XRE_GetProcessType(),
-                               GetMaxTextureSize(),
-                               true,
-                               SupportsPartialTextureUpdate());
-    return result;
-  }
-
-  virtual already_AddRefed<CompositingRenderTarget>
-  CreateRenderTarget(const gfx::IntRect &aRect, SurfaceInitMode aInit) override { return nullptr; }
-
-  virtual already_AddRefed<CompositingRenderTarget>
-  CreateRenderTargetFromSource(const gfx::IntRect &aRect,
-                               const CompositingRenderTarget *aSource,
-                               const gfx::IntPoint &aSourcePoint) override { return nullptr; }
-
-  virtual void SetRenderTarget(CompositingRenderTarget *aSurface) override { }
-
-  virtual CompositingRenderTarget* GetCurrentRenderTarget() const override { return nullptr; }
-
-  virtual void DrawQuad(const gfx::Rect& aRect,
-                        const gfx::IntRect& aClipRect,
-                        const EffectChain &aEffectChain,
-                        gfx::Float aOpacity,
-                        const gfx::Matrix4x4& aTransform,
-                        const gfx::Rect& aVisibleRect) override { }
-
-  virtual void DrawTriangle(const gfx::TexturedTriangle& aTriangle,
-                            const gfx::IntRect& aClipRect,
-                            const EffectChain& aEffectChain,
-                            gfx::Float aOpacity,
-                            const gfx::Matrix4x4& aTransform,
-                            const gfx::Rect& aVisibleRect) override { }
-
-  virtual void ClearRect(const gfx::Rect& aRect) override { }
-
-  virtual void BeginFrame(const nsIntRegion& aInvalidRegion,
-                          const gfx::IntRect *aClipRectIn,
-                          const gfx::IntRect& aRenderBounds,
-                          const nsIntRegion& aOpaqueRegion,
-                          gfx::IntRect *aClipRectOut = nullptr,
-                          gfx::IntRect *aRenderBoundsOut = nullptr) override { }
-
-  virtual void EndFrame() override { }
-
-  virtual bool SupportsPartialTextureUpdate() override;
-
-  virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override
-  {
-    if (!mGLContext)
-      return false;
-    int32_t maxSize = GetMaxTextureSize();
-    return aSize <= gfx::IntSize(maxSize, maxSize);
-  }
-
-  virtual int32_t GetMaxTextureSize() const override;
-
-  virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) override { }
-  virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) override { }
-
-  virtual void MakeCurrent(MakeCurrentFlags aFlags = 0) override;
-
-#ifdef MOZ_DUMP_PAINTING
-  virtual const char* Name() const override { return "WROGL"; }
-#endif // MOZ_DUMP_PAINTING
-
-  virtual LayersBackend GetBackendType() const override {
-    return LayersBackend::LAYERS_WR;
-  }
-
-  virtual bool IsValid() const override { return true; }
-
-  GLContext* gl() const { return mGLContext; }
-
-  void AddExternalImageId(uint64_t aExternalImageId, CompositableHost* aHost);
-  void RemoveExternalImageId(uint64_t aExternalImageId);
-  void UpdateExternalImages();
-
-  void ScheduleComposition();
-private:
-  void CleanupResources();
-
-  CompositorBridgeParent* MOZ_NON_OWNING_REF mCompositorBridge;
-  RefPtr<GLContext> mGLContext;
-  // Holds CompositableHosts that are bound to external image ids.
-  nsDataHashtable<nsUint64HashKey, RefPtr<CompositableHost> > mCompositableHosts;
-
-  bool mDestroyed;
-};
-
-} // namespace layers
-} // namespace mozilla
-
-#endif /* MOZILLA_GFX_WEBRENDERCOMPOSITOROGL_H */
--- a/gfx/layers/wr/WebRenderContainerLayer.cpp
+++ b/gfx/layers/wr/WebRenderContainerLayer.cpp
@@ -2,33 +2,41 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebRenderContainerLayer.h"
 
 #include <inttypes.h>
 #include "gfxPrefs.h"
+#include "LayersLogging.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/webrender/WebRenderTypes.h"
-#include "WebRenderLayersLogging.h"
 
 namespace mozilla {
 namespace layers {
 
 void
 WebRenderContainerLayer::RenderLayer()
 {
   WrScrollFrameStackingContextGenerator scrollFrames(this);
 
   nsTArray<LayerPolygon> children = SortChildrenBy3DZOrder(SortMode::WITHOUT_GEOMETRY);
+  gfx::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 = TransformedVisibleBoundsRelativeToParent();
   gfx::Rect overflow(0, 0, relBounds.width, relBounds.height);
-  gfx::Matrix4x4 transform;// = GetTransform();
   WrMixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
   Maybe<WrImageMask> mask = buildMaskLayer();
 
   if (gfxPrefs::LayersDump()) {
     printf_stderr("ContainerLayer %p using bounds=%s, overflow=%s, transform=%s, mix-blend-mode=%s\n",
                   this->GetLayer(),
                   Stringify(relBounds).c_str(),
                   Stringify(overflow).c_str(),
@@ -66,13 +74,13 @@ WebRenderRefLayer::RenderLayer()
   if (gfxPrefs::LayersDump()) {
     printf_stderr("RefLayer %p (%" PRIu64 ") using bounds/overflow=%s, transform=%s\n",
                   this->GetLayer(),
                   mId,
                   Stringify(relBounds).c_str(),
                   Stringify(transform).c_str());
   }
 
-  WrBridge()->AddWebRenderCommand(OpDPPushIframe(wr::ToWrRect(relBounds), wr::ToWrRect(relBounds), wr::PipelineId(mId)));
+  WrBridge()->AddWebRenderCommand(OpDPPushIframe(wr::ToWrRect(relBounds), wr::ToWrRect(relBounds), wr::AsPipelineId(mId)));
 }
 
 } // namespace layers
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderDisplayItemLayer.cpp
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebRenderDisplayItemLayer.h"
+
+#include "LayersLogging.h"
+#include "mozilla/webrender/webrender_ffi.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+#include "mozilla/layers/WebRenderBridgeChild.h"
+#include "nsDisplayList.h"
+#include "mozilla/gfx/Matrix.h"
+
+namespace mozilla {
+namespace layers {
+
+void
+WebRenderDisplayItemLayer::RenderLayer()
+{
+  if (mItem) {
+    // We might have recycled this layer. Throw away the old commands.
+    mCommands.Clear();
+    mItem->CreateWebRenderCommands(mCommands, this);
+  }
+  // else we have an empty transaction and just use the
+  // old commands.
+
+  WrBridge()->AddWebRenderCommands(mCommands);
+}
+
+uint64_t
+WebRenderDisplayItemLayer::SendImageContainer(ImageContainer* aContainer)
+{
+  if (mImageContainer != aContainer) {
+    AutoLockImage autoLock(aContainer);
+    Image* image = autoLock.GetImage();
+    if (!image) {
+      return 0;
+    }
+
+    if (!mImageClient) {
+      mImageClient = ImageClient::CreateImageClient(CompositableType::IMAGE,
+                                                    WrBridge(),
+                                                    TextureFlags::DEFAULT);
+      if (!mImageClient) {
+        return 0;
+      }
+      mImageClient->Connect();
+    }
+
+    if (!mExternalImageId) {
+      MOZ_ASSERT(mImageClient);
+      mExternalImageId = WrBridge()->AllocExternalImageIdForCompositable(mImageClient);
+    }
+    MOZ_ASSERT(mExternalImageId);
+
+    if (mImageClient && !mImageClient->UpdateImage(aContainer, /* unused */0)) {
+      return 0;
+    }
+    mImageContainer = aContainer;
+  }
+
+  return mExternalImageId;
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderDisplayItemLayer.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_WEBRENDERDISPLAYITEMLAYER_H
+#define GFX_WEBRENDERDISPLAYITEMLAYER_H
+
+#include "Layers.h"
+#include "WebRenderLayerManager.h"
+#include "mozilla/layers/ImageClient.h"
+#include "mozilla/layers/PWebRenderBridgeChild.h"
+
+namespace mozilla {
+namespace layers {
+
+class WebRenderDisplayItemLayer : public WebRenderLayer,
+                                  public DisplayItemLayer {
+public:
+  explicit WebRenderDisplayItemLayer(WebRenderLayerManager* aLayerManager)
+    : DisplayItemLayer(aLayerManager, static_cast<WebRenderLayer*>(this))
+    , mExternalImageId(0)
+  {
+    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;
+
+private:
+  nsTArray<WebRenderCommand> mCommands;
+  RefPtr<ImageClient> mImageClient;
+  RefPtr<ImageContainer> mImageContainer;
+  uint64_t mExternalImageId;
+
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_WEBRENDERDisplayItemLayer_H
\ No newline at end of file
--- a/gfx/layers/wr/WebRenderImageHost.cpp
+++ b/gfx/layers/wr/WebRenderImageHost.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebRenderImageHost.h"
 
-#include "LayersLogging.h"              // for AppendToString
-
+#include "LayersLogging.h"
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/Effects.h"     // for TexturedEffect, Effect, etc
 #include "mozilla/layers/LayerManagerComposite.h"     // for TexturedEffect, Effect, etc
 #include "nsAString.h"
 #include "nsDebug.h"                    // for NS_WARNING, NS_ASSERTION
 #include "nsPrintfCString.h"            // for nsPrintfCString
 #include "nsString.h"                   // for nsAutoCString
 
--- a/gfx/layers/wr/WebRenderImageLayer.cpp
+++ b/gfx/layers/wr/WebRenderImageLayer.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebRenderImageLayer.h"
 
-#include "WebRenderLayersLogging.h"
 #include "gfxPrefs.h"
+#include "LayersLogging.h"
 #include "mozilla/layers/ImageClient.h"
 #include "mozilla/layers/TextureClientRecycleAllocator.h"
 #include "mozilla/layers/TextureWrapperImage.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 
 namespace mozilla {
 namespace layers {
@@ -124,30 +124,38 @@ WebRenderImageLayer::RenderLayer()
   gfx::IntSize size = image->GetSize();
 
   if (mImageClient && !mImageClient->UpdateImage(mContainer, /* unused */0)) {
     return;
   }
 
   WrScrollFrameStackingContextGenerator scrollFrames(this);
 
+  Matrix4x4 transform = GetTransform();
   Rect rect(0, 0, size.width, size.height);
-
   Rect clip;
   if (GetClipRect().isSome()) {
-      clip = RelativeToTransformedVisible(IntRectToRect(GetClipRect().ref().ToUnknownRect()));
+      clip = RelativeToVisible(transform.Inverse().TransformBounds(IntRectToRect(GetClipRect().ref().ToUnknownRect())));
   } else {
       clip = rect;
   }
 
-  Rect relBounds = TransformedVisibleBoundsRelativeToParent();
+  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);
-  Matrix4x4 transform;// = GetTransform();
   Maybe<WrImageMask> mask = buildMaskLayer();
-  WrTextureFilter filter = (mSamplingFilter == gfx::SamplingFilter::POINT) ? WrTextureFilter::Point : WrTextureFilter::Linear;
+  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(),
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -2,33 +2,34 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebRenderLayerManager.h"
 
 #include "apz/src/AsyncPanZoomController.h"
 #include "gfxPrefs.h"
-#include "WebRenderLayersLogging.h"
+#include "LayersLogging.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/AsyncCompositionManager.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/layers/TextureClient.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/widget/PlatformWidgetTypes.h"
 #include "nsThreadUtils.h"
 #include "TreeTraversal.h"
 #include "WebRenderBorderLayer.h"
 #include "WebRenderCanvasLayer.h"
 #include "WebRenderColorLayer.h"
 #include "WebRenderContainerLayer.h"
 #include "WebRenderImageLayer.h"
 #include "WebRenderPaintedLayer.h"
 #include "WebRenderTextLayer.h"
+#include "WebRenderDisplayItemLayer.h"
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
 WebRenderLayerManager*
@@ -81,16 +82,29 @@ WebRenderLayer::ParentStackingContextBou
 Rect
 WebRenderLayer::RelativeToParent(Rect aRect)
 {
   Rect parentBounds = ParentStackingContextBounds(-1);
   aRect.MoveBy(-parentBounds.x, -parentBounds.y);
   return aRect;
 }
 
+Point
+WebRenderLayer::GetOffsetToParent()
+{
+  Rect parentBounds = ParentStackingContextBounds(-1);
+  return parentBounds.TopLeft();
+}
+
+Rect
+WebRenderLayer::VisibleBoundsRelativeToParent()
+{
+  return RelativeToParent(IntRectToRect(GetLayer()->GetVisibleRegion().GetBounds().ToUnknownRect()));
+}
+
 Rect
 WebRenderLayer::TransformedVisibleBoundsRelativeToParent()
 {
   IntRect bounds = GetLayer()->GetVisibleRegion().GetBounds().ToUnknownRect();
   Rect transformed = GetLayer()->GetTransform().TransformBounds(IntRectToRect(bounds));
   return RelativeToParent(transformed);
 }
 
@@ -113,17 +127,17 @@ WebRenderLayer::buildMaskLayer() {
           RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
           DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::MapType::READ);
           gfx::IntSize size = surface->GetSize();
           MOZ_RELEASE_ASSERT(surface->GetFormat() == SurfaceFormat::A8, "bad format");
           wr::ByteBuffer buf(size.height * map.GetStride(), map.GetData());
           wr::ImageKey maskKey;
           WrBridge()->SendAddImage(size, map.GetStride(), SurfaceFormat::A8, buf, &maskKey);
 
-          imageMask.image = maskKey.mHandle;
+          imageMask.image = maskKey;
           imageMask.rect = wr::ToWrRect(Rect(0, 0, size.width, size.height));
           imageMask.repeat = false;
           WrManager()->AddImageKeyForDiscard(maskKey);
           mask = Some(imageMask);
       }
     }
   }
   return mask;
@@ -153,38 +167,34 @@ WrScrollFrameStackingContextGenerator::W
     // the available overflow. If we didn't do this and WR did bounds checking
     // on the scroll offset, we'd fail those checks.
     overflow.MoveBy(bounds.x - scrollPos.x, bounds.y - scrollPos.y);
     if (gfxPrefs::LayersDump()) {
       printf_stderr("Pushing stacking context id %" PRIu64 " with bounds=%s, overflow=%s\n",
         fm.GetScrollId(), Stringify(bounds).c_str(), Stringify(overflow).c_str());
     }
 
-    mLayer->WrBridge()->AddWebRenderCommand(
-      OpDPPushStackingContext(wr::ToWrRect(bounds),
-                              wr::ToWrRect(overflow),
-                              Nothing(),
-                              1.0f,
-                              layer->GetAnimations(),
-                              identity,
-                              WrMixBlendMode::Normal,
-                              fm.GetScrollId()));
+/*    mLayer->WrBridge()->AddWebRenderCommand(
+      OpDPPushScrollLayer(wr::ToWrRect(bounds),
+                          wr::ToWrRect(overflow),
+                          Nothing(),
+                          fm.GetScrollId()));*/
   }
 }
 
 WrScrollFrameStackingContextGenerator::~WrScrollFrameStackingContextGenerator()
 {
   Layer* layer = mLayer->GetLayer();
   for (size_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
     const FrameMetrics& fm = layer->GetFrameMetrics(i);
     if (!fm.IsScrollable()) {
       continue;
     }
     if (gfxPrefs::LayersDump()) printf_stderr("Popping stacking context id %" PRIu64"\n", fm.GetScrollId());
-    mLayer->WrBridge()->AddWebRenderCommand(OpDPPopStackingContext());
+//    mLayer->WrBridge()->AddWebRenderCommand(OpDPPopStackingContext());
   }
 }
 
 
 WebRenderLayerManager::WebRenderLayerManager(nsIWidget* aWidget)
   : mWidget(aWidget)
   , mLatestTransactionId(0)
   , mTarget(nullptr)
@@ -195,24 +205,24 @@ WebRenderLayerManager::WebRenderLayerMan
 KnowsCompositor*
 WebRenderLayerManager::AsKnowsCompositor()
 {
   return mWrChild;
 }
 
 void
 WebRenderLayerManager::Initialize(PCompositorBridgeChild* aCBChild,
-                                  uint64_t aLayersId,
+                                  wr::PipelineId aLayersId,
                                   TextureFactoryIdentifier* aTextureFactoryIdentifier)
 {
   MOZ_ASSERT(mWrChild == nullptr);
   MOZ_ASSERT(aTextureFactoryIdentifier);
 
   TextureFactoryIdentifier textureFactoryIdentifier;
-  PWebRenderBridgeChild* bridge = aCBChild->SendPWebRenderBridgeConstructor(wr::PipelineId(aLayersId),
+  PWebRenderBridgeChild* bridge = aCBChild->SendPWebRenderBridgeConstructor(aLayersId,
                                                                             &textureFactoryIdentifier);
   MOZ_ASSERT(bridge);
   mWrChild = static_cast<WebRenderBridgeChild*>(bridge);
   LayoutDeviceIntSize size = mWidget->GetClientSize();
   WrBridge()->SendCreate(size.ToUnknownSize());
   WrBridge()->IdentifyTextureHost(textureFactoryIdentifier);
   *aTextureFactoryIdentifier = textureFactoryIdentifier;
 }
@@ -534,10 +544,16 @@ WebRenderLayerManager::CreateTextLayer()
 }
 
 already_AddRefed<BorderLayer>
 WebRenderLayerManager::CreateBorderLayer()
 {
   return MakeAndAddRef<WebRenderBorderLayer>(this);
 }
 
+already_AddRefed<DisplayItemLayer>
+WebRenderLayerManager::CreateDisplayItemLayer()
+{
+  return MakeAndAddRef<WebRenderDisplayItemLayer>(this);
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderLayerManager.h
+++ b/gfx/layers/wr/WebRenderLayerManager.h
@@ -39,16 +39,18 @@ public:
 
   WebRenderLayerManager* WrManager();
   WebRenderBridgeChild* WrBridge();
 
   gfx::Rect RelativeToVisible(gfx::Rect aRect);
   gfx::Rect RelativeToTransformedVisible(gfx::Rect aRect);
   gfx::Rect ParentStackingContextBounds(size_t aScrollMetadataIndex);
   gfx::Rect RelativeToParent(gfx::Rect aRect);
+  gfx::Rect VisibleBoundsRelativeToParent();
+  gfx::Point GetOffsetToParent();
   gfx::Rect TransformedVisibleBoundsRelativeToParent();
 protected:
   Maybe<WrImageMask> buildMaskLayer();
 
 };
 
 class MOZ_RAII WrScrollFrameStackingContextGenerator
 {
@@ -60,17 +62,17 @@ private:
 };
 
 class WebRenderLayerManager final : public LayerManager
 {
   typedef nsTArray<RefPtr<Layer> > LayerRefArray;
 
 public:
   explicit WebRenderLayerManager(nsIWidget* aWidget);
-  void Initialize(PCompositorBridgeChild* aCBChild, uint64_t aLayersId, TextureFactoryIdentifier* aTextureFactoryIdentifier);
+  void Initialize(PCompositorBridgeChild* aCBChild, wr::PipelineId aLayersId, TextureFactoryIdentifier* aTextureFactoryIdentifier);
 
   virtual void Destroy() override;
 
 protected:
   virtual ~WebRenderLayerManager();
 
 public:
   virtual KnowsCompositor* AsKnowsCompositor() override;
@@ -96,16 +98,17 @@ public:
   virtual already_AddRefed<ContainerLayer> CreateContainerLayer() override;
   virtual already_AddRefed<ImageLayer> CreateImageLayer() override;
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
   virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() override;
   virtual already_AddRefed<ColorLayer> CreateColorLayer() override;
   virtual already_AddRefed<RefLayer> CreateRefLayer() override;
   virtual already_AddRefed<TextLayer> CreateTextLayer() override;
   virtual already_AddRefed<BorderLayer> CreateBorderLayer() override;
+  virtual already_AddRefed<DisplayItemLayer> CreateDisplayItemLayer() override;
 
   virtual bool NeedsWidgetInvalidation() override { return true; }
 
   virtual void SetLayerObserverEpoch(uint64_t aLayerObserverEpoch) override;
 
   virtual void DidComposite(uint64_t aTransactionId,
                             const mozilla::TimeStamp& aCompositeStart,
                             const mozilla::TimeStamp& aCompositeEnd) override;
--- a/gfx/layers/wr/WebRenderLayersLogging.cpp
+++ b/gfx/layers/wr/WebRenderLayersLogging.cpp
@@ -8,71 +8,73 @@
 #include "WebRenderLayersLogging.h"
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace layers {
 
 void
-AppendToString(std::stringstream& aStream, WrMixBlendMode aMixBlendMode,
+AppendToString(std::stringstream& aStream, wr::MixBlendMode aMixBlendMode,
                const char* pfx, const char* sfx)
 {
   aStream << pfx;
   switch (aMixBlendMode) {
-  case WrMixBlendMode::Normal:
-    aStream << "WrMixBlendMode::Normal"; break;
-  case WrMixBlendMode::Multiply:
-    aStream << "WrMixBlendMode::Multiply"; break;
-  case WrMixBlendMode::Screen:
-    aStream << "WrMixBlendMode::Screen"; break;
-  case WrMixBlendMode::Overlay:
-    aStream << "WrMixBlendMode::Overlay"; break;
-  case WrMixBlendMode::Darken:
-    aStream << "WrMixBlendMode::Darken"; break;
-  case WrMixBlendMode::Lighten:
-    aStream << "WrMixBlendMode::Lighten"; break;
-  case WrMixBlendMode::ColorDodge:
-    aStream << "WrMixBlendMode::ColorDodge"; break;
-  case WrMixBlendMode::ColorBurn:
-    aStream << "WrMixBlendMode::ColorBurn"; break;
-  case WrMixBlendMode::HardLight:
-    aStream << "WrMixBlendMode::HardLight"; break;
-  case WrMixBlendMode::SoftLight:
-    aStream << "WrMixBlendMode::SoftLight"; break;
-  case WrMixBlendMode::Difference:
-    aStream << "WrMixBlendMode::Difference"; break;
-  case WrMixBlendMode::Exclusion:
-    aStream << "WrMixBlendMode::Exclusion"; break;
-  case WrMixBlendMode::Hue:
-    aStream << "WrMixBlendMode::Hue"; break;
-  case WrMixBlendMode::Saturation:
-    aStream << "WrMixBlendMode::Saturation"; break;
-  case WrMixBlendMode::Color:
-    aStream << "WrMixBlendMode::Color"; break;
-  case WrMixBlendMode::Luminosity:
-    aStream << "WrMixBlendMode::Luminosity"; break;
-  case WrMixBlendMode::Sentinel:
+  case wr::MixBlendMode::Normal:
+    aStream << "wr::MixBlendMode::Normal"; break;
+  case wr::MixBlendMode::Multiply:
+    aStream << "wr::MixBlendMode::Multiply"; break;
+  case wr::MixBlendMode::Screen:
+    aStream << "wr::MixBlendMode::Screen"; break;
+  case wr::MixBlendMode::Overlay:
+    aStream << "wr::MixBlendMode::Overlay"; break;
+  case wr::MixBlendMode::Darken:
+    aStream << "wr::MixBlendMode::Darken"; break;
+  case wr::MixBlendMode::Lighten:
+    aStream << "wr::MixBlendMode::Lighten"; break;
+  case wr::MixBlendMode::ColorDodge:
+    aStream << "wr::MixBlendMode::ColorDodge"; break;
+  case wr::MixBlendMode::ColorBurn:
+    aStream << "wr::MixBlendMode::ColorBurn"; break;
+  case wr::MixBlendMode::HardLight:
+    aStream << "wr::MixBlendMode::HardLight"; break;
+  case wr::MixBlendMode::SoftLight:
+    aStream << "wr::MixBlendMode::SoftLight"; break;
+  case wr::MixBlendMode::Difference:
+    aStream << "wr::MixBlendMode::Difference"; break;
+  case wr::MixBlendMode::Exclusion:
+    aStream << "wr::MixBlendMode::Exclusion"; break;
+  case wr::MixBlendMode::Hue:
+    aStream << "wr::MixBlendMode::Hue"; break;
+  case wr::MixBlendMode::Saturation:
+    aStream << "wr::MixBlendMode::Saturation"; break;
+  case wr::MixBlendMode::Color:
+    aStream << "wr::MixBlendMode::Color"; break;
+  case wr::MixBlendMode::Luminosity:
+    aStream << "wr::MixBlendMode::Luminosity"; break;
+  case wr::MixBlendMode::Sentinel:
     NS_ERROR("unknown mix blend mode");
     aStream << "???";
   }
   aStream << sfx;
 }
 
 void
-AppendToString(std::stringstream& aStream, WrTextureFilter aTextureFilter,
+AppendToString(std::stringstream& aStream, wr::ImageRendering aTextureFilter,
                const char* pfx, const char* sfx)
 {
   aStream << pfx;
   switch (aTextureFilter) {
-  case WrTextureFilter::Linear:
-    aStream << "WrTextureFilter::Linear"; break;
-  case WrTextureFilter::Point:
-    aStream << "WrTextureFilter::Point"; break;
-  case WrTextureFilter::Sentinel:
+  case wr::ImageRendering::Auto:
+    aStream << "ImageRendering::Auto"; break;
+  case wr::ImageRendering::CrispEdges:
+    aStream << "ImageRendering::CrispEdges"; break;
+  case wr::ImageRendering::Pixelated:
+    aStream << "ImageRendering::Pixelated"; break;
+  case wr::ImageRendering::Sentinel:
     NS_ERROR("unknown texture filter");
     aStream << "???";
   }
   aStream << sfx;
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderLayersLogging.h
+++ b/gfx/layers/wr/WebRenderLayersLogging.h
@@ -8,21 +8,21 @@
 #define GFX_WEBRENDERLAYERSLOGGING_H
 
 #include "mozilla/webrender/WebRenderTypes.h"
 
 namespace mozilla {
 namespace layers {
 
 void
-AppendToString(std::stringstream& aStream, WrMixBlendMode aMixBlendMode,
+AppendToString(std::stringstream& aStream, wr::MixBlendMode aMixBlendMode,
                const char* pfx="", const char* sfx="");
 
 void
-AppendToString(std::stringstream& aStream, WrTextureFilter aTextureFilter,
+AppendToString(std::stringstream& aStream, wr::ImageRendering aTextureFilter,
                const char* pfx="", const char* sfx="");
 
 } // namespace layers
 } // namespace mozilla
 
 // this ensures that the WebRender AppendToString's are in scope for Stringify
 #include "LayersLogging.h"
 
--- a/gfx/layers/wr/WebRenderMessageUtils.h
+++ b/gfx/layers/wr/WebRenderMessageUtils.h
@@ -36,55 +36,61 @@ struct ParamTraits<mozilla::wr::ByteBuff
 };
 
 template<>
 struct ParamTraits<mozilla::wr::ImageKey>
 {
   static void
   Write(Message* aMsg, const mozilla::wr::ImageKey& aParam)
   {
+    WriteParam(aMsg, aParam.mNamespace);
     WriteParam(aMsg, aParam.mHandle);
   }
 
   static bool
   Read(const Message* aMsg, PickleIterator* aIter, mozilla::wr::ImageKey* aResult)
   {
-    return ReadParam(aMsg, aIter, &aResult->mHandle);
+    return ReadParam(aMsg, aIter, &aResult->mNamespace)
+        && ReadParam(aMsg, aIter, &aResult->mHandle);
   }
 };
 
 template<>
 struct ParamTraits<mozilla::wr::FontKey>
 {
   static void
   Write(Message* aMsg, const mozilla::wr::FontKey& aParam)
   {
+    WriteParam(aMsg, aParam.mNamespace);
     WriteParam(aMsg, aParam.mHandle);
   }
 
   static bool
   Read(const Message* aMsg, PickleIterator* aIter, mozilla::wr::FontKey* aResult)
   {
-    return ReadParam(aMsg, aIter, &aResult->mHandle);
+    return ReadParam(aMsg, aIter, &aResult->mNamespace)
+        && ReadParam(aMsg, aIter, &aResult->mHandle);
   }
 };
 
 template<>
 struct ParamTraits<mozilla::wr::PipelineId>
 {
   static void
   Write(Message* aMsg, const mozilla::wr::PipelineId& aParam)
   {
+    WriteParam(aMsg, aParam.mNamespace);
     WriteParam(aMsg, aParam.mHandle);
   }
 
   static bool
   Read(const Message* aMsg, PickleIterator* aIter, mozilla::wr::PipelineId* aResult)
   {
-    return ReadParam(aMsg, aIter, &aResult->mHandle);
+    return ReadParam(aMsg, aIter, &aResult->mNamespace)
+        && ReadParam(aMsg, aIter, &aResult->mHandle);
   }
 };
 
 template<>
 struct ParamTraits<WrImageFormat>
   : public ContiguousEnumSerializer<
         WrImageFormat,
         WrImageFormat::Invalid,
@@ -179,16 +185,34 @@ struct ParamTraits<WrGlyphArray>
       }
     }
 
     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);
@@ -261,16 +285,34 @@ struct ParamTraits<WrRect>
     return ReadParam(aMsg, aIter, &aResult->x)
         && ReadParam(aMsg, aIter, &aResult->y)
         && ReadParam(aMsg, aIter, &aResult->width)
         && ReadParam(aMsg, aIter, &aResult->height);
   }
 };
 
 template<>
+struct ParamTraits<WrPoint>
+{
+  static void
+  Write(Message* aMsg, const WrPoint& aParam)
+  {
+    WriteParam(aMsg, aParam.x);
+    WriteParam(aMsg, aParam.y);
+  }
+
+  static bool
+  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);
@@ -281,28 +323,46 @@ struct ParamTraits<WrImageMask>
   {
     return ReadParam(aMsg, aIter, &aResult->image)
         && ReadParam(aMsg, aIter, &aResult->rect)
         && ReadParam(aMsg, aIter, &aResult->repeat);
   }
 };
 
 template<>
-struct ParamTraits<WrTextureFilter>
+struct ParamTraits<WrImageRendering>
   : public ContiguousEnumSerializer<
-        WrTextureFilter,
-        WrTextureFilter::Linear,
-        WrTextureFilter::Sentinel>
+        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
@@ -1,18 +1,20 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebRenderPaintedLayer.h"
 
-#include "WebRenderLayersLogging.h"
+#include "LayersLogging.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+#include "mozilla/layers/TextureWrapperImage.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "gfxPrefs.h"
 #include "gfxUtils.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
@@ -69,16 +71,19 @@ WebRenderPaintedLayer::PaintThebes(nsTAr
     didUpdate = true;
   }
 
   mContentClient->EndPaint(aReadbackUpdates);
 
   if (didUpdate) {
     Mutated();
 
+    // XXX It will cause reftests failures. See Bug 1340798.
+    //mValidRegion.Or(mValidRegion, state.mRegionToDraw);
+
     ContentClientRemote* contentClientRemote = static_cast<ContentClientRemote*>(mContentClient.get());
 
     // Hold(this) ensures this layer is kept alive through the current transaction
     // The ContentClient assumes this layer is kept alive (e.g., in CreateBuffer),
     // so deleting this Hold for whatever reason will break things.
     Manager()->Hold(this);
 
     contentClientRemote->Updated(state.mRegionToDraw,
@@ -106,73 +111,132 @@ WebRenderPaintedLayer::RenderLayerWithRe
   }
 
   PaintThebes(&readbackUpdates);
 }
 
 void
 WebRenderPaintedLayer::RenderLayer()
 {
-  RenderLayerWithReadback(nullptr);
+  // 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();
+  }
+
+  if (!mImageClient) {
+    mImageClient = ImageClient::CreateImageClient(CompositableType::IMAGE,
+                                                  WrBridge(),
+                                                  TextureFlags::DEFAULT);
+    if (!mImageClient) {
+      return;
+    }
+    mImageClient->Connect();
+  }
 
   if (!mExternalImageId) {
-    mExternalImageId = WrBridge()->AllocExternalImageIdForCompositable(mContentClient);
+    mExternalImageId = WrBridge()->AllocExternalImageIdForCompositable(mImageClient);
     MOZ_ASSERT(mExternalImageId);
   }
 
   LayerIntRegion visibleRegion = GetVisibleRegion();
   LayerIntRect bounds = visibleRegion.GetBounds();
   LayerIntSize size = bounds.Size();
   if (size.IsEmpty()) {
       if (gfxPrefs::LayersDump()) {
         printf_stderr("PaintedLayer %p skipping\n", this->GetLayer());
       }
       return;
   }
 
+  IntSize imageSize(size.width, size.height);
+  RefPtr<TextureClient> texture = mImageClient->GetTextureClientRecycler()->CreateOrRecycle(SurfaceFormat::B8G8R8A8,
+                                                                                            imageSize,
+                                                                                            BackendSelector::Content,
+                                                                                            TextureFlags::DEFAULT);
+  if (!texture) {
+    return;
+  }
+
+  {
+    TextureClientAutoLock autoLock(texture, OpenMode::OPEN_WRITE_ONLY);
+    if (!autoLock.Succeeded()) {
+      return;
+    }
+    RefPtr<DrawTarget> target = texture->BorrowDrawTarget();
+    if (!target) {
+      return;
+    }
+    target->ClearRect(Rect(0, 0, imageSize.width, imageSize.height));
+    target->SetTransform(Matrix().PreTranslate(-bounds.x, -bounds.y));
+    RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(target);
+    MOZ_ASSERT(ctx); // already checked the target above
+
+    Manager()->GetPaintedLayerCallback()(this,
+                                         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 = RelativeToVisible(IntRectToRect(bounds.ToUnknownRect()));
+  Rect rect(0, 0, size.width, size.height);
   Rect clip;
   if (GetClipRect().isSome()) {
-      clip = RelativeToTransformedVisible(IntRectToRect(GetClipRect().ref().ToUnknownRect()));
+      clip = RelativeToVisible(transform.Inverse().TransformBounds(IntRectToRect(GetClipRect().ref().ToUnknownRect())));
   } else {
       clip = rect;
   }
 
   Maybe<WrImageMask> mask = buildMaskLayer();
-  Rect relBounds = TransformedVisibleBoundsRelativeToParent();
+  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);
-  Matrix4x4 transform;// = GetTransform();
   WrMixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
 
   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());
   }
 
-  ContentClientRemoteBuffer* contentClientRemote = static_cast<ContentClientRemoteBuffer*>(mContentClient.get());
-  visibleRegion.MoveBy(-contentClientRemote->BufferRect().x, -contentClientRemote->BufferRect().y);
-
   WrBridge()->AddWebRenderCommand(
       OpDPPushStackingContext(wr::ToWrRect(relBounds),
                               wr::ToWrRect(overflow),
                               mask,
                               1.0f,
                               GetAnimations(),
                               transform,
                               mixBlendMode,
                               FrameMetrics::NULL_SCROLL_ID));
-  WrBridge()->AddWebRenderCommand(OpDPPushExternalImageId(visibleRegion, wr::ToWrRect(rect), wr::ToWrRect(clip), Nothing(), WrTextureFilter::Linear, mExternalImageId));
+  WrBridge()->AddWebRenderCommand(OpDPPushExternalImageId(LayerIntRegion(), wr::ToWrRect(rect), wr::ToWrRect(clip), Nothing(), wr::ImageRendering::Auto, mExternalImageId));
+
   WrBridge()->AddWebRenderCommand(OpDPPopStackingContext());
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderPaintedLayer.h
+++ b/gfx/layers/wr/WebRenderPaintedLayer.h
@@ -50,14 +50,16 @@ public:
     mValidRegion.Sub(mValidRegion, mInvalidRegion.GetRegion());
   }
 
   Layer* GetLayer() override { return this; }
   void RenderLayer() override;
   void PaintThebes(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates);
   void RenderLayerWithReadback(ReadbackProcessor *aReadback);
   RefPtr<ContentClient> mContentClient;
+  RefPtr<ImageContainer> mImageContainer;
+  RefPtr<ImageClient> mImageClient;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // GFX_WEBRENDERPAINTEDLAYER_H
--- a/gfx/layers/wr/WebRenderTextLayer.cpp
+++ b/gfx/layers/wr/WebRenderTextLayer.cpp
@@ -1,92 +1,49 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebRenderTextLayer.h"
 
-#include "WebRenderLayersLogging.h"
 #include "gfxPrefs.h"
+#include "LayersLogging.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 
 #include "mozilla/gfx/2D.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
-static void
-DWriteFontFileData(const uint8_t* aData, uint32_t aLength, uint32_t aIndex,
-                   float aGlyphSize, uint32_t aVariationCount,
-                   const ScaledFont::VariationSetting* aVariations, void* aBaton)
-{
-    WebRenderTextLayer* layer = static_cast<WebRenderTextLayer*>(aBaton);
-
-    uint8_t* fontData = (uint8_t*)malloc(aLength * sizeof(uint8_t));
-    memcpy(fontData, aData, aLength * sizeof(uint8_t));
-
-    layer->mFontData = fontData;
-    layer->mFontDataLength = aLength;
-    layer->mIndex = aIndex;
-    layer->mGlyphSize = aGlyphSize;
-}
-
 void
 WebRenderTextLayer::RenderLayer()
 {
     if (mBounds.IsEmpty()) {
         return;
     }
 
     gfx::Rect rect = RelativeToParent(GetTransform().TransformBounds(IntRectToRect(mBounds)));
     gfx::Rect clip;
     if (GetClipRect().isSome()) {
       clip = RelativeToParent(IntRectToRect(GetClipRect().ref().ToUnknownRect()));
     } else {
       clip = rect;
     }
 
-    MOZ_ASSERT(mFont->GetType() == FontType::DWRITE);
-    mFont->GetFontFileData(&DWriteFontFileData, this);
-    wr::ByteBuffer fontBuffer(mFontDataLength, mFontData);
-
-    nsTArray<WrGlyphArray> wr_glyphs;
-    wr_glyphs.SetLength(mGlyphs.Length());
-
-    for (size_t i = 0; i < mGlyphs.Length(); i++) {
-        GlyphArray glyph_array = mGlyphs[i];
-        nsTArray<Glyph>& glyphs = glyph_array.glyphs();
-
-        nsTArray<WrGlyphInstance>& wr_glyph_instances = wr_glyphs[i].glyphs;
-        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;
-            wr_glyph_instances[j].y = glyphs[j].mPosition.y;
-        }
-    }
-
     if (gfxPrefs::LayersDump()) {
         printf_stderr("TextLayer %p using rect=%s, clip=%s\n",
                       this->GetLayer(),
                       Stringify(rect).c_str(),
                       Stringify(clip).c_str());
     }
 
-    WrBridge()->AddWebRenderCommand(OpDPPushText(
-        wr::ToWrRect(rect),
-        wr::ToWrRect(clip),
-        wr_glyphs,
-        mIndex,
-        mGlyphSize,
-        fontBuffer,
-        mFontDataLength
-    ));
+    nsTArray<WebRenderCommand> commands;
+    mGlyphHelper.BuildWebRenderCommands(commands, mGlyphs, mFont,
+                                        GetOffsetToParent(), rect, clip);
+    WrBridge()->AddWebRenderCommands(commands);
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderTextLayer.h
+++ b/gfx/layers/wr/WebRenderTextLayer.h
@@ -2,50 +2,42 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #ifndef GFX_WEBRENDERTEXTLAYER_H
 #define GFX_WEBRENDERTEXTLAYER_H
 
+#include "gfxUtils.h"
 #include "Layers.h"
 #include "WebRenderLayerManager.h"
 
 namespace mozilla {
 namespace layers {
 
 class WebRenderTextLayer : public WebRenderLayer,
                            public TextLayer {
 public:
     explicit WebRenderTextLayer(WebRenderLayerManager* aLayerManager)
         : TextLayer(aLayerManager, static_cast<WebRenderLayer*>(this))
-        , mFontData(nullptr)
-        , mFontDataLength(0)
-        , mIndex(0)
-        , mGlyphSize(0.0)
     {
         MOZ_COUNT_CTOR(WebRenderTextLayer);
     }
 
 protected:
     virtual ~WebRenderTextLayer()
     {
         MOZ_COUNT_DTOR(WebRenderTextLayer);
-        if (mFontData) {
-            free(mFontData);
-        }
     }
 
 public:
   Layer* GetLayer() override { return this; }
   void RenderLayer() override;
 
-  uint8_t* mFontData;
-  uint32_t mFontDataLength;
-  uint32_t mIndex;
-  float mGlyphSize;
+protected:
+  gfx::WebRenderGlyphHelper mGlyphHelper;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // GFX_WEBRENDERTEXTLAYER_H
\ No newline at end of file
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -453,16 +453,18 @@ private:
   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-outer-layers", LayersAllowOuterBoxShadow, bool, false);
   DECL_GFX_PREF(Once, "layers.allow-d3d9-fallback",            LayersAllowD3D9Fallback, 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
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -16,20 +16,22 @@
 #include "mozilla/dom/ImageEncoder.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/DataSurfaceHelpers.h"
 #include "mozilla/gfx/Logging.h"
 #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 "nsComponentManagerUtils.h"
 #include "nsIClipboardHelper.h"
 #include "nsIFile.h"
 #include "nsIGfxInfo.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsRegion.h"
 #include "nsServiceManagerUtils.h"
@@ -1434,10 +1436,70 @@ Color ToDeviceColor(Color aColor)
   return aColor;
 }
 
 Color ToDeviceColor(nscolor aColor)
 {
   return ToDeviceColor(Color::FromABGR(aColor));
 }
 
+static void
+WriteFontFileData(const uint8_t* aData, uint32_t aLength, uint32_t aIndex,
+                  float aGlyphSize, uint32_t aVariationCount,
+                  const ScaledFont::VariationSetting* aVariations, void* aBaton)
+{
+  WebRenderGlyphHelper* helper = static_cast<WebRenderGlyphHelper*>(aBaton);
+
+  uint8_t* fontData = (uint8_t*)malloc(aLength * sizeof(uint8_t));
+  memcpy(fontData, aData, aLength * sizeof(uint8_t));
+
+  helper->mFontData = fontData;
+  helper->mFontDataLength = aLength;
+  helper->mIndex = aIndex;
+  helper->mGlyphSize = aGlyphSize;
+}
+
+void
+WebRenderGlyphHelper::BuildWebRenderCommands(nsTArray<WebRenderCommand>& aCommands,
+                                             const nsTArray<GlyphArray>& aGlyphs,
+                                             ScaledFont* aFont,
+                                             const Point& aOffset,
+                                             const Rect& aBounds,
+                                             const Rect& aClip)
+{
+  MOZ_ASSERT(aFont);
+  MOZ_ASSERT(!aGlyphs.IsEmpty());
+  MOZ_ASSERT((aFont->GetType() == gfx::FontType::DWRITE) ||
+              (aFont->GetType() == gfx::FontType::MAC));
+
+  aFont->GetFontFileData(&WriteFontFileData, this);
+  wr::ByteBuffer fontBuffer(mFontDataLength, mFontData);
+
+  nsTArray<WrGlyphArray> wr_glyphs;
+  wr_glyphs.SetLength(aGlyphs.Length());
+
+  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;
+    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;
+    }
+  }
+
+  aCommands.AppendElement(OpDPPushText(
+                            wr::ToWrRect(aBounds),
+                            wr::ToWrRect(aClip),
+                            wr_glyphs,
+                            mIndex,
+                            mGlyphSize,
+                            fontBuffer,
+                            mFontDataLength));
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -21,17 +21,19 @@
 class gfxASurface;
 class gfxDrawable;
 class nsIInputStream;
 class nsIGfxInfo;
 class nsIPresShell;
 
 namespace mozilla {
 namespace layers {
+class GlyphArray;
 struct PlanarYCbCrData;
+class WebRenderCommand;
 } // namespace layers
 namespace image {
 class ImageRegion;
 } // namespace image
 } // namespace mozilla
 
 class gfxUtils {
 public:
@@ -301,12 +303,43 @@ static inline CheckedInt<uint32_t>
 SafeBytesForBitmap(uint32_t aWidth, uint32_t aHeight, unsigned aBytesPerPixel)
 {
   MOZ_ASSERT(aBytesPerPixel > 0);
   CheckedInt<uint32_t> width = uint32_t(aWidth);
   CheckedInt<uint32_t> height = uint32_t(aHeight);
   return width * height * aBytesPerPixel;
 }
 
+class WebRenderGlyphHelper final {
+public:
+  WebRenderGlyphHelper()
+    : mFontData(nullptr)
+    , mFontDataLength(0)
+    , mIndex(0)
+    , mGlyphSize(0.0)
+  {
+  }
+
+  ~WebRenderGlyphHelper()
+  {
+    if (mFontData) {
+      free(mFontData);
+    }
+  }
+
+  void BuildWebRenderCommands(nsTArray<layers::WebRenderCommand>& aCommands,
+                              const nsTArray<layers::GlyphArray>& aGlyphs,
+                              ScaledFont* aFont,
+                              const Point& aOffset,
+                              const Rect& aBounds,
+                              const Rect& aClip);
+
+public:
+  uint8_t* mFontData;
+  uint32_t mFontDataLength;
+  uint32_t mIndex;
+  float mGlyphSize;
+};
+
 } // namespace gfx
 } // namespace mozilla
 
 #endif
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -1,44 +1,46 @@
 [package]
 name = "webrender"
-version = "0.11.1"
+version = "0.15.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 build = "build.rs"
-workspace = ".."
 
 [features]
 default = ["codegen", "freetype-lib"]
 codegen = ["webrender_traits/codegen"]
 freetype-lib = ["freetype/servo-freetype-sys"]
 serde_derive = ["webrender_traits/serde_derive"]
+profiler = ["thread_profiler/thread_profiler"]
 
 [dependencies]
 app_units = "0.3"
 bincode = "0.6"
 bit-set = "0.4"
-byteorder = "0.5"
+byteorder = "1.0"
 euclid = "0.10.3"
 fnv="1.0"
 gleam = "0.2.30"
 lazy_static = "0.2"
 log = "0.3"
 num-traits = "0.1.32"
 offscreen_gl_context = {version = "0.5", features = ["serde_serialization", "osmesa"]}
 time = "0.1"
 threadpool = "1.3.2"
 webrender_traits = {path = "../webrender_traits", default-features = false}
 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]
-dwrote = "0.1.5"
+dwrote = "0.1.7"
 
 [target.'cfg(target_os = "macos")'.dependencies]
-core-graphics = "0.5.0"
-core-text = "2.0"
+core-graphics = "0.6.0"
+core-text = "3.0"
--- a/gfx/webrender/res/cs_clip_image.fs.glsl
+++ b/gfx/webrender/res/cs_clip_image.fs.glsl
@@ -4,13 +4,14 @@
 
 void main(void) {
     float alpha = 1.f;
     vec2 local_pos = init_transform_fs(vPos, vLocalRect, alpha);
 
     bool repeat_mask = false; //TODO
     vec2 clamped_mask_uv = repeat_mask ? fract(vClipMaskUv.xy) :
         clamp(vClipMaskUv.xy, vec2(0.0, 0.0), vec2(1.0, 1.0));
-    vec2 source_uv = clamped_mask_uv * vClipMaskUvRect.zw + vClipMaskUvRect.xy;
+    vec2 source_uv = clamp(clamped_mask_uv * vClipMaskUvRect.zw + vClipMaskUvRect.xy,
+        vClipMaskUvInnerRect.xy, vClipMaskUvInnerRect.zw);
     float clip_alpha = texture(sMask, source_uv).r; //careful: texture has type A8
 
     oFragColor = vec4(min(alpha, clip_alpha), 1.0, 1.0, 1.0);
 }
--- a/gfx/webrender/res/cs_clip_image.glsl
+++ b/gfx/webrender/res/cs_clip_image.glsl
@@ -2,8 +2,9 @@
 
 /* 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/. */
 
 varying vec3 vPos;
 flat varying vec4 vLocalRect;
 flat varying vec4 vClipMaskUvRect;
+flat varying vec4 vClipMaskUvInnerRect;
--- a/gfx/webrender/res/cs_clip_image.vs.glsl
+++ b/gfx/webrender/res/cs_clip_image.vs.glsl
@@ -31,9 +31,12 @@ void main(void) {
                                                     area,
                                                     cci.segment_index);
     vLocalRect = vi.clipped_local_rect;
     vPos = vi.local_pos;
 
     vClipMaskUv = vec3((vPos.xy / vPos.z - local_rect.xy) / local_rect.zw, 0.0);
     vec2 texture_size = vec2(textureSize(sMask, 0));
     vClipMaskUvRect = mask.uv_rect / texture_size.xyxy;
+    // applying a half-texel offset to the UV boundaries to prevent linear samples from the outside
+    vec4 inner_rect = vec4(mask.uv_rect.xy, mask.uv_rect.xy + mask.uv_rect.zw);
+    vClipMaskUvInnerRect = (inner_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy;
 }
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -25,34 +25,30 @@
 #define BORDER_LEFT      0
 #define BORDER_TOP       1
 #define BORDER_RIGHT     2
 #define BORDER_BOTTOM    3
 
 #define UV_NORMALIZED    uint(0)
 #define UV_PIXEL         uint(1)
 
-#define MAX_STOPS_PER_ANGLE_GRADIENT 8
-#define MAX_STOPS_PER_RADIAL_GRADIENT 8
+#define EXTEND_MODE_CLAMP  0
+#define EXTEND_MODE_REPEAT 1
 
 uniform sampler2DArray sCache;
 
 flat varying vec4 vClipMaskUvBounds;
 varying vec3 vClipMaskUv;
 
 #ifdef WR_VERTEX_SHADER
 
 #define VECS_PER_LAYER             13
 #define VECS_PER_RENDER_TASK        3
 #define VECS_PER_PRIM_GEOM          2
 
-#define GRADIENT_HORIZONTAL     0
-#define GRADIENT_VERTICAL       1
-#define GRADIENT_ROTATED        2
-
 uniform sampler2D sLayers;
 uniform sampler2D sRenderTasks;
 uniform sampler2D sPrimGeometry;
 
 uniform sampler2D sData16;
 uniform sampler2D sData32;
 uniform sampler2D sData64;
 uniform sampler2D sData128;
@@ -141,29 +137,33 @@ RenderTaskData fetch_render_task(int ind
 
     task.data0 = texelFetchOffset(sRenderTasks, uv, 0, ivec2(0, 0));
     task.data1 = texelFetchOffset(sRenderTasks, uv, 0, ivec2(1, 0));
     task.data2 = texelFetchOffset(sRenderTasks, uv, 0, ivec2(2, 0));
 
     return task;
 }
 
-struct Tile {
-    vec4 screen_origin_task_origin;
-    vec4 size_target_index;
+struct AlphaBatchTask {
+    vec2 screen_space_origin;
+    vec2 render_target_origin;
+    vec2 size;
+    float render_target_layer_index;
 };
 
-Tile fetch_tile(int index) {
-    RenderTaskData task = fetch_render_task(index);
+AlphaBatchTask fetch_alpha_batch_task(int index) {
+    RenderTaskData data = fetch_render_task(index);
 
-    Tile tile;
-    tile.screen_origin_task_origin = task.data0;
-    tile.size_target_index = task.data1;
+    AlphaBatchTask task;
+    task.render_target_origin = data.data0.xy;
+    task.size = data.data0.zw;
+    task.screen_space_origin = data.data1.xy;
+    task.render_target_layer_index = data.data1.z;
 
-    return tile;
+    return task;
 }
 
 struct ClipArea {
     vec4 task_bounds;
     vec4 screen_origin_target_index;
     vec4 inner_rect;
 };
 
@@ -181,26 +181,26 @@ ClipArea fetch_clip_area(int index) {
         area.inner_rect = task.data2;
     }
 
     return area;
 }
 
 struct Gradient {
     vec4 start_end_point;
-    vec4 kind;
+    vec4 extend_mode;
 };
 
 Gradient fetch_gradient(int index) {
     Gradient gradient;
 
     ivec2 uv = get_fetch_uv_2(index);
 
     gradient.start_end_point = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
-    gradient.kind = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
+    gradient.extend_mode = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
 
     return gradient;
 }
 
 struct GradientStop {
     vec4 color;
     vec4 offset;
 };
@@ -213,26 +213,26 @@ GradientStop fetch_gradient_stop(int ind
     stop.color = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
     stop.offset = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
 
     return stop;
 }
 
 struct RadialGradient {
     vec4 start_end_center;
-    vec4 start_end_radius;
+    vec4 start_end_radius_extend_mode;
 };
 
 RadialGradient fetch_radial_gradient(int index) {
     RadialGradient gradient;
 
     ivec2 uv = get_fetch_uv_2(index);
 
     gradient.start_end_center = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
-    gradient.start_end_radius = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
+    gradient.start_end_radius_extend_mode = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
 
     return gradient;
 }
 
 struct Glyph {
     vec4 offset;
 };
 
@@ -315,34 +315,34 @@ CachePrimitiveInstance fetch_cache_insta
     cpi.sub_index = pi.sub_index;
     cpi.user_data = pi.user_data;
 
     return cpi;
 }
 
 struct Primitive {
     Layer layer;
-    Tile tile;
     ClipArea clip_area;
+    AlphaBatchTask task;
     vec4 local_rect;
     vec4 local_clip_rect;
     int prim_index;
     // when sending multiple primitives of the same type (e.g. border segments)
     // this index allows the vertex shader to recognize the difference
     int sub_index;
     ivec2 user_data;
     float z;
 };
 
 Primitive load_primitive_custom(PrimitiveInstance pi) {
     Primitive prim;
 
     prim.layer = fetch_layer(pi.layer_index);
-    prim.tile = fetch_tile(pi.render_task_index);
     prim.clip_area = fetch_clip_area(pi.clip_task_index);
+    prim.task = fetch_alpha_batch_task(pi.render_task_index);
 
     PrimitiveGeometry pg = fetch_prim_geometry(pi.global_prim_index);
     prim.local_rect = pg.local_rect;
     prim.local_clip_rect = pg.local_clip_rect;
 
     prim.prim_index = pi.specific_prim_index;
     prim.sub_index = pi.sub_index;
     prim.user_data = pi.user_data;
@@ -408,69 +408,84 @@ vec2 clamp_rect(vec2 point, vec4 rect) {
 
 struct Rect {
     vec2 p0;
     vec2 p1;
 };
 
 struct VertexInfo {
     Rect local_rect;
-    vec2 local_clamped_pos;
-    vec2 global_clamped_pos;
+    vec2 local_pos;
+    vec2 screen_pos;
 };
 
 VertexInfo write_vertex(vec4 instance_rect,
                         vec4 local_clip_rect,
                         float z,
                         Layer layer,
-                        Tile tile) {
-    vec2 p0 = floor(0.5 + instance_rect.xy * uDevicePixelRatio) / uDevicePixelRatio;
-    vec2 p1 = floor(0.5 + (instance_rect.xy + instance_rect.zw) * uDevicePixelRatio) / uDevicePixelRatio;
+                        AlphaBatchTask task) {
+    // Get the min/max local space coords of the rectangle.
+    vec2 local_p0 = instance_rect.xy;
+    vec2 local_p1 = instance_rect.xy + instance_rect.zw;
 
-    vec2 local_pos = mix(p0, p1, aPosition.xy);
+    // Get the min/max coords of the local space clip rect.
+    vec2 local_clip_p0 = local_clip_rect.xy;
+    vec2 local_clip_p1 = local_clip_rect.xy + local_clip_rect.zw;
+
+    // Get the min/max coords of the layer clip rect.
+    vec2 layer_clip_p0 = layer.local_clip_rect.xy;
+    vec2 layer_clip_p1 = layer.local_clip_rect.xy + layer.local_clip_rect.zw;
 
-    vec2 cp0 = floor(0.5 + local_clip_rect.xy * uDevicePixelRatio) / uDevicePixelRatio;
-    vec2 cp1 = floor(0.5 + (local_clip_rect.xy + local_clip_rect.zw) * uDevicePixelRatio) / uDevicePixelRatio;
-    local_pos = clamp(local_pos, cp0, cp1);
+    // Select the corner of the local rect that we are processing.
+    vec2 local_pos = mix(local_p0, local_p1, aPosition.xy);
+
+    // xy = top left corner of the local rect, zw = position of current vertex.
+    vec4 local_p0_pos = vec4(local_p0, local_pos);
 
-    local_pos = clamp_rect(local_pos, layer.local_clip_rect);
+    // Clamp to the two local clip rects.
+    local_p0_pos = clamp(local_p0_pos, local_clip_p0.xyxy, local_clip_p1.xyxy);
+    local_p0_pos = clamp(local_p0_pos, layer_clip_p0.xyxy, layer_clip_p1.xyxy);
 
-    vec4 world_pos = layer.transform * vec4(local_pos, 0.0, 1.0);
+    // Transform the top corner and current vertex to world space.
+    vec4 world_p0 = layer.transform * vec4(local_p0_pos.xy, 0.0, 1.0);
+    world_p0.xyz /= world_p0.w;
+    vec4 world_pos = layer.transform * vec4(local_p0_pos.zw, 0.0, 1.0);
     world_pos.xyz /= world_pos.w;
 
-    vec2 device_pos = world_pos.xy * uDevicePixelRatio;
+    // Convert the world positions to device pixel space. xy=top left corner. zw=current vertex.
+    vec4 device_p0_pos = vec4(world_p0.xy, world_pos.xy) * uDevicePixelRatio;
+
+    // Calculate the distance to snap the vertex by (snap top left corner).
+    vec2 snap_delta = device_p0_pos.xy - floor(device_p0_pos.xy + 0.5);
 
-    vec2 clamped_pos = clamp(device_pos,
-                             tile.screen_origin_task_origin.xy,
-                             tile.screen_origin_task_origin.xy + tile.size_target_index.xy);
-
-    vec4 local_clamped_pos = layer.inv_transform * vec4(clamped_pos / uDevicePixelRatio, world_pos.z, 1);
-    local_clamped_pos.xyz /= local_clamped_pos.w;
-
-    vec2 final_pos = clamped_pos + tile.screen_origin_task_origin.zw - tile.screen_origin_task_origin.xy;
+    // Apply offsets for the render task to get correct screen location.
+    vec2 final_pos = device_p0_pos.zw -
+                     snap_delta -
+                     task.screen_space_origin +
+                     task.render_target_origin;
 
     gl_Position = uTransform * vec4(final_pos, z, 1.0);
 
-    VertexInfo vi = VertexInfo(Rect(p0, p1), local_clamped_pos.xy, clamped_pos.xy);
+    VertexInfo vi = VertexInfo(Rect(local_p0, local_p1), local_p0_pos.zw, device_p0_pos.zw);
     return vi;
 }
 
 #ifdef WR_FEATURE_TRANSFORM
 
 struct TransformVertexInfo {
     vec3 local_pos;
-    vec2 global_clamped_pos;
+    vec2 screen_pos;
     vec4 clipped_local_rect;
 };
 
 TransformVertexInfo write_transform_vertex(vec4 instance_rect,
                                            vec4 local_clip_rect,
                                            float z,
                                            Layer layer,
-                                           Tile tile) {
+                                           AlphaBatchTask task) {
     vec2 lp0_base = instance_rect.xy;
     vec2 lp1_base = instance_rect.xy + instance_rect.zw;
 
     vec2 lp0 = clamp_rect(clamp_rect(lp0_base, local_clip_rect),
                           layer.local_clip_rect);
     vec2 lp1 = clamp_rect(clamp_rect(lp1_base, local_clip_rect),
                           layer.local_clip_rect);
 
@@ -487,42 +502,31 @@ TransformVertexInfo write_transform_vert
     vec4 t3 = layer.transform * vec4(p3, 0, 1);
 
     vec2 tp0 = t0.xy / t0.w;
     vec2 tp1 = t1.xy / t1.w;
     vec2 tp2 = t2.xy / t2.w;
     vec2 tp3 = t3.xy / t3.w;
 
     // compute a CSS space aligned bounding box
-    vec2 min_pos = min(min(tp0.xy, tp1.xy), min(tp2.xy, tp3.xy));
-    vec2 max_pos = max(max(tp0.xy, tp1.xy), max(tp2.xy, tp3.xy));
-
-    // clamp to the tile boundaries, in device space
-    vec2 min_pos_clamped = clamp(min_pos * uDevicePixelRatio,
-                                 tile.screen_origin_task_origin.xy,
-                                 tile.screen_origin_task_origin.xy + tile.size_target_index.xy);
-
-    vec2 max_pos_clamped = clamp(max_pos * uDevicePixelRatio,
-                                 tile.screen_origin_task_origin.xy,
-                                 tile.screen_origin_task_origin.xy + tile.size_target_index.xy);
+    vec2 min_pos = uDevicePixelRatio * min(min(tp0.xy, tp1.xy), min(tp2.xy, tp3.xy));
+    vec2 max_pos = uDevicePixelRatio * max(max(tp0.xy, tp1.xy), max(tp2.xy, tp3.xy));
 
     // compute the device space position of this vertex
-    vec2 clamped_pos = mix(min_pos_clamped,
-                           max_pos_clamped,
-                           aPosition.xy);
+    vec2 device_pos = mix(min_pos, max_pos, aPosition.xy);
 
     // compute the point position in side the layer, in CSS space
-    vec4 layer_pos = get_layer_pos(clamped_pos / uDevicePixelRatio, layer);
+    vec4 layer_pos = get_layer_pos(device_pos / uDevicePixelRatio, layer);
 
     // apply the task offset
-    vec2 final_pos = clamped_pos + tile.screen_origin_task_origin.zw - tile.screen_origin_task_origin.xy;
+    vec2 final_pos = device_pos - task.screen_space_origin + task.render_target_origin;
 
     gl_Position = uTransform * vec4(final_pos, z, 1.0);
 
-    return TransformVertexInfo(layer_pos.xyw, clamped_pos, clipped_local_rect);
+    return TransformVertexInfo(layer_pos.xyw, device_pos, clipped_local_rect);
 }
 
 #endif //WR_FEATURE_TRANSFORM
 
 struct ResourceRect {
     vec4 uv_rect;
 };
 
--- a/gfx/webrender/res/ps_angle_gradient.fs.glsl
+++ b/gfx/webrender/res/ps_angle_gradient.fs.glsl
@@ -1,39 +1,21 @@
 /* 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/. */
 
-float offset(int index) {
-    return vOffsets[index / 4][index % 4];
-}
-
-float linearStep(float lo, float hi, float x) {
-    float d = hi - lo;
-    float v = x - lo;
-    if (d != 0.0) {
-        v /= d;
-    }
-    return clamp(v, 0.0, 1.0);
-}
+uniform sampler2D sGradients;
 
 void main(void) {
-    float angle = atan(-vEndPoint.y + vStartPoint.y,
-                        vEndPoint.x - vStartPoint.x);
-    float sa = sin(angle);
-    float ca = cos(angle);
+    vec2 texture_size = vec2(textureSize(sGradients, 0));
 
-    float sx = vStartPoint.x * ca - vStartPoint.y * sa;
-    float ex = vEndPoint.x * ca - vEndPoint.y * sa;
-    float d = ex - sx;
-
-    float x = vPos.x * ca - vPos.y * sa;
+    // Either saturate or modulo the offset depending on repeat mode, then scale to number of
+    // gradient color entries (texture width / 2).
+    float x = mix(clamp(vOffset, 0.0, 1.0), fract(vOffset), vGradientRepeat) * 0.5 * texture_size.x;
 
-    oFragColor = mix(vColors[0],
-                     vColors[1],
-                     linearStep(sx + d * offset(0), sx + d * offset(1), x));
+    // Start at the center of first color in the nearest 2-color entry, then offset with the
+    // fractional remainder to interpolate between the colors. Rely on texture clamping when
+    // outside of valid range.
+    x = 2.0 * floor(x) + 0.5 + fract(x);
 
-    for (int i=1 ; i < vStopCount-1 ; ++i) {
-        oFragColor = mix(oFragColor,
-                         vColors[i+1],
-                         linearStep(sx + d * offset(i), sx + d * offset(i+1), x));
-    }
+    // Normalize the texture coordates so we can use texture() for bilinear filtering.
+    oFragColor = texture(sGradients, vec2(x, vGradientIndex) / texture_size);
 }
--- a/gfx/webrender/res/ps_angle_gradient.glsl
+++ b/gfx/webrender/res/ps_angle_gradient.glsl
@@ -1,11 +1,7 @@
 /* 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/. */
 
-flat varying int vStopCount;
-flat varying float vAngle;
-flat varying vec2 vStartPoint;
-flat varying vec2 vEndPoint;
-varying vec2 vPos;
-flat varying vec4 vColors[MAX_STOPS_PER_ANGLE_GRADIENT];
-flat varying vec4 vOffsets[MAX_STOPS_PER_ANGLE_GRADIENT/4];
+flat varying float vGradientIndex;
+flat varying float vGradientRepeat;
+varying float vOffset;
--- a/gfx/webrender/res/ps_angle_gradient.vs.glsl
+++ b/gfx/webrender/res/ps_angle_gradient.vs.glsl
@@ -6,28 +6,29 @@
 void main(void) {
     Primitive prim = load_primitive();
     Gradient gradient = fetch_gradient(prim.prim_index);
 
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.tile);
-
-    vStopCount = int(prim.user_data.x);
-    vPos = vi.local_clamped_pos;
+                                 prim.task);
 
     // Snap the start/end points to device pixel units.
     // I'm not sure this is entirely correct, but the
     // old render path does this, and it is needed to
     // make the angle gradient ref tests pass. It might
     // be better to fix this higher up in DL construction
     // and not snap here?
-    vStartPoint = floor(0.5 + gradient.start_end_point.xy * uDevicePixelRatio) / uDevicePixelRatio;
-    vEndPoint = floor(0.5 + gradient.start_end_point.zw * uDevicePixelRatio) / uDevicePixelRatio;
+    vec2 start_point = floor(0.5 + gradient.start_end_point.xy * uDevicePixelRatio) / uDevicePixelRatio;
+    vec2 end_point = floor(0.5 + gradient.start_end_point.zw * uDevicePixelRatio) / uDevicePixelRatio;
+
+    vec2 dir = end_point - start_point;
+    // Normalized offset of this vertex within the gradient, before clamp/repeat.
+    vOffset = dot(vi.local_pos - start_point, dir) / dot(dir, dir);
 
-    for (int i=0 ; i < vStopCount ; ++i) {
-        GradientStop stop = fetch_gradient_stop(prim.sub_index + i);
-        vColors[i] = stop.color;
-        vOffsets[i/4][i%4] = stop.offset.x;
-    }
+    // V coordinate of gradient row in lookup texture.
+    vGradientIndex = float(prim.sub_index) + 0.5;
+
+    // Whether to repeat the gradient instead of clamping.
+    vGradientRepeat = float(int(gradient.extend_mode.x) == EXTEND_MODE_REPEAT);
 }
--- a/gfx/webrender/res/ps_blend.vs.glsl
+++ b/gfx/webrender/res/ps_blend.vs.glsl
@@ -1,46 +1,28 @@
 #line 1
 /* 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/. */
 
-struct Blend {
-    ivec4 src_id_target_id_op_amount;
-    int z;
-};
-
-Blend fetch_blend() {
+void main(void) {
     PrimitiveInstance pi = fetch_prim_instance();
+    AlphaBatchTask dest_task = fetch_alpha_batch_task(pi.render_task_index);
+    AlphaBatchTask src_task = fetch_alpha_batch_task(pi.user_data.x);
 
-    Blend blend;
-    blend.src_id_target_id_op_amount = ivec4(pi.user_data.x,
-                                             pi.render_task_index,
-                                             pi.sub_index,
-                                             pi.user_data.y);
-    blend.z = pi.z;
-
-    return blend;
-}
-
-void main(void) {
-    Blend blend = fetch_blend();
-    Tile src = fetch_tile(blend.src_id_target_id_op_amount.x);
-    Tile dest = fetch_tile(blend.src_id_target_id_op_amount.y);
-
-    vec2 dest_origin = dest.screen_origin_task_origin.zw -
-                       dest.screen_origin_task_origin.xy +
-                       src.screen_origin_task_origin.xy;
+    vec2 dest_origin = dest_task.render_target_origin -
+                       dest_task.screen_space_origin +
+                       src_task.screen_space_origin;
 
     vec2 local_pos = mix(dest_origin,
-                         dest_origin + src.size_target_index.xy,
+                         dest_origin + src_task.size,
                          aPosition.xy);
 
     vec2 texture_size = vec2(textureSize(sCache, 0));
-    vec2 st0 = src.screen_origin_task_origin.zw / texture_size;
-    vec2 st1 = (src.screen_origin_task_origin.zw + src.size_target_index.xy) / texture_size;
-    vUv = vec3(mix(st0, st1, aPosition.xy), src.size_target_index.z);
+    vec2 st0 = src_task.render_target_origin / texture_size;
+    vec2 st1 = (src_task.render_target_origin + src_task.size) / texture_size;
+    vUv = vec3(mix(st0, st1, aPosition.xy), src_task.render_target_layer_index);
 
-    vOp = blend.src_id_target_id_op_amount.z;
-    vAmount = float(blend.src_id_target_id_op_amount.w) / 65535.0;
+    vOp = pi.sub_index;
+    vAmount = float(pi.user_data.y) / 65535.0;
 
-    gl_Position = uTransform * vec4(local_pos, blend.z, 1.0);
+    gl_Position = uTransform * vec4(local_pos, pi.z, 1.0);
 }
--- a/gfx/webrender/res/ps_border.vs.glsl
+++ b/gfx/webrender/res/ps_border.vs.glsl
@@ -133,28 +133,28 @@ void main(void) {
             break;
     }
 
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(segment_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
-                                                    prim.tile);
+                                                    prim.task);
     vLocalPos = vi.local_pos;
 
     // Local space
     vLocalRect = vi.clipped_local_rect;
 #else
     VertexInfo vi = write_vertex(segment_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.tile);
-    vLocalPos = vi.local_clamped_pos.xy;
+                                 prim.task);
+    vLocalPos = vi.local_pos.xy;
 
     // Local space
     vLocalRect = prim.local_rect;
 #endif
 
     float x0, y0, x1, y1;
     switch (sub_part) {
         // These are the layer tile part PrimitivePart as uploaded by the tiling.rs
@@ -212,15 +212,15 @@ void main(void) {
 
     // The fragment shader needs to calculate the distance from the bisecting line
     // to properly mix border colors. For transformed borders, we calculate this distance
     // in the fragment shader itself. For non-transformed borders, we can use the
     // interpolator.
 #ifdef WR_FEATURE_TRANSFORM
     vPieceRectHypotenuseLength = sqrt(pow(width, 2.0) + pow(height, 2.0));
 #else
-    vDistanceFromMixLine = (vi.local_clamped_pos.x - x0) * height -
-                           (vi.local_clamped_pos.y - y0) * width;
-    vDistanceFromMiddle = (vi.local_clamped_pos.x - vLocalRect.x) +
-                          (vi.local_clamped_pos.y - vLocalRect.y) -
+    vDistanceFromMixLine = (vi.local_pos.x - x0) * height -
+                           (vi.local_pos.y - y0) * width;
+    vDistanceFromMiddle = (vi.local_pos.x - vLocalRect.x) +
+                          (vi.local_pos.y - vLocalRect.y) -
                           0.5 * (vLocalRect.z + vLocalRect.w);
 #endif
 }
--- a/gfx/webrender/res/ps_box_shadow.vs.glsl
+++ b/gfx/webrender/res/ps_box_shadow.vs.glsl
@@ -7,26 +7,26 @@ void main(void) {
     Primitive prim = load_primitive();
     BoxShadow bs = fetch_boxshadow(prim.prim_index);
     vec4 segment_rect = fetch_instance_geometry(prim.sub_index);
 
     VertexInfo vi = write_vertex(segment_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.tile);
+                                 prim.task);
 
     RenderTaskData child_task = fetch_render_task(prim.user_data.x);
     vUv.z = child_task.data1.x;
 
     // Constant offsets to inset from bilinear filtering border.
     vec2 patch_origin = child_task.data0.xy + vec2(1.0);
     vec2 patch_size_device_pixels = child_task.data0.zw - vec2(2.0);
     vec2 patch_size = patch_size_device_pixels / uDevicePixelRatio;
 
-    vUv.xy = (vi.local_clamped_pos - prim.local_rect.xy) / patch_size;
+    vUv.xy = (vi.local_pos - prim.local_rect.xy) / patch_size;
     vMirrorPoint = 0.5 * prim.local_rect.zw / patch_size;
 
     vec2 texture_size = vec2(textureSize(sCache, 0));
     vCacheUvRectCoords = vec4(patch_origin, patch_origin + patch_size_device_pixels) / texture_size.xyxy;
 
     vColor = bs.color;
 }
--- a/gfx/webrender/res/ps_cache_image.vs.glsl
+++ b/gfx/webrender/res/ps_cache_image.vs.glsl
@@ -8,21 +8,21 @@
 
 void main(void) {
     Primitive prim = load_primitive();
 
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.tile);
+                                 prim.task);
 
     RenderTaskData child_task = fetch_render_task(prim.user_data.x);
     vUv.z = child_task.data1.x;
 
     vec2 texture_size = vec2(textureSize(sCache, 0));
     vec2 uv0 = child_task.data0.xy / texture_size;
     vec2 uv1 = (child_task.data0.xy + child_task.data0.zw) / texture_size;
 
-    vec2 f = (vi.local_clamped_pos - prim.local_rect.xy) / prim.local_rect.zw;
+    vec2 f = (vi.local_pos - prim.local_rect.xy) / prim.local_rect.zw;
 
     vUv.xy = mix(uv0, uv1, f);
 }
--- a/gfx/webrender/res/ps_composite.fs.glsl
+++ b/gfx/webrender/res/ps_composite.fs.glsl
@@ -148,40 +148,39 @@ vec3 Color(vec3 Cb, vec3 Cs) {
 }
 
 vec3 Luminosity(vec3 Cb, vec3 Cs) {
     return SetLum(Cb, Lum(Cs));
 }
 
 void main(void) {
     vec4 Cb = texture(sCache, vUv0);
-
-    if (vUv1.x < vUv1Rect.x ||
-        vUv1.x > vUv1Rect.z ||
-        vUv1.y < vUv1Rect.y ||
-        vUv1.y > vUv1Rect.w) {
-        oFragColor = Cb;
-        return;
-    }
-
     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:
             result.rgb = Multiply(Cb.rgb, Cs.rgb);
             break;
         case 2:
             result.rgb = Screen(Cb.rgb, Cs.rgb);
             break;
         case 3:
             result.rgb = HardLight(Cs.rgb, Cb.rgb);        // Overlay is inverse of Hardlight
             break;
+        case 4:
+            // mix-blend-mode: darken
+            result.rgb = min(Cs.rgb, Cb.rgb);
+            break;
+        case 5:
+            // mix-blend-mode: lighten
+            result.rgb = max(Cs.rgb, Cb.rgb);
+            break;
         case 6:
             result.r = ColorDodge(Cb.r, Cs.r);
             result.g = ColorDodge(Cb.g, Cs.g);
             result.b = ColorDodge(Cb.b, Cs.b);
             break;
         case 7:
             result.r = ColorBurn(Cb.r, Cs.r);
             result.g = ColorBurn(Cb.g, Cs.g);
--- a/gfx/webrender/res/ps_composite.glsl
+++ b/gfx/webrender/res/ps_composite.glsl
@@ -1,8 +1,7 @@
 /* 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/. */
 
 varying vec3 vUv0;
 varying vec3 vUv1;
-flat varying vec4 vUv1Rect;
 flat varying int vOp;
--- a/gfx/webrender/res/ps_composite.vs.glsl
+++ b/gfx/webrender/res/ps_composite.vs.glsl
@@ -1,50 +1,34 @@
 #line 1
 /* 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/. */
 
-struct Composite {
-    ivec4 src0_src1_target_id_op;
-    int z;
-};
-
-Composite fetch_composite() {
+void main(void) {
     PrimitiveInstance pi = fetch_prim_instance();
+    AlphaBatchTask dest_task = fetch_alpha_batch_task(pi.render_task_index);
+    AlphaBatchTask backdrop_task = fetch_alpha_batch_task(pi.user_data.x);
+    AlphaBatchTask src_task = fetch_alpha_batch_task(pi.user_data.y);
 
-    Composite composite;
-    composite.src0_src1_target_id_op = ivec4(pi.user_data.xy,
-                                             pi.render_task_index,
-                                             pi.sub_index);
-    composite.z = pi.z;
-
-    return composite;
-}
+    vec2 dest_origin = dest_task.render_target_origin -
+                       dest_task.screen_space_origin +
+                       src_task.screen_space_origin;
 
-void main(void) {
-    Composite composite = fetch_composite();
-    Tile src0 = fetch_tile(composite.src0_src1_target_id_op.x);
-    Tile src1 = fetch_tile(composite.src0_src1_target_id_op.y);
-    Tile dest = fetch_tile(composite.src0_src1_target_id_op.z);
-
-    vec2 local_pos = mix(dest.screen_origin_task_origin.zw,
-                         dest.screen_origin_task_origin.zw + dest.size_target_index.xy,
+    vec2 local_pos = mix(dest_origin,
+                         dest_origin + src_task.size,
                          aPosition.xy);
 
     vec2 texture_size = vec2(textureSize(sCache, 0));
-    vec2 st0 = src0.screen_origin_task_origin.zw / texture_size;
-    vec2 st1 = (src0.screen_origin_task_origin.zw + src0.size_target_index.xy) / texture_size;
-    vUv0 = vec3(mix(st0, st1, aPosition.xy), src0.size_target_index.z);
+
+    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;
+    vUv0 = vec3(mix(st0, st1, aPosition.xy), backdrop_task.render_target_layer_index);
 
-    st0 = vec2(src1.screen_origin_task_origin.zw) / texture_size;
-    st1 = vec2(src1.screen_origin_task_origin.zw + src1.size_target_index.xy) / texture_size;
-    vec2 local_virtual_pos = mix(dest.screen_origin_task_origin.xy,
-                                 dest.screen_origin_task_origin.xy + dest.size_target_index.xy,
-                                 aPosition.xy);
-    vec2 f = (local_virtual_pos - src1.screen_origin_task_origin.xy) / src1.size_target_index.xy;
-    vUv1 = vec3(mix(st0, st1, f), src1.size_target_index.z);
-    vUv1Rect = vec4(st0, st1);
+    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 = composite.src0_src1_target_id_op.w;
+    vOp = pi.sub_index;
 
-    gl_Position = uTransform * vec4(local_pos, composite.z, 1.0);
+    gl_Position = uTransform * vec4(local_pos, pi.z, 1.0);
+
 }
--- a/gfx/webrender/res/ps_gradient.vs.glsl
+++ b/gfx/webrender/res/ps_gradient.vs.glsl
@@ -6,67 +6,57 @@
 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);
 
     vec4 segment_rect;
-    switch (int(gradient.kind.x)) {
-        case GRADIENT_HORIZONTAL: {
-            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.yw = prim.local_rect.yw;
-            segment_rect.x = x0;
-            segment_rect.z = x1 - x0;
-            } break;
-        case GRADIENT_VERTICAL: {
-            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.xz = prim.local_rect.xz;
-            segment_rect.y = y0;
-            segment_rect.w = y1 - y0;
-            } break;
+    vec2 axis;
+    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.yw = prim.local_rect.yw;
+        segment_rect.x = x0;
+        segment_rect.z = x1 - x0;
+        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.xz = prim.local_rect.xz;
+        segment_rect.y = y0;
+        segment_rect.w = y1 - y0;
+        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,
-                                                    prim.tile);
+                                                    prim.task);
     vLocalRect = vi.clipped_local_rect;
     vLocalPos = vi.local_pos;
     vec2 f = (vi.local_pos.xy - prim.local_rect.xy) / prim.local_rect.zw;
 #else
     VertexInfo vi = write_vertex(segment_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.tile);
+                                 prim.task);
 
-    vec2 f = (vi.local_clamped_pos - segment_rect.xy) / segment_rect.zw;
-    vPos = vi.local_clamped_pos;
+    vec2 f = (vi.local_pos - segment_rect.xy) / segment_rect.zw;
+    vPos = vi.local_pos;
 #endif
 
-    write_clip(vi.global_clamped_pos, prim.clip_area);
+    write_clip(vi.screen_pos, prim.clip_area);
 
-    switch (int(gradient.kind.x)) {
-        case GRADIENT_HORIZONTAL:
-            vColor = mix(g0.color, g1.color, f.x);
-            break;
-        case GRADIENT_VERTICAL:
-            vColor = mix(g0.color, g1.color, f.y);
-            break;
-        case GRADIENT_ROTATED:
-            vColor = vec4(1.0, 0.0, 1.0, 1.0);
-            break;
-    }
+    vColor = mix(g0.color, g1.color, dot(f, axis));
 }
copy from gfx/webrender/res/ps_composite.glsl
copy to gfx/webrender/res/ps_hardware_composite.fs.glsl
--- a/gfx/webrender/res/ps_composite.glsl
+++ b/gfx/webrender/res/ps_hardware_composite.fs.glsl
@@ -1,8 +1,7 @@
 /* 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/. */
 
-varying vec3 vUv0;
-varying vec3 vUv1;
-flat varying vec4 vUv1Rect;
-flat varying int vOp;
+void main(void) {
+    oFragColor = texture(sCache, vUv);
+}
copy from gfx/webrender/res/ps_composite.glsl
copy to gfx/webrender/res/ps_hardware_composite.glsl
--- a/gfx/webrender/res/ps_composite.glsl
+++ b/gfx/webrender/res/ps_hardware_composite.glsl
@@ -1,8 +1,5 @@
 /* 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/. */
 
-varying vec3 vUv0;
-varying vec3 vUv1;
-flat varying vec4 vUv1Rect;
-flat varying int vOp;
+varying vec3 vUv;
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_hardware_composite.vs.glsl
@@ -0,0 +1,25 @@
+#line 1
+/* 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/. */
+
+void main(void) {
+    PrimitiveInstance pi = fetch_prim_instance();
+    AlphaBatchTask dest_task = fetch_alpha_batch_task(pi.render_task_index);
+    AlphaBatchTask src_task = fetch_alpha_batch_task(pi.user_data.x);
+
+    vec2 dest_origin = dest_task.render_target_origin -
+                       dest_task.screen_space_origin +
+                       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 = src_task.render_target_origin / texture_size;
+    vec2 st1 = (src_task.render_target_origin + src_task.size) / texture_size;
+    vUv = vec3(mix(st0, st1, aPosition.xy), src_task.render_target_layer_index);
+
+    gl_Position = uTransform * vec4(local_pos, pi.z, 1.0);
+}
--- a/gfx/webrender/res/ps_image.fs.glsl
+++ b/gfx/webrender/res/ps_image.fs.glsl
@@ -19,13 +19,17 @@ void main(void) {
 #endif
 
     alpha = min(alpha, do_clip());
 
     // We calculate the particular tile this fragment belongs to, taking into
     // account the spacing in between tiles. We only paint if our fragment does
     // not fall into that spacing.
     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) * texture(sColor0, st);
+    oFragColor = vec4(1.0, 1.0, 1.0, alpha) * textureLod(sColor0, st, 0.0);
 }
--- a/gfx/webrender/res/ps_image.glsl
+++ b/gfx/webrender/res/ps_image.glsl
@@ -1,15 +1,16 @@
 /* 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/. */
 
 flat varying vec2 vTextureOffset; // Offset of this image into the texture atlas.
 flat varying vec2 vTextureSize;   // Size of the image in the texture atlas.
 flat varying vec2 vTileSpacing;   // Amount of space between tiled instances of this image.
+flat varying vec4 vStRect;     	  // Rectangle of valid texture rect, in st-space.
 
 #ifdef WR_FEATURE_TRANSFORM
 varying vec3 vLocalPos;
 flat varying vec4 vLocalRect;
 flat varying vec2 vStretchSize;
 #else
 varying vec2 vLocalPos;
 flat varying vec2 vStretchSize;
--- a/gfx/webrender/res/ps_image.vs.glsl
+++ b/gfx/webrender/res/ps_image.vs.glsl
@@ -8,32 +8,35 @@ void main(void) {
     Image image = fetch_image(prim.prim_index);
     ResourceRect res = fetch_resource_rect(prim.user_data.x);
 
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(prim.local_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
-                                                    prim.tile);
+                                                    prim.task);
     vLocalRect = vi.clipped_local_rect;
     vLocalPos = vi.local_pos;
 #else
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.tile);
-    vLocalPos = vi.local_clamped_pos - vi.local_rect.p0;
+                                 prim.task);
+    vLocalPos = vi.local_pos - vi.local_rect.p0;
 #endif
 
-    write_clip(vi.global_clamped_pos, prim.clip_area);
+    write_clip(vi.screen_pos, prim.clip_area);
 
     // vUv will contain how many times this image has wrapped around the image size.
     vec2 texture_size = vec2(textureSize(sColor0, 0));
     vec2 st0 = res.uv_rect.xy / texture_size;
     vec2 st1 = res.uv_rect.zw / texture_size;
 
     vTextureSize = st1 - st0;
     vTextureOffset = st0;
     vTileSpacing = image.stretch_size_and_tile_spacing.zw;
     vStretchSize = image.stretch_size_and_tile_spacing.xy;
+
+    vec2 half_texel = vec2(0.5) / texture_size;
+    vStRect = vec4(min(st0, st1) + half_texel, max(st0, st1) - half_texel);
 }
--- a/gfx/webrender/res/ps_radial_gradient.fs.glsl
+++ b/gfx/webrender/res/ps_radial_gradient.fs.glsl
@@ -1,24 +1,13 @@
 /* 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/. */
 
-float offset(int index) {
-    return vOffsets[index / 4][index % 4];
-}
-
-float linearStep(float lo, float hi, float x) {
-    float d = hi - lo;
-    float v = x - lo;
-    if (d != 0.0) {
-        v /= d;
-    }
-    return clamp(v, 0.0, 1.0);
-}
+uniform sampler2D sGradients;
 
 void main(void) {
     vec2 cd = vEndCenter - vStartCenter;
     vec2 pd = vPos - vStartCenter;
     float rd = vEndRadius - vStartRadius;
 
     // Solve for t in length(t * cd - pd) = vStartRadius + t * rd
     // using a quadratic equation in form of At^2 - 2Bt + C = 0
@@ -50,18 +39,22 @@ void main(void) {
             x = t0;
         } else if (vStartRadius + rd * t1 >= 0.0) {
             x = t1;
         } else {
             discard;
         }
     }
 
-    oFragColor = mix(vColors[0],
-                     vColors[1],
-                     linearStep(offset(0), offset(1), x));
+    vec2 texture_size = vec2(textureSize(sGradients, 0));
+
+    // Either saturate or modulo the offset depending on repeat mode, then scale to number of
+    // gradient color entries (texture width / 2).
+    x = mix(clamp(x, 0.0, 1.0), fract(x), vGradientRepeat) * 0.5 * texture_size.x;
 
-    for (int i=1 ; i < vStopCount-1 ; ++i) {
-        oFragColor = mix(oFragColor,
-                         vColors[i+1],
-                         linearStep(offset(i), offset(i+1), x));
-    }
+    // Start at the center of first color in the nearest 2-color entry, then offset with the
+    // fractional remainder to interpolate between the colors. Rely on texture clamping when
+    // outside of valid range.
+    x = 2.0 * floor(x) + 0.5 + fract(x);
+
+    // Normalize the texture coordates so we can use texture() for bilinear filtering.
+    oFragColor = texture(sGradients, vec2(x, vGradientIndex) / texture_size);
 }
--- a/gfx/webrender/res/ps_radial_gradient.glsl
+++ b/gfx/webrender/res/ps_radial_gradient.glsl
@@ -1,12 +1,11 @@
 /* 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/. */
 
-flat varying int vStopCount;
+flat varying float vGradientIndex;
+flat varying float vGradientRepeat;
 flat varying vec2 vStartCenter;
 flat varying vec2 vEndCenter;
 flat varying float vStartRadius;
 flat varying float vEndRadius;
 varying vec2 vPos;
-flat varying vec4 vColors[MAX_STOPS_PER_RADIAL_GRADIENT];
-flat varying vec4 vOffsets[MAX_STOPS_PER_RADIAL_GRADIENT/4];
--- a/gfx/webrender/res/ps_radial_gradient.vs.glsl
+++ b/gfx/webrender/res/ps_radial_gradient.vs.glsl
@@ -6,30 +6,29 @@
 void main(void) {
     Primitive prim = load_primitive();
     RadialGradient gradient = fetch_radial_gradient(prim.prim_index);
 
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.tile);
+                                 prim.task);
 
-    vStopCount = int(prim.user_data.x);
-    vPos = vi.local_clamped_pos;
+    vPos = vi.local_pos;
 
     // Snap the start/end points to device pixel units.
     // I'm not sure this is entirely correct, but the
     // old render path does this, and it is needed to
     // make the angle gradient ref tests pass. It might
     // be better to fix this higher up in DL construction
     // and not snap here?
     vStartCenter = floor(0.5 + gradient.start_end_center.xy * uDevicePixelRatio) / uDevicePixelRatio;
     vEndCenter = floor(0.5 + gradient.start_end_center.zw * uDevicePixelRatio) / uDevicePixelRatio;
-    vStartRadius = gradient.start_end_radius.x;
-    vEndRadius = gradient.start_end_radius.y;
+    vStartRadius = gradient.start_end_radius_extend_mode.x;
+    vEndRadius = gradient.start_end_radius_extend_mode.y;
 
-    for (int i=0 ; i < vStopCount ; ++i) {
-        GradientStop stop = fetch_gradient_stop(prim.sub_index + i);
-        vColors[i] = stop.color;
-        vOffsets[i/4][i%4] = stop.offset.x;
-    }
+    // V coordinate of gradient row in lookup texture.
+    vGradientIndex = float(prim.sub_index) + 0.5;
+
+    // Whether to repeat the gradient instead of clamping.
+    vGradientRepeat = float(int(gradient.start_end_radius_extend_mode.z) == EXTEND_MODE_REPEAT);
 }
--- a/gfx/webrender/res/ps_rectangle.vs.glsl
+++ b/gfx/webrender/res/ps_rectangle.vs.glsl
@@ -7,23 +7,23 @@ void main(void) {
     Primitive prim = load_primitive();
     Rectangle rect = fetch_rectangle(prim.prim_index);
     vColor = rect.color;
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(prim.local_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
-                                                    prim.tile);
+                                                    prim.task);
     vLocalRect = vi.clipped_local_rect;
     vLocalPos = vi.local_pos;
 #else
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.tile);
+                                 prim.task);
 #endif
 
 #ifdef WR_FEATURE_CLIP
-    write_clip(vi.global_clamped_pos, prim.clip_area);
+    write_clip(vi.screen_pos, prim.clip_area);
 #endif
 }
--- a/gfx/webrender/res/ps_text_run.fs.glsl
+++ b/gfx/webrender/res/ps_text_run.fs.glsl
@@ -1,18 +1,19 @@
 /* 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/. */
 
 void main(void) {
+    vec2 tc = clamp(vUv, vUvBorder.xy, vUvBorder.zw);
 #ifdef WR_FEATURE_SUBPIXEL_AA
     //note: the blend mode is not compatible with clipping
-    oFragColor = texture(sColor0, vUv);
+    oFragColor = texture(sColor0, tc);
 #else
-    float alpha = texture(sColor0, vUv).a;
+    float alpha = texture(sColor0, tc).a;
 #ifdef WR_FEATURE_TRANSFORM
     float a = 0.0;
     init_transform_fs(vLocalPos, vLocalRect, a);
     alpha *= a;
 #endif
     vec4 color = vColor;
     alpha = min(alpha, do_clip());
     oFragColor = vec4(vColor.rgb, vColor.a * alpha);
--- a/gfx/webrender/res/ps_text_run.glsl
+++ b/gfx/webrender/res/ps_text_run.glsl
@@ -1,11 +1,12 @@
 /* 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/. */
 
 flat varying vec4 vColor;
 varying vec2 vUv;
+flat varying vec4 vUvBorder;
 
 #ifdef WR_FEATURE_TRANSFORM
 varying vec3 vLocalPos;
 flat varying vec4 vLocalRect;
 #endif
--- a/gfx/webrender/res/ps_text_run.vs.glsl
+++ b/gfx/webrender/res/ps_text_run.vs.glsl
@@ -11,30 +11,31 @@ void main(void) {
 
     vec4 local_rect = vec4(glyph.offset.xy, (res.uv_rect.zw - res.uv_rect.xy) / uDevicePixelRatio);
 
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(local_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
-                                                    prim.tile);
+                                                    prim.task);
     vLocalRect = vi.clipped_local_rect;
     vLocalPos = vi.local_pos;
     vec2 f = (vi.local_pos.xy / vi.local_pos.z - local_rect.xy) / local_rect.zw;
 #else
     VertexInfo vi = write_vertex(local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.tile);
-    vec2 f = (vi.local_clamped_pos - vi.local_rect.p0) / (vi.local_rect.p1 - vi.local_rect.p0);
+                                 prim.task);
+    vec2 f = (vi.local_pos - vi.local_rect.p0) / (vi.local_rect.p1 - vi.local_rect.p0);
 #endif
 
-    write_clip(vi.global_clamped_pos, prim.clip_area);
+    write_clip(vi.screen_pos, prim.clip_area);
 
     vec2 texture_size = vec2(textureSize(sColor0, 0));
     vec2 st0 = res.uv_rect.xy / texture_size;
     vec2 st1 = res.uv_rect.zw / texture_size;
 
     vColor = text.color;
     vUv = mix(st0, st1, f);
+    vUvBorder = (res.uv_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy;
 }
--- a/gfx/webrender/res/ps_yuv_image.fs.glsl
+++ b/gfx/webrender/res/ps_yuv_image.fs.glsl
@@ -14,20 +14,27 @@ void main(void) {
          clamp(pos, vLocalRect.xy, vLocalRect.xy + vLocalRect.zw) - vLocalRect.xy;
 #else
     float alpha = 1.0;;
     vec2 relative_pos_in_rect = vLocalPos;
 #endif
 
     alpha = min(alpha, do_clip());
 
-    vec2 st_y = vTextureOffsetY + relative_pos_in_rect / vStretchSize * vTextureSizeY;
-    vec2 st_u = vTextureOffsetU + relative_pos_in_rect / vStretchSize * vTextureSizeUv;
-    vec2 st_v = vTextureOffsetV + relative_pos_in_rect / vStretchSize * vTextureSizeUv;
+    // 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_y = vTextureOffsetY + clamp(
+        relative_pos_in_rect / vStretchSize * vTextureSizeY,
+        vHalfTexelY, vTextureSizeY - vHalfTexelY);
+    vec2 uv_offset = clamp(
+        relative_pos_in_rect / vStretchSize * vTextureSizeUv,
+        vHalfTexelUv, vTextureSizeUv - vHalfTexelUv);
+    vec2 st_u = vTextureOffsetU + uv_offset;
+    vec2 st_v = vTextureOffsetV + uv_offset;
 
-    float y = texture(sColor0, st_y).r;
-    float u = texture(sColor1, st_u).r;
-    float v = texture(sColor2, st_v).r;
+    float y = textureLod(sColor0, st_y, 0.0).r;
+    float u = textureLod(sColor1, st_u, 0.0).r;
+    float v = textureLod(sColor2, st_v, 0.0).r;
 
     // See the vertex shader for an explanation of where the constants come from.
     vec3 rgb = vYuvColorMatrix * vec3(y - 0.06275, u - 0.50196, v - 0.50196);
     oFragColor = vec4(rgb, alpha);
 }
--- a/gfx/webrender/res/ps_yuv_image.glsl
+++ b/gfx/webrender/res/ps_yuv_image.glsl
@@ -3,16 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 flat varying vec2 vTextureOffsetY; // Offset of the y plane into the texture atlas.
 flat varying vec2 vTextureOffsetU; // Offset of the u plane into the texture atlas.
 flat varying vec2 vTextureOffsetV; // Offset of the v plane into the texture atlas.
 flat varying vec2 vTextureSizeY;   // Size of the y plane in the texture atlas.
 flat varying vec2 vTextureSizeUv;  // Size of the u and v planes in the texture atlas.
 flat varying vec2 vStretchSize;
+flat varying vec2 vHalfTexelY;     // Normalized length of the half of a Y texel.
+flat varying vec2 vHalfTexelUv;    // Normalized length of the half of u and v texels.
 
 flat varying mat3 vYuvColorMatrix;
 
 #ifdef WR_FEATURE_TRANSFORM
 varying vec3 vLocalPos;
 flat varying vec4 vLocalRect;
 #else
 varying vec2 vLocalPos;
--- a/gfx/webrender/res/ps_yuv_image.vs.glsl
+++ b/gfx/webrender/res/ps_yuv_image.vs.glsl
@@ -5,26 +5,26 @@
 
 void main(void) {
     Primitive prim = load_primitive();
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(prim.local_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
-                                                    prim.tile);
+                                                    prim.task);
     vLocalRect = vi.clipped_local_rect;
     vLocalPos = vi.local_pos;
 #else
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.tile);
-    vLocalPos = vi.local_clamped_pos - vi.local_rect.p0;
+                                 prim.task);
+    vLocalPos = vi.local_pos - vi.local_rect.p0;
 #endif
 
     YuvImage image = fetch_yuv_image(prim.prim_index);
 
     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;
 
@@ -40,16 +40,19 @@ void main(void) {
 
     // 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;
 
+    vHalfTexelY = vec2(0.5) / y_texture_size;
+    vHalfTexelUv = vec2(0.5) / uv_texture_size;
+
     // The constants added to the Y, U and V components are applied in the fragment shader.
     if (image.color_space == YUV_REC601) {
         // From Rec601:
         // [R]   [1.1643835616438356,  0.0,                 1.5960267857142858   ]   [Y -  16]
         // [G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708   ] x [U - 128]
         // [B]   [1.1643835616438356,  2.017232142857143,   8.862867620416422e-17]   [V - 128]
         //
         // For the range [0,1] instead of [0,255].
@@ -67,11 +70,11 @@ void main(void) {
         // For the range [0,1] instead of [0,255]:
         vYuvColorMatrix = mat3(
             1.16438,  0.0,      1.79274,
             1.16438, -0.21325, -0.53291,
             1.16438,  2.11240,  0.0
         );
     }
 
-    write_clip(vi.global_clamped_pos, prim.clip_area);
+    write_clip(vi.screen_pos, prim.clip_area);
 
 }
--- a/gfx/webrender/src/debug_render.rs
+++ b/gfx/webrender/src/debug_render.rs
@@ -23,18 +23,18 @@ pub struct DebugRenderer {
     tri_vao: VAOId,
     line_vertices: Vec<DebugColorVertex>,
     line_vao: VAOId,
     color_program_id: ProgramId,
 }
 
 impl DebugRenderer {
     pub fn new(device: &mut Device) -> DebugRenderer {
-        let font_program_id = device.create_program("debug_font", "shared_other");
-        let color_program_id = device.create_program("debug_color", "shared_other");
+        let font_program_id = device.create_program("debug_font", "shared_other", VertexFormat::DebugFont).unwrap();
+        let color_program_id = device.create_program("debug_color", "shared_other", VertexFormat::DebugColor).unwrap();
 
         let font_vao = device.create_vao(VertexFormat::DebugFont, 32);
         let line_vao = device.create_vao(VertexFormat::DebugColor, 32);
         let tri_vao = device.create_vao(VertexFormat::DebugColor, 32);
 
         let font_texture_id = device.create_texture_ids(1, TextureTarget::Default)[0];
         device.init_texture(font_texture_id,
                             debug_font_data::BMP_WIDTH,
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -9,22 +9,23 @@ use internal_types::{PackedVertex, Rende
 use internal_types::{BlurAttribute, ClearAttribute, ClipAttribute, VertexAttribute};
 use internal_types::{DebugFontVertex, DebugColorVertex};
 //use notify::{self, Watcher};
 use super::shader_source;
 use std::collections::HashMap;
 use std::fs::File;
 use std::hash::BuildHasherDefault;
 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};
+use webrender_traits::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintSize};
 
 #[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"))))]
@@ -36,18 +37,16 @@ const GL_FORMAT_BGRA: gl::GLuint = gl::B
 #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
 const SHADER_VERSION: &'static str = "#version 150\n";
 
 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
 const SHADER_VERSION: &'static str = "#version 300 es\n";
 
 static SHADER_PREAMBLE: &'static str = "shared";
 
-pub type ViewportDimensions = [u32; 2];
-
 lazy_static! {
     pub static ref MAX_TEXTURE_SIZE: gl::GLint = {
         gl::get_integer_v(gl::MAX_TEXTURE_SIZE)
     };
 }
 
 #[repr(u32)]
 pub enum DepthFunction {
@@ -371,57 +370,65 @@ struct Program {
     vs_id: Option<gl::GLuint>,
     fs_id: Option<gl::GLuint>,
 }
 
 impl Program {
     fn attach_and_bind_shaders(&mut self,
                                vs_id: gl::GLuint,
                                fs_id: gl::GLuint,
-                               panic_on_fail: bool) -> bool {
+                               vertex_format: VertexFormat) -> Result<(), ShaderError> {
         gl::attach_shader(self.id, vs_id);
         gl::attach_shader(self.id, fs_id);
 
-        gl::bind_attrib_location(self.id, VertexAttribute::Position as gl::GLuint, "aPosition");
-        gl::bind_attrib_location(self.id, VertexAttribute::Color as gl::GLuint, "aColor");
-        gl::bind_attrib_location(self.id, VertexAttribute::ColorTexCoord as gl::GLuint, "aColorTexCoord");
+        match vertex_format {
+            VertexFormat::Triangles | VertexFormat::Rectangles |
+            VertexFormat::DebugFont |  VertexFormat::DebugColor => {
+                gl::bind_attrib_location(self.id, VertexAttribute::Position as gl::GLuint, "aPosition");
+                gl::bind_attrib_location(self.id, VertexAttribute::Color as gl::GLuint, "aColor");
+                gl::bind_attrib_location(self.id, VertexAttribute::ColorTexCoord as gl::GLuint, "aColorTexCoord");
 
-        gl::bind_attrib_location(self.id, VertexAttribute::GlobalPrimId as gl::GLuint, "aGlobalPrimId");
-        gl::bind_attrib_location(self.id, VertexAttribute::PrimitiveAddress as gl::GLuint, "aPrimitiveAddress");
-        gl::bind_attrib_location(self.id, VertexAttribute::TaskIndex as gl::GLuint, "aTaskIndex");
-        gl::bind_attrib_location(self.id, VertexAttribute::ClipTaskIndex as gl::GLuint, "aClipTaskIndex");
-        gl::bind_attrib_location(self.id, VertexAttribute::LayerIndex as gl::GLuint, "aLayerIndex");
-        gl::bind_attrib_location(self.id, VertexAttribute::ElementIndex as gl::GLuint, "aElementIndex");
-        gl::bind_attrib_location(self.id, VertexAttribute::UserData as gl::GLuint, "aUserData");
-        gl::bind_attrib_location(self.id, VertexAttribute::ZIndex as gl::GLuint, "aZIndex");
-
-        gl::bind_attrib_location(self.id, ClearAttribute::Rectangle as gl::GLuint, "aClearRectangle");
-
-        gl::bind_attrib_location(self.id, BlurAttribute::RenderTaskIndex as gl::GLuint, "aBlurRenderTaskIndex");
-        gl::bind_attrib_location(self.id, BlurAttribute::SourceTaskIndex as gl::GLuint, "aBlurSourceTaskIndex");
-        gl::bind_attrib_location(self.id, BlurAttribute::Direction as gl::GLuint, "aBlurDirection");
-
-        gl::bind_attrib_location(self.id, ClipAttribute::RenderTaskIndex as gl::GLuint, "aClipRenderTaskIndex");
-        gl::bind_attrib_location(self.id, ClipAttribute::LayerIndex as gl::GLuint, "aClipLayerIndex");
-        gl::bind_attrib_location(self.id, ClipAttribute::DataIndex as gl::GLuint, "aClipDataIndex");
-        gl::bind_attrib_location(self.id, ClipAttribute::SegmentIndex as gl::GLuint, "aClipSegmentIndex");
+                gl::bind_attrib_location(self.id, VertexAttribute::GlobalPrimId as gl::GLuint, "aGlobalPrimId");
+                gl::bind_attrib_location(self.id, VertexAttribute::PrimitiveAddress as gl::GLuint, "aPrimitiveAddress");
+                gl::bind_attrib_location(self.id, VertexAttribute::TaskIndex as gl::GLuint, "aTaskIndex");
+                gl::bind_attrib_location(self.id, VertexAttribute::ClipTaskIndex as gl::GLuint, "aClipTaskIndex");
+                gl::bind_attrib_location(self.id, VertexAttribute::LayerIndex as gl::GLuint, "aLayerIndex");
+                gl::bind_attrib_location(self.id, VertexAttribute::ElementIndex as gl::GLuint, "aElementIndex");
+                gl::bind_attrib_location(self.id, VertexAttribute::UserData as gl::GLuint, "aUserData");
+                gl::bind_attrib_location(self.id, VertexAttribute::ZIndex as gl::GLuint, "aZIndex");
+            }
+            VertexFormat::Clear => {
+                gl::bind_attrib_location(self.id, ClearAttribute::Position as gl::GLuint, "aPosition");
+                gl::bind_attrib_location(self.id, ClearAttribute::Rectangle as gl::GLuint, "aClearRectangle");
+            }
+            VertexFormat::Blur => {
+                gl::bind_attrib_location(self.id, BlurAttribute::Position as gl::GLuint, "aPosition");
+                gl::bind_attrib_location(self.id, BlurAttribute::RenderTaskIndex as gl::GLuint, "aBlurRenderTaskIndex");
+                gl::bind_attrib_location(self.id, BlurAttribute::SourceTaskIndex as gl::GLuint, "aBlurSourceTaskIndex");
+                gl::bind_attrib_location(self.id, BlurAttribute::Direction as gl::GLuint, "aBlurDirection");
+            }
+            VertexFormat::Clip => {
+                gl::bind_attrib_location(self.id, ClipAttribute::Position as gl::GLuint, "aPosition");
+                gl::bind_attrib_location(self.id, ClipAttribute::RenderTaskIndex as gl::GLuint, "aClipRenderTaskIndex");
+                gl::bind_attrib_location(self.id, ClipAttribute::LayerIndex as gl::GLuint, "aClipLayerIndex");
+                gl::bind_attrib_location(self.id, ClipAttribute::DataIndex as gl::GLuint, "aClipDataIndex");
+                gl::bind_attrib_location(self.id, ClipAttribute::SegmentIndex as gl::GLuint, "aClipSegmentIndex");
+            }
+        }
 
         gl::link_program(self.id);
         if gl::get_program_iv(self.id, gl::LINK_STATUS) == (0 as gl::GLint) {
-            println!("Failed to link shader program: {}", gl::get_program_info_log(self.id));
+            let error_log = gl::get_program_info_log(self.id);
+            println!("Failed to link shader program: {}", error_log);
             gl::detach_shader(self.id, vs_id);
             gl::detach_shader(self.id, fs_id);
-            if panic_on_fail {
-                panic!("-- Program link failed - exiting --");
-            }
-            false
-        } else {
-            //println!("{}", gl::get_program_info_log(self.id));
-            true
+            return Err(ShaderError::Link(error_log));
         }
+
+        Ok(())
     }
 }
 
 impl Drop for Program {
     fn drop(&mut self) {
         gl::delete_program(self.id);
     }
 }
@@ -777,16 +784,22 @@ impl FileWatcherThread {
 }
 */
 
 pub struct Capabilities {
     pub max_ubo_size: usize,
     pub supports_multisampling: bool,
 }
 
+#[derive(Clone, Debug)]
+pub enum ShaderError {
+    Compilation(String, String), // name, error mssage
+    Link(String), // error message
+}
+
 pub struct Device {
     // device state
     bound_textures: [TextureId; 16],
     bound_program: ProgramId,
     bound_vao: VAOId,
     bound_read_fbo: FBOId,
     bound_draw_fbo: FBOId,
     default_read_fbo: gl::GLuint,
@@ -855,19 +868,18 @@ impl Device {
 
     pub fn get_capabilities(&self) -> &Capabilities {
         &self.capabilities
     }
 
     pub fn compile_shader(name: &str,
                           source_str: &str,
                           shader_type: gl::GLenum,
-                          shader_preamble: &[String],
-                          panic_on_fail: bool)
-                          -> Option<gl::GLuint> {
+                          shader_preamble: &[String])
+                          -> Result<gl::GLuint, ShaderError> {
         debug!("compile {:?}", name);
 
         let mut s = String::new();
         s.push_str(SHADER_VERSION);
         for prefix in shader_preamble {
             s.push_str(&prefix);
         }
         s.push_str(source_str);
@@ -875,26 +887,22 @@ impl Device {
         let id = gl::create_shader(shader_type);
         let mut source = Vec::new();
         source.extend_from_slice(s.as_bytes());
         gl::shader_source(id, &[&source[..]]);
         gl::compile_shader(id);
         let log = gl::get_shader_info_log(id);
         if gl::get_shader_iv(id, gl::COMPILE_STATUS) == (0 as gl::GLint) {
             println!("Failed to compile shader: {:?}\n{}", name, log);
-            if panic_on_fail {
-                panic!("-- Shader compile failed - exiting --");
-            }
-
-            None
+            Err(ShaderError::Compilation(name.to_string(), log))
         } else {
             if !log.is_empty() {
                 println!("Warnings detected on shader: {:?}\n{}", name, log);
             }
-            Some(id)
+            Ok(id)
         }
     }
 
     pub fn begin_frame(&mut self, device_pixel_ratio: f32) {
         debug_assert!(!self.inside_frame);
         self.inside_frame = true;
         self.device_pixel_ratio = device_pixel_ratio;
 
@@ -954,30 +962,30 @@ impl Device {
         if self.bound_read_fbo != fbo_id {
             self.bound_read_fbo = fbo_id;
             fbo_id.bind(FBOTarget::Read);
         }
     }
 
     pub fn bind_draw_target(&mut self,
                             texture_id: Option<(TextureId, i32)>,
-                            dimensions: Option<ViewportDimensions>) {
+                            dimensions: Option<DeviceUintSize>) {
         debug_assert!(self.inside_frame);
 
         let fbo_id = texture_id.map_or(FBOId(self.default_draw_fbo), |texture_id| {
             self.textures.get(&texture_id.0).unwrap().fbo_ids[texture_id.1 as usize]
         });
 
         if self.bound_draw_fbo != fbo_id {
             self.bound_draw_fbo = fbo_id;
             fbo_id.bind(FBOTarget::Draw);
         }
 
         if let Some(dimensions) = dimensions {
-            gl::viewport(0, 0, dimensions[0] as gl::GLint, dimensions[1] as gl::GLint);
+            gl::viewport(0, 0, dimensions.width as gl::GLint, dimensions.height as gl::GLint);
         }
     }
 
     pub fn bind_program(&mut self,
                         program_id: ProgramId,
                         projection: &Matrix4D<f32>) {
         debug_assert!(self.inside_frame);
 
@@ -1023,19 +1031,19 @@ impl Device {
             self.textures.insert(texture_id, texture);
 
             texture_ids.push(texture_id);
         }
 
         texture_ids
     }
 
-    pub fn get_texture_dimensions(&self, texture_id: TextureId) -> (u32, u32) {
+    pub fn get_texture_dimensions(&self, texture_id: TextureId) -> DeviceUintSize {
         let texture = &self.textures[&texture_id];
-        (texture.width, texture.height)
+        DeviceUintSize::new(texture.width, texture.height)
     }
 
     fn set_texture_parameters(&mut self, target: gl::GLuint, filter: TextureFilter) {
         let filter = match filter {
             TextureFilter::Nearest => {
                 gl::NEAREST
             }
             TextureFilter::Linear => {
@@ -1106,23 +1114,32 @@ impl Device {
             RenderTargetMode::LayerRenderTarget(layer_count) => {
                 self.bind_texture(DEFAULT_TEXTURE, texture_id);
                 self.set_texture_parameters(texture_id.target, filter);
                 self.create_fbo_for_texture_if_necessary(texture_id, Some(layer_count));
             }
             RenderTargetMode::None => {
                 self.bind_texture(DEFAULT_TEXTURE, texture_id);
                 self.set_texture_parameters(texture_id.target, filter);
+                let expanded_data: Vec<u8>;
+                let actual_pixels = if pixels.is_some() &&
+                                       format == ImageFormat::A8 &&
+                                       cfg!(any(target_arch="arm", target_arch="aarch64")) {
+                    expanded_data = pixels.unwrap().iter().flat_map(|&byte| repeat(byte).take(4)).collect();
+                    Some(expanded_data.as_slice())
+                } else {
+                    pixels
+                };
                 self.upload_texture_image(texture_id.target,
                                           width,
                                           height,
                                           internal_format as u32,
                                           gl_format,
                                           type_,
-                                          pixels);
+                                          actual_pixels);
             }
         }
     }
 
     pub fn get_render_target_layer_count(&self, texture_id: TextureId) -> usize {
         self.textures[&texture_id].fbo_ids.len()
     }
 
@@ -1239,48 +1256,48 @@ impl Device {
                           texture_id: TextureId,
                           new_width: u32,
                           new_height: u32,
                           format: ImageFormat,
                           filter: TextureFilter,
                           mode: RenderTargetMode) {
         debug_assert!(self.inside_frame);
 
-        let (old_width, old_height) = self.get_texture_dimensions(texture_id);
+        let old_size = self.get_texture_dimensions(texture_id);
 
         let temp_texture_id = self.create_texture_ids(1, TextureTarget::Default)[0];
-        self.init_texture(temp_texture_id, old_width, old_height, format, filter, mode, None);
+        self.init_texture(temp_texture_id, old_size.width, old_size.height, format, filter, mode, None);
         self.create_fbo_for_texture_if_necessary(temp_texture_id, None);
 
         self.bind_read_target(Some((texture_id, 0)));
         self.bind_texture(DEFAULT_TEXTURE, temp_texture_id);
 
         gl::copy_tex_sub_image_2d(temp_texture_id.target,
                                   0,
                                   0,
                                   0,
                                   0,
                                   0,
-                                  old_width as i32,
-                                  old_height as i32);
+                                  old_size.width as i32,
+                                  old_size.height as i32);
 
         self.deinit_texture(texture_id);
         self.init_texture(texture_id, new_width, new_height, format, filter, mode, None);
         self.create_fbo_for_texture_if_necessary(texture_id, None);
         self.bind_read_target(Some((temp_texture_id, 0)));
         self.bind_texture(DEFAULT_TEXTURE, texture_id);
 
         gl::copy_tex_sub_image_2d(texture_id.target,
                                   0,
                                   0,
                                   0,
                                   0,
                                   0,
-                                  old_width as i32,
-                                  old_height as i32);
+                                  old_size.width as i32,
+                                  old_size.height as i32);
 
         self.bind_read_target(None);
         self.deinit_texture(temp_texture_id);
     }
 
     pub fn deinit_texture(&mut self, texture_id: TextureId) {
         debug_assert!(self.inside_frame);
 
@@ -1308,24 +1325,26 @@ impl Device {
         texture.format = ImageFormat::Invalid;
         texture.width = 0;
         texture.height = 0;
         texture.fbo_ids.clear();
     }
 
     pub fn create_program(&mut self,
                           base_filename: &str,
-                          include_filename: &str) -> ProgramId {
-        self.create_program_with_prefix(base_filename, &[include_filename], None)
+                          include_filename: &str,
+                          vertex_format: VertexFormat) -> Result<ProgramId, ShaderError> {
+        self.create_program_with_prefix(base_filename, &[include_filename], None, vertex_format)
     }
 
     pub fn create_program_with_prefix(&mut self,
                                       base_filename: &str,
                                       include_filenames: &[&str],
-                                      prefix: Option<String>) -> ProgramId {
+                                      prefix: Option<String>,
+                                      vertex_format: VertexFormat) -> Result<ProgramId, ShaderError> {
         debug_assert!(self.inside_frame);
 
         let pid = gl::create_program();
 
         let mut vs_name = String::from(base_filename);
         vs_name.push_str(".vs");
         let mut fs_name = String::from(base_filename);
         fs_name.push_str(".fs");
@@ -1352,25 +1371,25 @@ impl Device {
             fs_id: None,
         };
 
         let program_id = ProgramId(pid);
 
         debug_assert!(self.programs.contains_key(&program_id) == false);
         self.programs.insert(program_id, program);
 
-        self.load_program(program_id, include, true);
+        try!{ self.load_program(program_id, include, vertex_format) };
 
-        program_id
+        Ok(program_id)
     }
 
     fn load_program(&mut self,
                     program_id: ProgramId,
                     include: String,
-                    panic_on_fail: bool) {
+                    vertex_format: VertexFormat) -> Result<(), ShaderError> {
         debug_assert!(self.inside_frame);
 
         let program = self.programs.get_mut(&program_id).unwrap();
 
         let mut vs_preamble = Vec::new();
         let mut fs_preamble = Vec::new();
 
         vs_preamble.push("#define WR_VERTEX_SHADER\n".to_owned());
@@ -1383,132 +1402,124 @@ impl Device {
 
         vs_preamble.push(self.shader_preamble.to_owned());
         fs_preamble.push(self.shader_preamble.to_owned());
 
         vs_preamble.push(include.clone());
         fs_preamble.push(include);
 
         // todo(gw): store shader ids so they can be freed!
-        let vs_id = Device::compile_shader(&program.name,
-                                           &program.vs_source,
-                                           gl::VERTEX_SHADER,
-                                           &vs_preamble,
-                                           panic_on_fail);
-        let fs_id = Device::compile_shader(&program.name,
-                                           &program.fs_source,
-                                           gl::FRAGMENT_SHADER,
-                                           &fs_preamble,
-                                           panic_on_fail);
+        let vs_id = try!{ Device::compile_shader(&program.name,
+                                                 &program.vs_source,
+                                                 gl::VERTEX_SHADER,
+                                                 &vs_preamble) };
+        let fs_id = try!{ Device::compile_shader(&program.name,
+                                                 &program.fs_source,
+                                                 gl::FRAGMENT_SHADER,
+                                                 &fs_preamble) };
+
+        if let Some(vs_id) = program.vs_id {
+            gl::detach_shader(program.id, vs_id);
+        }
 
-        match (vs_id, fs_id) {
-            (Some(vs_id), None) => {
-                println!("FAILED to load fs - falling back to previous!");
+        if let Some(fs_id) = program.fs_id {
+            gl::detach_shader(program.id, fs_id);
+        }
+
+        if let Err(bind_error) = program.attach_and_bind_shaders(vs_id, fs_id, vertex_format) {
+            if let (Some(vs_id), Some(fs_id)) = (program.vs_id, program.fs_id) {
+                try! { program.attach_and_bind_shaders(vs_id, fs_id, vertex_format) };
+            } else {
+               return Err(bind_error);
+            }
+        } else {
+            if let Some(vs_id) = program.vs_id {
                 gl::delete_shader(vs_id);
             }
-            (None, Some(fs_id)) => {
-                println!("FAILED to load vs - falling back to previous!");
+
+            if let Some(fs_id) = program.fs_id {
                 gl::delete_shader(fs_id);
             }
-            (None, None) => {
-                println!("FAILED to load vs/fs - falling back to previous!");
-            }
-            (Some(vs_id), Some(fs_id)) => {
-                if let Some(vs_id) = program.vs_id {
-                    gl::detach_shader(program.id, vs_id);
-                }
 
-                if let Some(fs_id) = program.fs_id {
-                    gl::detach_shader(program.id, fs_id);
-                }
+            program.vs_id = Some(vs_id);
+            program.fs_id = Some(fs_id);
+        }
+
+        program.u_transform = gl::get_uniform_location(program.id, "uTransform");
+        program.u_device_pixel_ratio = gl::get_uniform_location(program.id, "uDevicePixelRatio");
 
-                if program.attach_and_bind_shaders(vs_id, fs_id, panic_on_fail) {
-                    if let Some(vs_id) = program.vs_id {
-                        gl::delete_shader(vs_id);
-                    }
-
-                    if let Some(fs_id) = program.fs_id {
-                        gl::delete_shader(fs_id);
-                    }
+        program_id.bind();
+        let u_color_0 = gl::get_uniform_location(program.id, "sColor0");
+        if u_color_0 != -1 {
+            gl::uniform_1i(u_color_0, TextureSampler::Color0 as i32);
+        }
+        let u_color1 = gl::get_uniform_location(program.id, "sColor1");
+        if u_color1 != -1 {
+            gl::uniform_1i(u_color1, TextureSampler::Color1 as i32);
+        }
+        let u_color_2 = gl::get_uniform_location(program.id, "sColor2");
+        if u_color_2 != -1 {
+            gl::uniform_1i(u_color_2, TextureSampler::Color2 as i32);
+        }
+        let u_mask = gl::get_uniform_location(program.id, "sMask");
+        if u_mask != -1 {
+            gl::uniform_1i(u_mask, TextureSampler::Mask as i32);
+        }
 
-                    program.vs_id = Some(vs_id);
-                    program.fs_id = Some(fs_id);
-                } else {
-                    let vs_id = program.vs_id.unwrap();
-                    let fs_id = program.fs_id.unwrap();
-                    program.attach_and_bind_shaders(vs_id, fs_id, true);
-                }
+        let u_cache = gl::get_uniform_location(program.id, "sCache");
+        if u_cache != -1 {
+            gl::uniform_1i(u_cache, TextureSampler::Cache as i32);
+        }
 
-                program.u_transform = gl::get_uniform_location(program.id, "uTransform");
-                program.u_device_pixel_ratio = gl::get_uniform_location(program.id, "uDevicePixelRatio");
+        let u_layers = gl::get_uniform_location(program.id, "sLayers");
+        if u_layers != -1 {
+            gl::uniform_1i(u_layers, TextureSampler::Layers as i32);
+        }
 
-                program_id.bind();
-                let u_color_0 = gl::get_uniform_location(program.id, "sColor0");
-                if u_color_0 != -1 {
-                    gl::uniform_1i(u_color_0, TextureSampler::Color0 as i32);
-                }
-                let u_color1 = gl::get_uniform_location(program.id, "sColor1");
-                if u_color1 != -1 {
-                    gl::uniform_1i(u_color1, TextureSampler::Color1 as i32);
-                }
-                let u_color_2 = gl::get_uniform_location(program.id, "sColor2");
-                if u_color_2 != -1 {
-                    gl::uniform_1i(u_color_2, TextureSampler::Color2 as i32);
-                }
-                let u_mask = gl::get_uniform_location(program.id, "sMask");
-                if u_mask != -1 {
-                    gl::uniform_1i(u_mask, TextureSampler::Mask as i32);
-                }
+        let u_tasks = gl::get_uniform_location(program.id, "sRenderTasks");
+        if u_tasks != -1 {
+            gl::uniform_1i(u_tasks, TextureSampler::RenderTasks as i32);
+        }
+
+        let u_prim_geom = gl::get_uniform_location(program.id, "sPrimGeometry");
+        if u_prim_geom != -1 {
+            gl::uniform_1i(u_prim_geom, TextureSampler::Geometry as i32);
+        }
 
-                let u_cache = gl::get_uniform_location(program.id, "sCache");
-                if u_cache != -1 {
-                    gl::uniform_1i(u_cache, TextureSampler::Cache as i32);
-                }
+        let u_data16 = gl::get_uniform_location(program.id, "sData16");
+        if u_data16 != -1 {
+            gl::uniform_1i(u_data16, TextureSampler::Data16 as i32);
+        }
 
-                let u_layers = gl::get_uniform_location(program.id, "sLayers");
-                if u_layers != -1 {
-                    gl::uniform_1i(u_layers, TextureSampler::Layers as i32);
-                }
+        let u_data32 = gl::get_uniform_location(program.id, "sData32");
+        if u_data32 != -1 {
+            gl::uniform_1i(u_data32, TextureSampler::Data32 as i32);
+        }
 
-                let u_tasks = gl::get_uniform_location(program.id, "sRenderTasks");
-                if u_tasks != -1 {
-                    gl::uniform_1i(u_tasks, TextureSampler::RenderTasks as i32);
-                }
+        let u_data64 = gl::get_uniform_location(program.id, "sData64");
+        if u_data64 != -1 {
+            gl::uniform_1i(u_data64, TextureSampler::Data64 as i32);
+        }
 
-                let u_prim_geom = gl::get_uniform_location(program.id, "sPrimGeometry");
-                if u_prim_geom != -1 {
-                    gl::uniform_1i(u_prim_geom, TextureSampler::Geometry as i32);
-                }
-
-                let u_data16 = gl::get_uniform_location(program.id, "sData16");
-                if u_data16 != -1 {
-                    gl::uniform_1i(u_data16, TextureSampler::Data16 as i32);
-                }
+        let u_data128 = gl::get_uniform_location(program.id, "sData128");
+        if u_data128 != -1 {
+            gl::uniform_1i(u_data128, TextureSampler::Data128    as i32);
+        }
 
-                let u_data32 = gl::get_uniform_location(program.id, "sData32");
-                if u_data32 != -1 {
-                    gl::uniform_1i(u_data32, TextureSampler::Data32 as i32);
-                }
-
-                let u_data64 = gl::get_uniform_location(program.id, "sData64");
-                if u_data64 != -1 {
-                    gl::uniform_1i(u_data64, TextureSampler::Data64 as i32);
-                }
+        let u_resource_rects = gl::get_uniform_location(program.id, "sResourceRects");
+        if u_resource_rects != -1 {
+            gl::uniform_1i(u_resource_rects, TextureSampler::ResourceRects as i32);
+        }
 
-                let u_data128 = gl::get_uniform_location(program.id, "sData128");
-                if u_data128 != -1 {
-                    gl::uniform_1i(u_data128, TextureSampler::Data128    as i32);
-                }
+        let u_gradients = gl::get_uniform_location(program.id, "sGradients");
+        if u_gradients != -1 {
+            gl::uniform_1i(u_gradients, TextureSampler::Gradients as i32);
+        }
 
-                let u_resource_rects = gl::get_uniform_location(program.id, "sResourceRects");
-                if u_resource_rects != -1 {
-                    gl::uniform_1i(u_resource_rects, TextureSampler::ResourceRects as i32);
-                }
-            }
-        }
+        Ok(())
     }
 
 /*
     pub fn refresh_shader(&mut self, path: PathBuf) {
         let mut vs_preamble_path = self.resource_path.clone();
         vs_preamble_path.push(VERTEX_SHADER_PREAMBLE);
 
         let mut fs_preamble_path = self.resource_path.clone();
@@ -1921,16 +1932,26 @@ impl Device {
         gl::blend_func(gl::CONSTANT_COLOR, gl::ONE_MINUS_SRC_COLOR);
     }
 
     pub fn set_blend_mode_multiply(&self) {
         gl::blend_func_separate(gl::ZERO, gl::SRC_COLOR,
                                 gl::ZERO, gl::SRC_ALPHA);
         gl::blend_equation(gl::FUNC_ADD);
     }
+    pub fn set_blend_mode_max(&self) {
+        gl::blend_func_separate(gl::ONE, gl::ONE,
+                                gl::ONE, gl::ONE);
+        gl::blend_equation_separate(gl::MAX, gl::FUNC_ADD);
+    }
+    pub fn set_blend_mode_min(&self) {
+        gl::blend_func_separate(gl::ONE, gl::ONE,
+                                gl::ONE, gl::ONE);
+        gl::blend_equation_separate(gl::MIN, gl::FUNC_ADD);
+    }
 }
 
 impl Drop for Device {
     fn drop(&mut self) {
         //self.file_watcher.exit();
     }
 }
 
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -1,49 +1,47 @@
 /* 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 fnv::FnvHasher;
 use internal_types::{ANGLE_FLOAT_TO_FIXED, AxisDirection};
-use internal_types::{CompositionOp};
 use internal_types::{LowLevelFilterOp};
 use internal_types::{RendererFrame};
+use frame_builder::{FrameBuilder, FrameBuilderConfig};
 use layer::Layer;
 use resource_cache::ResourceCache;
-use scene::Scene;
+use scene::{Scene, SceneProperties};
 use scroll_tree::{ScrollTree, ScrollStates};
 use std::collections::HashMap;
 use std::hash::BuildHasherDefault;
-use tiling::{AuxiliaryListsMap, FrameBuilder, FrameBuilderConfig, PrimitiveFlags};
+use tiling::{AuxiliaryListsMap, CompositeOps, PrimitiveFlags};
 use webrender_traits::{AuxiliaryLists, ClipRegion, ColorF, DisplayItem, Epoch, FilterOp};
-use webrender_traits::{LayerPoint, LayerRect, LayerSize, LayerToScrollTransform};
+use webrender_traits::{LayerPoint, LayerRect, LayerSize, LayerToScrollTransform, LayoutTransform};
 use webrender_traits::{MixBlendMode, PipelineId, ScrollEventPhase, ScrollLayerId, ScrollLayerState};
 use webrender_traits::{ScrollLocation, ScrollPolicy, ServoScrollRootId, SpecificDisplayItem};
 use webrender_traits::{StackingContext, 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,
-    pipeline_sizes: &'a mut HashMap<PipelineId, LayerSize>,
     builder: &'a mut FrameBuilder,
 }
 
 // TODO: doc
 pub struct Frame {
     pub scroll_tree: ScrollTree,
     pub pipeline_epoch_map: HashMap<PipelineId, Epoch, BuildHasherDefault<FnvHasher>>,
     pub pipeline_auxiliary_lists: AuxiliaryListsMap,
     id: FrameId,
-    debug: bool,
     frame_builder_config: FrameBuilderConfig,
     frame_builder: Option<FrameBuilder>,
 }
 
 trait DisplayListHelpers {
     fn starting_stacking_context<'a>(&'a self) -> Option<(&'a StackingContext, &'a ClipRegion)>;
 }
 
@@ -54,94 +52,81 @@ impl DisplayListHelpers for Vec<DisplayI
                 Some((&specific_item.stacking_context, &item.clip))
             },
             _ => None,
         })
     }
 }
 
 trait StackingContextHelpers {
-    fn needs_composition_operation_for_mix_blend_mode(&self) -> bool;
-    fn composition_operations(&self, auxiliary_lists: &AuxiliaryLists) -> Vec<CompositionOp>;
+    fn mix_blend_mode_for_compositing(&self) -> Option<MixBlendMode>;
+    fn filter_ops_for_compositing(&self,
+                                  auxiliary_lists: &AuxiliaryLists,
+                                  properties: &SceneProperties) -> Vec<LowLevelFilterOp>;
 }
 
 impl StackingContextHelpers for StackingContext {
-    fn needs_composition_operation_for_mix_blend_mode(&self) -> bool {
+    fn mix_blend_mode_for_compositing(&self) -> Option<MixBlendMode> {
         match self.mix_blend_mode {
-            MixBlendMode::Normal => false,
-            MixBlendMode::Multiply |
-            MixBlendMode::Screen |
-            MixBlendMode::Overlay |
-            MixBlendMode::Darken |
-            MixBlendMode::Lighten |
-            MixBlendMode::ColorDodge |
-            MixBlendMode::ColorBurn |
-            MixBlendMode::HardLight |
-            MixBlendMode::SoftLight |
-            MixBlendMode::Difference |
-            MixBlendMode::Exclusion |
-            MixBlendMode::Hue |
-            MixBlendMode::Saturation |
-            MixBlendMode::Color |
-            MixBlendMode::Luminosity => true,
+            MixBlendMode::Normal => None,
+            _ => Some(self.mix_blend_mode),
         }
     }
 
-    fn composition_operations(&self, auxiliary_lists: &AuxiliaryLists) -> Vec<CompositionOp> {
-        let mut composition_operations = vec![];
-        if self.needs_composition_operation_for_mix_blend_mode() {
-            composition_operations.push(CompositionOp::MixBlend(self.mix_blend_mode));
-        }
+    fn filter_ops_for_compositing(&self,
+                                  auxiliary_lists: &AuxiliaryLists,
+                                  properties: &SceneProperties) -> Vec<LowLevelFilterOp> {
+        let mut filters = vec![];
         for filter in auxiliary_lists.filters(&self.filters) {
             match *filter {
                 FilterOp::Blur(radius) => {
-                    composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Blur(
+                    filters.push(LowLevelFilterOp::Blur(
                         radius,
-                        AxisDirection::Horizontal)));
-                    composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Blur(
+                        AxisDirection::Horizontal));
+                    filters.push(LowLevelFilterOp::Blur(
                         radius,
-                        AxisDirection::Vertical)));
+                        AxisDirection::Vertical));
                 }
                 FilterOp::Brightness(amount) => {
-                    composition_operations.push(CompositionOp::Filter(
-                            LowLevelFilterOp::Brightness(Au::from_f32_px(amount))));
+                    filters.push(
+                            LowLevelFilterOp::Brightness(Au::from_f32_px(amount)));
                 }
                 FilterOp::Contrast(amount) => {
-                    composition_operations.push(CompositionOp::Filter(
-                            LowLevelFilterOp::Contrast(Au::from_f32_px(amount))));
+                    filters.push(
+                            LowLevelFilterOp::Contrast(Au::from_f32_px(amount)));
                 }
                 FilterOp::Grayscale(amount) => {
-                    composition_operations.push(CompositionOp::Filter(
-                            LowLevelFilterOp::Grayscale(Au::from_f32_px(amount))));
+                    filters.push(
+                            LowLevelFilterOp::Grayscale(Au::from_f32_px(amount)));
                 }
                 FilterOp::HueRotate(angle) => {
-                    composition_operations.push(CompositionOp::Filter(
+                    filters.push(
                             LowLevelFilterOp::HueRotate(f32::round(
-                                    angle * ANGLE_FLOAT_TO_FIXED) as i32)));
+                                    angle * ANGLE_FLOAT_TO_FIXED) as i32));
                 }
                 FilterOp::Invert(amount) => {
-                    composition_operations.push(CompositionOp::Filter(
-                            LowLevelFilterOp::Invert(Au::from_f32_px(amount))));
+                    filters.push(
+                            LowLevelFilterOp::Invert(Au::from_f32_px(amount)));
                 }
-                FilterOp::Opacity(amount) => {
-                    composition_operations.push(CompositionOp::Filter(
-                            LowLevelFilterOp::Opacity(Au::from_f32_px(amount))));
+                FilterOp::Opacity(ref value) => {
+                    let amount = properties.resolve_float(value, 1.0);
+                    filters.push(
+                            LowLevelFilterOp::Opacity(Au::from_f32_px(amount)));
                 }
                 FilterOp::Saturate(amount) => {
-                    composition_operations.push(CompositionOp::Filter(
-                            LowLevelFilterOp::Saturate(Au::from_f32_px(amount))));
+                    filters.push(
+                            LowLevelFilterOp::Saturate(Au::from_f32_px(amount)));
                 }
                 FilterOp::Sepia(amount) => {
-                    composition_operations.push(CompositionOp::Filter(
-                            LowLevelFilterOp::Sepia(Au::from_f32_px(amount))));
+                    filters.push(
+                            LowLevelFilterOp::Sepia(Au::from_f32_px(amount)));
                 }
             }
         }
-
-        composition_operations
+        filters
     }
 }
 
 struct DisplayListTraversal<'a> {
     pub display_list: &'a [DisplayItem],
     pub next_item_index: usize,
 }
 
@@ -186,23 +171,22 @@ impl<'a> Iterator for DisplayListTravers
 
         let item = &self.display_list[self.next_item_index];
         self.next_item_index += 1;
         Some(item)
     }
 }
 
 impl Frame {
-    pub fn new(debug: bool, config: FrameBuilderConfig) -> Frame {
+    pub fn new(config: FrameBuilderConfig) -> Frame {
         Frame {
             pipeline_epoch_map: HashMap::with_hasher(Default::default()),
             pipeline_auxiliary_lists: HashMap::with_hasher(Default::default()),
             scroll_tree: ScrollTree::new(),
             id: FrameId(0),
-            debug: debug,
             frame_builder: None,
             frame_builder_config: config,
         }
     }
 
     pub fn reset(&mut self) -> ScrollStates {
         self.pipeline_epoch_map.clear();
 
@@ -233,19 +217,21 @@ impl Frame {
                   -> bool {
         self.scroll_tree.scroll(scroll_location, cursor, phase,)
     }
 
     pub fn tick_scrolling_bounce_animations(&mut self) {
         self.scroll_tree.tick_scrolling_bounce_animations();
     }
 
-    pub fn create(&mut self,
-                  scene: &Scene,
-                  pipeline_sizes: &mut HashMap<PipelineId, LayerSize>) {
+    pub fn discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId) {
+        self.scroll_tree.discard_frame_state_for_pipeline(pipeline_id);
+    }
+
+    pub fn create(&mut self, scene: &Scene) {
         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,
@@ -265,213 +251,224 @@ impl Frame {
         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;
             }
         };
 
-        // Insert global position: fixed elements layer
-        debug_assert!(self.scroll_tree.layers.is_empty());
-        let root_scroll_layer_id = ScrollLayerId::root(root_pipeline_id);
-        let root_fixed_layer_id = ScrollLayerId::create_fixed(root_pipeline_id);
-        let root_viewport = LayerRect::new(LayerPoint::zero(), root_pipeline.viewport_size);
-        let layer = Layer::new(&root_viewport,
-                               root_clip.main.size,
-                               &LayerToScrollTransform::identity(),
-                               root_pipeline_id);
-        self.scroll_tree.add_layer(layer.clone(), root_fixed_layer_id, None);
-        self.scroll_tree.add_layer(layer, root_scroll_layer_id, None);
-        self.scroll_tree.root_scroll_layer_id = Some(root_scroll_layer_id);
+        self.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,
                                                   background_color,
-                                                  self.debug,
                                                   self.frame_builder_config);
 
         {
             let mut context = FlattenContext {
                 scene: scene,
-                pipeline_sizes: pipeline_sizes,
                 builder: &mut frame_builder,
             };
 
             let mut traversal = DisplayListTraversal::new_skipping_first(display_list);
+            let reference_frame_id = self.scroll_tree.root_reference_frame_id();
+            let topmost_scroll_layer_id = self.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_scroll_layer(reference_frame_id,
+                                              &clip,
+                                              &LayerPoint::zero(),
+                                              &root_pipeline.viewport_size);
+            context.builder.push_scroll_layer(topmost_scroll_layer_id,
+                                              &clip,
+                                              &LayerPoint::zero(),
+                                              &root_clip.main.size);
+
             self.flatten_stacking_context(&mut traversal,
                                           root_pipeline_id,
                                           &mut context,
-                                          root_fixed_layer_id,
-                                          root_scroll_layer_id,
+                                          reference_frame_id,
+                                          topmost_scroll_layer_id,
                                           LayerToScrollTransform::identity(),
                                           0,
                                           &root_stacking_context,
                                           root_clip);
+
+            context.builder.pop_scroll_layer();
+            context.builder.pop_scroll_layer();
         }
 
         self.frame_builder = Some(frame_builder);
         self.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_fixed_layer_id: ScrollLayerId,
-                                mut current_scroll_layer_id: ScrollLayerId,
+                                current_reference_frame_id: ScrollLayerId,
+                                parent_scroll_layer_id: ScrollLayerId,
                                 layer_relative_transform: LayerToScrollTransform,
                                 level: i32,
-                                clip: &LayerRect,
+                                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;
         }
 
-        let layer = Layer::new(&clip, *content_size, &layer_relative_transform, pipeline_id);
-        self.scroll_tree.add_layer(layer, new_scroll_layer_id, Some(current_scroll_layer_id));
-        current_scroll_layer_id = new_scroll_layer_id;
-
-        let layer_rect = LayerRect::new(LayerPoint::zero(),
-                                        LayerSize::new(content_size.width + clip.origin.x,
-                                                       content_size.height + clip.origin.y));
-        context.builder.push_layer(layer_rect,
-                                   &ClipRegion::simple(&layer_rect),
-                                   LayerToScrollTransform::identity(),
-                                   pipeline_id,
-                                   current_scroll_layer_id,
-                                   &[]);
+        let clip_rect = clip.main;
+        let layer = Layer::new(&clip_rect, *content_size, &layer_relative_transform, pipeline_id);
+        self.scroll_tree.add_layer(layer, new_scroll_layer_id, parent_scroll_layer_id);
+        context.builder.push_scroll_layer(new_scroll_layer_id,
+                                          clip,
+                                          &clip_rect.origin,
+                                          &content_size);
 
         self.flatten_items(traversal,
                            pipeline_id,
                            context,
-                           current_fixed_layer_id,
-                           current_scroll_layer_id,
+                           current_reference_frame_id,
+                           new_scroll_layer_id,
                            LayerToScrollTransform::identity(),
                            level);
 
-        context.builder.pop_layer();
+        context.builder.pop_scroll_layer();
     }
 
     fn flatten_stacking_context<'a>(&mut self,
                                     traversal: &mut DisplayListTraversal<'a>,
                                     pipeline_id: PipelineId,
                                     context: &mut FlattenContext,
-                                    current_fixed_layer_id: ScrollLayerId,
+                                    current_reference_frame_id: ScrollLayerId,
                                     current_scroll_layer_id: ScrollLayerId,
                                     layer_relative_transform: LayerToScrollTransform,
                                     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;
         }
 
         let composition_operations = {
             let auxiliary_lists = self.pipeline_auxiliary_lists
                                       .get(&pipeline_id)
                                       .expect("No auxiliary lists?!");
-            stacking_context.composition_operations(auxiliary_lists)
+            CompositeOps::new(
+                stacking_context.filter_ops_for_compositing(auxiliary_lists, &context.scene.properties),
+                stacking_context.mix_blend_mode_for_compositing())
         };
 
-        // Detect composition operations that will make us invisible.
-        for composition_operation in &composition_operations {
-            match *composition_operation {
-                CompositionOp::Filter(LowLevelFilterOp::Opacity(Au(0))) => {
-                    traversal.skip_current_stacking_context();
-                    return;
-                }
-                _ => {}
-            }
+        if composition_operations.will_make_invisible() {
+            traversal.skip_current_stacking_context();
+            return;
         }
 
-        let 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 stacking_context_transform = context.scene
+                                                .properties
+                                                .resolve_layout_transform(&stacking_context.transform);
 
-        // Build world space transform
-        let scroll_layer_id = match stacking_context.scroll_policy {
-            ScrollPolicy::Fixed => current_fixed_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 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.scroll_tree.add_reference_frame(clip_region.main,
+                                                                   transform,
+                                                                   pipeline_id,
+                                                                   scroll_layer_id);
+            reference_frame_id = scroll_layer_id;
+            transform = LayerToScrollTransform::identity();
+        }
+
         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_layer(clip_region.main,
-                                               &no_clip,
-                                               transform,
-                                               pipeline_id,
-                                               scroll_layer_id,
-                                               &composition_operations);
+                    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,
                     // it's already processed by the layer we just pushed.
                     context.builder.add_solid_rectangle(&clip_region.main,
                                                         &no_clip,
                                                         &bg_color,
                                                         PrimitiveFlags::None);
 
-                    context.builder.pop_layer();
+                    context.builder.pop_stacking_context();
                 }
             }
         }
 
          // TODO(gw): Int with overflow etc
-        context.builder.push_layer(clip_region.main,
-                                   &clip_region,
-                                   transform,
-                                   pipeline_id,
-                                   scroll_layer_id,
-                                   &composition_operations);
+        context.builder.push_stacking_context(clip_region.main,
+                                              transform,
+                                              pipeline_id,
+                                              scroll_layer_id,
+                                              composition_operations);
 
         self.flatten_items(traversal,
                            pipeline_id,
                            context,
-                           current_fixed_layer_id,
-                           current_scroll_layer_id,
+                           reference_frame_id,
+                           scroll_layer_id,
                            transform,
                            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(
                 &scrollbar_rect,
                 &ClipRegion::simple(&scrollbar_rect),
                 &DEFAULT_SCROLLBAR_COLOR,
-                PrimitiveFlags::Scrollbar(self.scroll_tree.root_scroll_layer_id.unwrap(), 4.0));
+                PrimitiveFlags::Scrollbar(self.scroll_tree.topmost_scroll_layer_id, 4.0));
         }
 
-        context.builder.pop_layer();
+        context.builder.pop_stacking_context();
     }
 
     fn flatten_iframe<'a>(&mut self,
                           pipeline_id: PipelineId,
                           bounds: &LayerRect,
                           context: &mut FlattenContext,
                           current_scroll_layer_id: ScrollLayerId,
                           layer_relative_transform: LayerToScrollTransform) {
-        context.pipeline_sizes.insert(pipeline_id, bounds.size);
 
         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 {
@@ -484,49 +481,64 @@ impl Frame {
             None => {
                 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 iframe_rect = LayerRect::new(LayerPoint::zero(), bounds.size);
         let transform = layer_relative_transform.pre_translated(bounds.origin.x,
                                                                 bounds.origin.y,
                                                                 0.0);
-
-        let iframe_fixed_layer_id = ScrollLayerId::create_fixed(pipeline_id);
-        let iframe_scroll_layer_id = ScrollLayerId::root(pipeline_id);
+        let iframe_reference_frame_id =
+            self.scroll_tree.add_reference_frame(iframe_rect,
+                                                 transform,
+                                                 pipeline_id,
+                                                 current_scroll_layer_id);
+        let iframe_scroll_layer_id = ScrollLayerId::root_scroll_layer(pipeline_id);
+        let layer = Layer::new(&LayerRect::new(LayerPoint::zero(), iframe_rect.size),
+                               iframe_clip.main.size,
+                               &LayerToScrollTransform::identity(),
+                               pipeline_id);
+        self.scroll_tree.add_layer(layer.clone(),
+                                   iframe_scroll_layer_id,
+                                   iframe_reference_frame_id);
 
-        let layer = Layer::new(iframe_rect,
-                               iframe_clip.main.size,
-                               &transform,
-                               pipeline_id);
-        self.scroll_tree.add_layer(layer.clone(), iframe_fixed_layer_id, None);
-        self.scroll_tree.add_layer(layer, iframe_scroll_layer_id, Some(current_scroll_layer_id));
+        context.builder.push_scroll_layer(iframe_reference_frame_id,
+                                          iframe_clip,
+                                          &LayerPoint::zero(),
+                                          &iframe_rect.size);
+        context.builder.push_scroll_layer(iframe_scroll_layer_id,
+                                          iframe_clip,
+                                          &LayerPoint::zero(),
+                                          &iframe_clip.main.size);
 
         let mut traversal = DisplayListTraversal::new_skipping_first(display_list);
 
         self.flatten_stacking_context(&mut traversal,
                                       pipeline_id,
                                       context,
-                                      iframe_fixed_layer_id,
+                                      iframe_reference_frame_id,
                                       iframe_scroll_layer_id,
                                       LayerToScrollTransform::identity(),
                                       0,
                                       &iframe_stacking_context,
                                       iframe_clip);
+
+        context.builder.pop_scroll_layer();
+        context.builder.pop_scroll_layer();
     }
 
     fn flatten_items<'a>(&mut self,
                          traversal: &mut DisplayListTraversal<'a>,
                          pipeline_id: PipelineId,
                          context: &mut FlattenContext,
-                         current_fixed_layer_id: ScrollLayerId,
+                         current_reference_frame_id: ScrollLayerId,
                          current_scroll_layer_id: ScrollLayerId,
                          layer_relative_transform: LayerToScrollTransform,
                          level: i32) {
         while let Some(item) = traversal.next() {
             match item.item {
                 SpecificDisplayItem::WebGL(ref info) => {
                     context.builder.add_webgl_rectangle(item.rect,
                                                         &item.clip, info.context_id);
@@ -549,39 +561,42 @@ impl Frame {
                 }
                 SpecificDisplayItem::Text(ref text_info) => {
                     context.builder.add_text(item.rect,
                                              &item.clip,
                                              text_info.font_key,
                                              text_info.size,
                                              text_info.blur_radius,
                                              &text_info.color,
-                                             text_info.glyphs);
+                                             text_info.glyphs,
+                                             text_info.glyph_options);
                 }
                 SpecificDisplayItem::Rectangle(ref info) => {
                     context.builder.add_solid_rectangle(&item.rect,
                                                         &item.clip,
                                                         &info.color,
                                                         PrimitiveFlags::None);
                 }
                 SpecificDisplayItem::Gradient(ref info) => {
                     context.builder.add_gradient(item.rect,
                                                  &item.clip,
                                                  info.start_point,
                                                  info.end_point,
-                                                 info.stops);
+                                                 info.stops,
+                                                 info.extend_mode);
                 }
                 SpecificDisplayItem::RadialGradient(ref info) => {
                     context.builder.add_radial_gradient(item.rect,
                                                         &item.clip,
                                                         info.start_center,
                                                         info.start_radius,
                                                         info.end_center,
                                                         info.end_radius,
-                                                        info.stops);
+                                                        info.stops,
+                                                        info.extend_mode);
                 }
                 SpecificDisplayItem::BoxShadow(ref box_shadow_info) => {
                     context.builder.add_box_shadow(&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,
@@ -590,32 +605,32 @@ impl Frame {
                 }
                 SpecificDisplayItem::Border(ref info) => {
                     context.builder.add_border(item.rect, &item.clip, info);
                 }
                 SpecificDisplayItem::PushStackingContext(ref info) => {
                     self.flatten_stacking_context(traversal,
                                                   pipeline_id,
                                                   context,
-                                                  current_fixed_layer_id,
+                                                  current_reference_frame_id,
                                                   current_scroll_layer_id,
                                                   layer_relative_transform,
                                                   level + 1,
                                                   &info.stacking_context,
                                                   &item.clip);
                 }
                 SpecificDisplayItem::PushScrollLayer(ref info) => {
                     self.flatten_scroll_layer(traversal,
                                               pipeline_id,
                                               context,
-                                              current_fixed_layer_id,
+                                              current_reference_frame_id,
                                               current_scroll_layer_id,
                                               layer_relative_transform,
                                               level,
-                                              &item.rect,
+                                              &item.clip,
                                               &info.content_size,
                                               info.id);
                 }
                 SpecificDisplayItem::Iframe(ref info) => {
                     self.flatten_iframe(info.pipeline_id,
                                         &item.rect,
                                         context,
                                         current_scroll_layer_id,
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/src/frame_builder.rs
@@ -0,0 +1,1223 @@
+/* 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 batch_builder::BorderSideHelpers;
+use frame::FrameId;
+use gpu_store::GpuStoreAddress;
+use internal_types::{HardwareCompositeOp, SourceTexture};
+use mask_cache::{ClipSource, MaskCacheInfo};
+use prim_store::{BorderPrimitiveCpu, BorderPrimitiveGpu, BoxShadowPrimitiveGpu};
+use prim_store::{GradientPrimitiveCpu, GradientPrimitiveGpu, ImagePrimitiveCpu, ImagePrimitiveGpu};
+use prim_store::{ImagePrimitiveKind, PrimitiveContainer, PrimitiveGeometry, PrimitiveIndex};
+use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu, RadialGradientPrimitiveGpu};
+use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu, TextRunPrimitiveGpu};
+use prim_store::{YuvImagePrimitiveCpu, YuvImagePrimitiveGpu};
+use profiler::FrameProfileCounters;
+use render_task::{AlphaRenderItem, MaskCacheKey, MaskResult, RenderTask, RenderTaskIndex};
+use render_task::RenderTaskLocation;
+use resource_cache::ResourceCache;
+use scroll_tree::ScrollTree;
+use std::{cmp, f32, i32, mem, usize};
+use tiling::{AuxiliaryListsMap, CompositeOps, Frame, PackedLayer, PackedLayerIndex};
+use tiling::{PrimitiveFlags, PrimitiveRunCmd, RenderPass, RenderTargetContext};
+use tiling::{RenderTaskCollection, ScrollbarPrimitive, ScrollLayer, ScrollLayerIndex};
+use tiling::{StackingContext, StackingContextIndex};
+use util::{self, pack_as_float, rect_from_points_f, subtract_rect, TransformedRect};
+use util::TransformedRectKind;
+use webrender_traits::{as_scroll_parent_rect, BorderDisplayItem, BorderSide, BorderStyle};
+use webrender_traits::{BoxShadowClipMode, ClipRegion, ColorF, device_length, DeviceIntPoint};
+use webrender_traits::{DeviceIntRect, DeviceIntSize, DeviceUintSize, ExtendMode, FontKey};
+use webrender_traits::{FontRenderMode, GlyphOptions, ImageKey, ImageRendering, ItemRange};
+use webrender_traits::{LayerPoint, LayerRect, LayerSize, LayerToScrollTransform, PipelineId};
+use webrender_traits::{ScrollLayerId, ScrollLayerPixel, WebGLContextId, YuvColorSpace};
+
+#[derive(Clone, Copy)]
+pub struct FrameBuilderConfig {
+    pub enable_scrollbars: bool,
+    pub enable_subpixel_aa: bool,
+    pub debug: bool,
+}
+
+impl FrameBuilderConfig {
+    pub fn new(enable_scrollbars: bool,
+               enable_subpixel_aa: bool,
+               debug: bool)
+               -> FrameBuilderConfig {
+        FrameBuilderConfig {
+            enable_scrollbars: enable_scrollbars,
+            enable_subpixel_aa: enable_subpixel_aa,
+            debug: debug,
+        }
+    }
+}
+
+pub struct FrameBuilder {
+    screen_rect: LayerRect,
+    background_color: Option<ColorF>,
+    prim_store: PrimitiveStore,
+    cmds: Vec<PrimitiveRunCmd>,
+    config: FrameBuilderConfig,
+
+    stacking_context_store: Vec<StackingContext>,
+    scroll_layer_store: Vec<ScrollLayer>,
+    packed_layers: Vec<PackedLayer>,
+
+    scrollbar_prims: Vec<ScrollbarPrimitive>,
+
+    /// A stack of scroll layers used during building to properly parent new scroll layers.
+    scroll_layer_stack: Vec<ScrollLayerIndex>,
+}
+
+impl FrameBuilder {
+    pub fn new(viewport_size: LayerSize,
+               background_color: Option<ColorF>,
+               config: FrameBuilderConfig) -> FrameBuilder {
+        FrameBuilder {
+            screen_rect: LayerRect::new(LayerPoint::zero(), viewport_size),
+            background_color: background_color,
+            stacking_context_store: Vec::new(),
+            scroll_layer_store: Vec::new(),
+            prim_store: PrimitiveStore::new(),
+            cmds: Vec::new(),
+            packed_layers: Vec::new(),
+            scrollbar_prims: Vec::new(),
+            config: config,
+            scroll_layer_stack: Vec::new(),
+        }
+    }
+
+    fn add_primitive(&mut self,
+                     rect: &LayerRect,
+                     clip_region: &ClipRegion,
+                     container: PrimitiveContainer) -> PrimitiveIndex {
+
+        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 {
+            ClipSource::NoClip
+        };
+        let clip_info = MaskCacheInfo::new(&clip_source,
+                                           false,
+                                           &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::PushStackingContext(..) |
+            &mut PrimitiveRunCmd::PopStackingContext |
+            &mut PrimitiveRunCmd::PushScrollLayer(..) |
+            &mut PrimitiveRunCmd::PopScrollLayer => {}
+        }
+
+        self.cmds.push(PrimitiveRunCmd::PrimitiveRun(prim_index, 1));
+
+        prim_index
+    }
+
+    pub fn push_stacking_context(&mut self,
+                                 rect: LayerRect,
+                                 transform: LayerToScrollTransform,
+                                 pipeline_id: PipelineId,
+                                 scroll_layer_id: ScrollLayerId,
+                                 composite_ops: CompositeOps) {
+        let stacking_context_index = StackingContextIndex(self.stacking_context_store.len());
+        let packed_layer_index = PackedLayerIndex(self.packed_layers.len());
+
+        self.stacking_context_store.push(StackingContext {
+            local_rect: rect,
+            local_transform: transform,
+            scroll_layer_id: scroll_layer_id,
+            pipeline_id: pipeline_id,
+            xf_rect: None,
+            composite_ops: composite_ops,
+            packed_layer_index: packed_layer_index,
+        });
+        self.packed_layers.push(PackedLayer::empty());
+        self.cmds.push(PrimitiveRunCmd::PushStackingContext(stacking_context_index));
+
+    }
+
+    pub fn pop_stacking_context(&mut self) {
+        self.cmds.push(PrimitiveRunCmd::PopStackingContext);
+    }
+
+    pub fn push_scroll_layer(&mut self,
+                             scroll_layer_id: ScrollLayerId,
+                             clip_region: &ClipRegion,
+                             iframe_origin: &LayerPoint,
+                             content_size: &LayerSize) {
+        let scroll_layer_index = ScrollLayerIndex(self.scroll_layer_store.len());
+        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);
+
+        let parent_index = *self.scroll_layer_stack.last().unwrap_or(&scroll_layer_index);
+        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));
+
+
+        // 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 + iframe_origin.x,
+                                                 content_size.height + iframe_origin.y));
+        self.push_stacking_context(rect,
+                                   LayerToScrollTransform::identity(),
+                                   scroll_layer_id.pipeline_id,
+                                   scroll_layer_id,
+                                   CompositeOps::empty());
+
+        self.scroll_layer_stack.push(scroll_layer_index);
+    }
+
+    pub fn pop_scroll_layer(&mut self) {
+        self.pop_stacking_context();
+        self.cmds.push(PrimitiveRunCmd::PopScrollLayer);
+        self.scroll_layer_stack.pop();
+    }
+
+    pub fn add_solid_rectangle(&mut self,
+                               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,
+                                            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,
+                    scroll_layer_id: scroll_layer_id,
+                    border_radius: border_radius,
+                });
+            }
+        }
+    }
+
+    pub fn supported_style(&mut self, border: &BorderSide) -> bool {
+        match border.style {
+            BorderStyle::Solid |
+            BorderStyle::None |
+            BorderStyle::Dotted |
+            BorderStyle::Dashed |
+            BorderStyle::Inset |
+            BorderStyle::Ridge |
+            BorderStyle::Groove |
+            BorderStyle::Outset |
+            BorderStyle::Double => {
+                return true;
+            }
+            _ => {
+                println!("TODO: Other border styles {:?}", border.style);
+                return false;
+            }
+        }
+    }
+
+    pub fn add_border(&mut self,
+                      rect: LayerRect,
+                      clip_region: &ClipRegion,
+                      border: &BorderDisplayItem) {
+        let radius = &border.radius;
+        let left = &border.left;
+        let right = &border.right;
+        let top = &border.top;
+        let bottom = &border.bottom;
+
+        if !self.supported_style(left) || !self.supported_style(right) ||
+           !self.supported_style(top) || !self.supported_style(bottom) {
+            println!("Unsupported border style, not rendering border");
+            return;
+        }
+
+        // These colors are used during inset/outset scaling.
+        let left_color      = left.border_color(1.0, 2.0/3.0, 0.3, 0.7);
+        let top_color       = top.border_color(1.0, 2.0/3.0, 0.3, 0.7);
+        let right_color     = right.border_color(2.0/3.0, 1.0, 0.7, 0.3);
+        let bottom_color    = bottom.border_color(2.0/3.0, 1.0, 0.7, 0.3);
+
+        let tl_outer = LayerPoint::new(rect.origin.x, rect.origin.y);
+        let tl_inner = tl_outer + LayerPoint::new(radius.top_left.width.max(left.width),
+                                                  radius.top_left.height.max(top.width));
+
+        let tr_outer = LayerPoint::new(rect.origin.x + rect.size.width, rect.origin.y);
+        let tr_inner = tr_outer + LayerPoint::new(-radius.top_right.width.max(right.width),
+                                                  radius.top_right.height.max(top.width));
+
+        let bl_outer = LayerPoint::new(rect.origin.x, rect.origin.y + rect.size.height);
+        let bl_inner = bl_outer + LayerPoint::new(radius.bottom_left.width.max(left.width),
+                                                  -radius.bottom_left.height.max(bottom.width));
+
+        let br_outer = LayerPoint::new(rect.origin.x + rect.size.width,
+                                       rect.origin.y + rect.size.height);
+        let br_inner = br_outer - LayerPoint::new(radius.bottom_right.width.max(right.width),
+                                                  radius.bottom_right.height.max(bottom.width));
+
+        // The border shader is quite expensive. For simple borders, we can just draw
+        // the border with a few rectangles. This generally gives better batching, and
+        // a GPU win in fragment shader time.
+        // More importantly, the software (OSMesa) implementation we run tests on is
+        // particularly slow at running our complex border shader, compared to the
+        // rectangle shader. This has the effect of making some of our tests time
+        // out more often on CI (the actual cause is simply too many Servo processes and
+        // threads being run on CI at once).
+        // TODO(gw): Detect some more simple cases and handle those with simpler shaders too.
+        // TODO(gw): Consider whether it's only worth doing this for large rectangles (since
+        //           it takes a little more CPU time to handle multiple rectangles compared
+        //           to a single border primitive).
+        if left.style == BorderStyle::Solid {
+            let same_color = left_color == top_color &&
+                             left_color == right_color &&
+                             left_color == bottom_color;
+            let same_style = left.style == top.style &&
+                             left.style == right.style &&
+                             left.style == bottom.style;
+
+            if same_color && same_style && radius.is_zero() {
+                let rects = [
+                    LayerRect::new(rect.origin,
+                                   LayerSize::new(rect.size.width, top.width)),
+                    LayerRect::new(LayerPoint::new(tl_outer.x, tl_inner.y),
+                                   LayerSize::new(left.width,
+                                                  rect.size.height - top.width - bottom.width)),
+                    LayerRect::new(tr_inner,
+                                   LayerSize::new(right.width,
+                                                  rect.size.height - top.width - bottom.width)),
+                    LayerRect::new(LayerPoint::new(bl_outer.x, bl_inner.y),
+                                   LayerSize::new(rect.size.width, bottom.width))
+                ];
+
+                for rect in &rects {
+                    self.add_solid_rectangle(rect,
+                                             clip_region,
+                                             &top_color,
+                                             PrimitiveFlags::None);
+                }
+
+                return;
+            }
+        }
+
+        //Note: while similar to `ComplexClipRegion::get_inner_rect()` in spirit,
+        // this code is a bit more complex and can not there for be merged.
+        let inner_rect = rect_from_points_f(tl_inner.x.max(bl_inner.x),
+                                            tl_inner.y.max(tr_inner.y),
+                                            tr_inner.x.min(br_inner.x),
+                                            bl_inner.y.min(br_inner.y));
+
+        let prim_cpu = BorderPrimitiveCpu {
+            inner_rect: LayerRect::from_untyped(&inner_rect),
+        };
+
+        let prim_gpu = BorderPrimitiveGpu {
+            colors: [ left_color, top_color, right_color, bottom_color ],
+            widths: [ left.width, top.width, right.width, bottom.width ],
+            style: [
+                pack_as_float(left.style as u32),
+                pack_as_float(top.style as u32),
+                pack_as_float(right.style as u32),
+                pack_as_float(bottom.style as u32),
+            ],
+            radii: [
+                radius.top_left,
+                radius.top_right,
+                radius.bottom_right,
+                radius.bottom_left,
+            ],
+        };
+
+        self.add_primitive(&rect,
+                           clip_region,
+                           PrimitiveContainer::Border(prim_cpu, prim_gpu));
+    }
+
+    pub fn add_gradient(&mut self,
+                        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 &&
+                      (start_point.x == end_point.x ||
+                       start_point.y == end_point.y);
+        // Try to ensure that if the gradient is specified in reverse, then so long as the stops
+        // are also supplied in reverse that the rendered result will be equivalent. To do this,
+        // a reference orientation for the gradient line must be chosen, somewhat arbitrarily, so
+        // just designate the reference orientation as start < end. Aligned gradient rendering
+        // manages to produce the same result regardless of orientation, so don't worry about
+        // reversing in that case.
+        let reverse_stops = !aligned &&
+                            (start_point.x > end_point.x ||
+                             (start_point.x == end_point.x &&
+                              start_point.y > end_point.y));
+
+        let gradient_cpu = GradientPrimitiveCpu {
+            stops_range: stops,
+            extend_mode: extend_mode,
+            reverse_stops: reverse_stops,
+            cache_dirty: true,
+        };
+
+        // To get reftests exactly matching with reverse start/end
+        // points, it's necessary to reverse the gradient
+        // line in some cases.
+        let (sp, ep) = if reverse_stops {
+            (end_point, start_point)
+        } else {
+            (start_point, end_point)
+        };
+
+        let gradient_gpu = GradientPrimitiveGpu {
+            start_point: sp,
+            end_point: ep,
+            extend_mode: pack_as_float(extend_mode as u32),
+            padding: [0.0, 0.0, 0.0],
+        };
+
+        let prim = if aligned {
+            PrimitiveContainer::AlignedGradient(gradient_cpu, gradient_gpu)
+        } else {
+            PrimitiveContainer::AngleGradient(gradient_cpu, gradient_gpu)
+        };
+
+        self.add_primitive(&rect, clip_region, prim);
+    }
+
+    pub fn add_radial_gradient(&mut self,
+                               rect: LayerRect,
+                               clip_region: &ClipRegion,
+                               start_center: LayerPoint,
+                               start_radius: f32,
+                               end_center: LayerPoint,
+                               end_radius: f32,
+                               stops: ItemRange,
+                               extend_mode: ExtendMode) {
+        let radial_gradient_cpu = RadialGradientPrimitiveCpu {
+            stops_range: stops,
+            extend_mode: extend_mode,
+            cache_dirty: true,
+        };
+
+        let radial_gradient_gpu = RadialGradientPrimitiveGpu {
+            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,
+                           clip_region,
+                           PrimitiveContainer::RadialGradient(radial_gradient_cpu, radial_gradient_gpu));
+    }
+
+    pub fn add_text(&mut self,
+                    rect: LayerRect,
+                    clip_region: &ClipRegion,
+                    font_key: FontKey,
+                    size: Au,
+                    blur_radius: Au,
+                    color: &ColorF,
+                    glyph_range: ItemRange,
+                    glyph_options: Option<GlyphOptions>) {
+        if color.a == 0.0 {
+            return
+        }
+
+        if size.0 <= 0 {
+            return
+        }
+
+        let (render_mode, glyphs_per_run) = if blur_radius == Au(0) {
+            // TODO(gw): Use a proper algorithm to select
+            // whether this item should be rendered with
+            // subpixel AA!
+            let render_mode = if self.config.enable_subpixel_aa {
+                FontRenderMode::Subpixel
+            } else {
+                FontRenderMode::Alpha
+            };
+
+            (render_mode, 8)
+        } else {
+            // TODO(gw): Support breaking up text shadow when
+            // the size of the text run exceeds the dimensions
+            // of the render target texture.
+            (FontRenderMode::Alpha, glyph_range.length)
+        };
+
+        let text_run_count = (glyph_range.length + glyphs_per_run - 1) / glyphs_per_run;
+        for run_index in 0..text_run_count {
+            let start = run_index * glyphs_per_run;
+            let end = cmp::min(start + glyphs_per_run, glyph_range.length);
+            let sub_range = ItemRange {
+                start: glyph_range.start + start,
+                length: end - start,
+            };
+
+            let prim_cpu = TextRunPrimitiveCpu {
+                font_key: font_key,
+                logical_font_size: size,
+                blur_radius: blur_radius,
+                glyph_range: sub_range,
+                cache_dirty: true,
+                glyph_instances: Vec::new(),
+                color_texture_id: SourceTexture::Invalid,
+                color: *color,
+                render_mode: render_mode,
+                glyph_options: glyph_options,
+                resource_address: GpuStoreAddress(0),
+            };
+
+            let prim_gpu = TextRunPrimitiveGpu {
+                color: *color,
+            };
+
+            self.add_primitive(&rect,
+                               clip_region,
+                               PrimitiveContainer::TextRun(prim_cpu, prim_gpu));
+        }
+    }
+
+    pub fn add_box_shadow(&mut self,
+                          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,
+                                     clip_region,
+                                     color,
+                                     PrimitiveFlags::None);
+            return;
+        }
+
+        let bs_rect = box_bounds.translate(box_offset)
+                                .inflate(spread_radius, spread_radius);
+
+        let outside_edge_size = 2.0 * blur_radius;
+        let inside_edge_size = outside_edge_size.max(border_radius);
+        let edge_size = outside_edge_size + inside_edge_size;
+        let outer_rect = bs_rect.inflate(outside_edge_size, outside_edge_size);
+        let mut instance_rects = Vec::new();
+        let (prim_rect, inverted) = match clip_mode {
+            BoxShadowClipMode::Outset | BoxShadowClipMode::None => {
+                subtract_rect(&outer_rect, box_bounds, &mut instance_rects);
+                (outer_rect, 0.0)
+            }
+            BoxShadowClipMode::Inset => {
+                subtract_rect(box_bounds, &bs_rect, &mut instance_rects);
+                (*box_bounds, 1.0)
+            }
+        };
+
+        if edge_size == 0.0 {
+            for rect in &instance_rects {
+                self.add_solid_rectangle(rect,
+                                         clip_region,
+                                         color,
+                                         PrimitiveFlags::None)
+            }
+        } else {
+            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(&prim_rect,
+                               clip_region,
+                               PrimitiveContainer::BoxShadow(prim_gpu, instance_rects));
+        }
+    }
+
+    pub fn add_webgl_rectangle(&mut self,
+                               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),
+        };
+
+        let prim_gpu = ImagePrimitiveGpu {
+            stretch_size: rect.size,
+            tile_spacing: LayerSize::zero(),
+        };
+
+        self.add_primitive(&rect,
+                           clip_region,
+                           PrimitiveContainer::Image(prim_cpu, prim_gpu));
+    }
+
+    pub fn add_image(&mut self,
+                     rect: LayerRect,
+                     clip_region: &ClipRegion,
+                     stretch_size: &LayerSize,
+                     tile_spacing: &LayerSize,
+                     image_key: ImageKey,
+                     image_rendering: ImageRendering) {
+        let prim_cpu = ImagePrimitiveCpu {
+            kind: ImagePrimitiveKind::Image(image_key,
+                                            image_rendering,
+                                            *tile_spacing),
+            color_texture_id: SourceTexture::Invalid,
+            resource_address: GpuStoreAddress(0),
+        };
+
+        let prim_gpu = ImagePrimitiveGpu {
+            stretch_size: *stretch_size,
+            tile_spacing: *tile_spacing,
+        };
+
+        self.add_primitive(&rect,
+                           clip_region,
+                           PrimitiveContainer::Image(prim_cpu, prim_gpu));
+    }
+
+    pub fn add_yuv_image(&mut self,
+                         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,
+        };
+
+        let prim_gpu = YuvImagePrimitiveGpu::new(rect.size, color_space);
+
+        self.add_primitive(&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,
+                                                scroll_tree: &ScrollTree,
+                                                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,
+                                                           scroll_tree,
+                                                           auxiliary_lists_map,
+                                                           resource_cache,
+                                                           profile_counters,
+                                                           device_pixel_ratio);
+    }
+
+    fn update_scroll_bars(&mut self, scroll_tree: &ScrollTree) {
+        let distance_from_edge = 8.0;
+
+        for scrollbar_prim in &self.scrollbar_prims {
+            let mut geom = (*self.prim_store.gpu_geometry.get(GpuStoreAddress(scrollbar_prim.prim_index.0 as i32))).clone();
+            let scroll_layer = &scroll_tree.layers[&scrollbar_prim.scroll_layer_id];
+
+            let scrollable_distance = scroll_layer.scrollable_height();
+
+            if scrollable_distance <= 0.0 {
+                geom.local_clip_rect.size = LayerSize::zero();
+                *self.prim_store.gpu_geometry.get_mut(GpuStoreAddress(scrollbar_prim.prim_index.0 as i32)) = geom;
+                continue;
+            }
+
+            let f = -scroll_layer.scrolling.offset.y / scrollable_distance;
+
+            let min_y = scroll_layer.local_viewport_rect.origin.y -
+                        scroll_layer.scrolling.offset.y +
+                        distance_from_edge;
+
+            let max_y = scroll_layer.local_viewport_rect.origin.y +
+                        scroll_layer.local_viewport_rect.size.height -
+                        scroll_layer.scrolling.offset.y -
+                        geom.local_rect.size.height -
+                        distance_from_edge;
+
+            geom.local_rect.origin.x = scroll_layer.local_viewport_rect.origin.x +
+                                       scroll_layer.local_viewport_rect.size.width -
+                                       geom.local_rect.size.width -
+                                       distance_from_edge;
+
+            geom.local_rect.origin.y = util::lerp(min_y, max_y, f);
+            geom.local_clip_rect = geom.local_rect;
+
+            let clip_source = if scrollbar_prim.border_radius == 0.0 {
+                ClipSource::NoClip
+            } else {
+                ClipSource::Complex(geom.local_rect, scrollbar_prim.border_radius)
+            };
+            self.prim_store.set_clip_source(scrollbar_prim.prim_index, clip_source);
+            *self.prim_store.gpu_geometry.get_mut(GpuStoreAddress(scrollbar_prim.prim_index.0 as i32)) = geom;
+        }
+    }
+
+    fn build_render_task(&self) -> (RenderTask, usize) {
+        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(),
+                                                           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 composite_count = stacking_context.composite_ops.count();
+                    for _ in 0..composite_count {
+                        let stacking_context_rect =
+                            stacking_context.xf_rect.as_ref().unwrap().bounding_rect;
+                        let location = RenderTaskLocation::Dynamic(None, stacking_context_rect.size);
+                        let new_task = RenderTask::new_alpha_batch(next_task_index,
+                                                                   stacking_context_rect.origin,
+                                                                   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;
+                    }
+
+                    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 stacking_context_rect =
+                                    stacking_context.xf_rect.as_ref().unwrap().bounding_rect;
+                                let readback_task =
+                                    RenderTask::new_readback(stacking_context_index, stacking_context_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;
+                            }
+                        }
+                    }
+                }
+                PrimitiveRunCmd::PrimitiveRun(first_prim_index, prim_count) => {
+                    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;
+                    }
+
+                    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);
+
+                            // Add any dynamic render tasks needed to render this primitive
+                            if let Some(ref render_task) = prim_metadata.render_task {
+                                current_task.children.push(render_task.clone());
+                            }
+                            if let Some(ref clip_task) = prim_metadata.clip_task {
+                                current_task.children.push(clip_task.clone());
+                            }
+
+                            let transform_kind = stacking_context.xf_rect.as_ref().unwrap().kind;
+                            let needs_clipping = prim_metadata.clip_task.is_some();
+                            let needs_blending = transform_kind == TransformedRectKind::Complex ||
+                                                 !prim_metadata.is_opaque ||
+                                                 needs_clipping;
+
+                            let items = if needs_blending {
+                                &mut current_task.as_alpha_batch().alpha_items
+                            } else {
+                                &mut current_task.as_alpha_batch().opaque_items
+                            };
+                            items.push(AlphaRenderItem::Primitive(stacking_context_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,
+                 scroll_tree: &ScrollTree,
+                 auxiliary_lists_map: &AuxiliaryListsMap,
+                 device_pixel_ratio: f32) -> 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)));
+
+        // 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));
+
+        self.update_scroll_bars(scroll_tree);
+
+        self.build_layer_screen_rects_and_cull_layers(&screen_rect,
+                                                      scroll_tree,
+                                                      auxiliary_lists_map,
+                                                      resource_cache,
+                                                      &mut profile_counters,
+                                                      device_pixel_ratio);
+
+        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();
+
+        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);
+            }
+        }
+
+        let deferred_resolves = self.prim_store.resolve_primitives(resource_cache,
+                                                                   device_pixel_ratio);
+
+        let mut passes = Vec::new();
+
+        // Do the allocations now, assigning each tile's tasks to a render
+        // pass and target as required.
+        for index in 0..required_pass_count {
+            passes.push(RenderPass::new(index as isize,
+                                        index == required_pass_count-1,
+                                        cache_size));
+        }
+
+        main_render_task.assign_to_passes(passes.len() - 1, &mut passes);
+
+        for pass in &mut passes {
+            let ctx = RenderTargetContext {
+                stacking_context_store: &self.stacking_context_store,
+                prim_store: &self.prim_store,
+                resource_cache: resource_cache,
+            };
+
+            pass.build(&ctx, &mut render_tasks);
+
+            profile_counters.passes.inc();
+            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,
+            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(),
+            gpu_data128: self.prim_store.gpu_data128.build(),
+            gpu_geometry: self.prim_store.gpu_geometry.build(),
+            gpu_gradient_data: self.prim_store.gpu_gradient_data.build(),
+            gpu_resource_rects: self.prim_store.gpu_resource_rects.build(),
+            deferred_resolves: deferred_resolves,
+        }
+    }
+}
+
+struct LayerRectCalculationAndCullingPass<'a> {
+    frame_builder: &'a mut FrameBuilder,
+    screen_rect: &'a DeviceIntRect,
+    scroll_tree: &'a ScrollTree,
+    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>
+}
+
+impl<'a> LayerRectCalculationAndCullingPass<'a> {
+    fn create_and_run(frame_builder: &'a mut FrameBuilder,
+                      screen_rect: &'a DeviceIntRect,
+                      scroll_tree: &'a ScrollTree,
+                      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,
+            scroll_tree: 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,
+        };
+        pass.run();
+    }
+
+    fn run(&mut self) {
+        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::PopStackingContext => {
+                    self.stacking_context_stack.pop();
+                }
+                &PrimitiveRunCmd::PopScrollLayer => self.handle_pop_scroll_layer(),
+            }
+        }
+
+        mem::replace(&mut self.frame_builder.cmds, commands);
+    }
+
+    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.scroll_tree.layers[&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);
+        }
+    }
+
+    fn handle_push_stacking_context(&mut self, stacking_context_index: StackingContextIndex) {
+        self.stacking_context_stack.push(stacking_context_index);
+
+        let stacking_context = &mut self.frame_builder
+                                        .stacking_context_store[stacking_context_index.0];
+        let packed_layer = &mut self.frame_builder
+                                    .packed_layers[stacking_context.packed_layer_index.0];
+        let scroll_layer = &self.scroll_tree.layers[&stacking_context.scroll_layer_id];
+        packed_layer.transform = scroll_layer.world_content_transform
+                                             .with_source::<ScrollLayerPixel>()
+                                             .pre_mul(&stacking_context.local_transform);
+        packed_layer.inv_transform = packed_layer.transform.inverse().unwrap();
+
+        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_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);
+
+        if let Some(layer_local_rect) = layer_local_rect {
+            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;
+                stacking_context.xf_rect = Some(layer_xf_rect);
+            }
+        }
+    }
+
+    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;
+            }
+        }
+
+        // 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())),
+                _ => {},
+            };
+
+            if scroll_layer.parent_index == scroll_layer_index {
+                break;
+            }
+            scroll_layer_index = scroll_layer.parent_index;
+        }
+
+        self.current_clip_stack.reverse();
+    }
+
+    fn handle_primitive_run(&mut self, prim_index: PrimitiveIndex, prim_count: usize) {
+        let scroll_layer_index = *self.scroll_layer_stack.last().unwrap();
+        self.rebuild_clip_info_stack_if_necessary(scroll_layer_index);
+
+        let stacking_context_index = self.stacking_context_stack.last().unwrap();
+        let stacking_context = &self.frame_builder.stacking_context_store[stacking_context_index.0];
+        if !stacking_context.is_visible() {
+            return;
+        }
+
+        let packed_layer_index = stacking_context.packed_layer_index;
+        let packed_layer = &self.frame_builder.packed_layers[packed_layer_index.0];
+        let auxiliary_lists = self.auxiliary_lists_map.get(&stacking_context.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,
+                                                                 &packed_layer.transform,
+                                                                 &packed_layer.local_clip_rect,
+                                                                 self.device_pixel_ratio) {
+                if self.frame_builder.prim_store.prepare_prim_for_render(prim_index,
+                                                                         self.resource_cache,
+                                                                         &packed_layer.transform,
+                                                                         self.device_pixel_ratio,
+                                                                         auxiliary_lists) {
+                    self.frame_builder.prim_store.build_bounding_rect(prim_index,
+                                                                      self.screen_rect,
+                                                                      &packed_layer.transform,
+                                                                      &packed_layer.local_clip_rect,
+                                                                      self.device_pixel_ratio);
+                }
+
+                // If the primitive is visible, consider culling it via clip rect(s).
+                // If it is visible but has clips, create the clip task for it.
+                let prim_bounding_rect =
+                    match self.frame_builder.prim_store.cpu_bounding_rects[prim_index.0] {
+                    Some(rect) => rect,
+                    _ => continue,
+                };
+
+                let prim_metadata = &mut self.frame_builder.prim_store.cpu_metadata[prim_index.0];
+                let prim_clip_info = prim_metadata.clip_cache_info.as_ref();
+                let mut visible = true;
+
+                if let Some(info) = prim_clip_info {
+                    self.current_clip_stack.push((packed_layer_index, info.clone()));
+                }
+
+                // 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)
+                        }
+                    };
+                    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;
+                        }
+                        MaskResult::Inside(task) => prim_metadata.clip_task = Some(task),
+                    }
+                }
+
+                if prim_clip_info.is_some() {
+                    self.current_clip_stack.pop();
+                }
+
+                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,47 +1,75 @@
 /* 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 renderer::MAX_VERTEX_TEXTURE_WIDTH;
+use device::TextureFilter;
+use std::marker::PhantomData;
 use std::mem;
+use webrender_traits::ImageFormat;
 
 #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
 pub struct GpuStoreAddress(pub i32);
 
+pub trait GpuStoreLayout {
+    fn image_format() -> ImageFormat;
+
+    fn texture_width<T>() -> usize;
+
+    fn texture_filter() -> TextureFilter;
+
+    fn texel_size() -> usize {
+        match Self::image_format() {
+            ImageFormat::RGBA8 => 4,
+            ImageFormat::RGBAF32 => 16,
+            _ => unreachable!(),
+        }
+    }
+
+    fn texels_per_item<T>() -> usize {
+        let item_size = mem::size_of::<T>();
+        let texel_size = Self::texel_size();
+        debug_assert!(item_size % texel_size == 0);
+        item_size / texel_size
+    }
+
+    fn items_per_row<T>() -> usize {
+        Self::texture_width::<T>() / Self::texels_per_item::<T>()
+    }
+}
+
 /// A CPU-side buffer storing content to be uploaded to the GPU.
-pub struct GpuStore<T> {
+pub struct GpuStore<T, L> {
     data: Vec<T>,
+    layout: PhantomData<L>,
     // TODO(gw): Could store this intrusively inside
     // the data array free slots.
     //free_list: Vec<GpuStoreAddress>,
 }
 
-impl<T: Clone + Default> GpuStore<T> {
-    pub fn new() -> GpuStore<T> {
+impl<T: Clone + Default, L: GpuStoreLayout> GpuStore<T, L> {
+    pub fn new() -> GpuStore<T, L> {
         GpuStore {
             data: Vec::new(),
+            layout: PhantomData,
             //free_list: Vec::new(),
         }
     }
 
     pub fn push<E>(&mut self, data: E) -> GpuStoreAddress where T: From<E> {
         let address = GpuStoreAddress(self.data.len() as i32);
         self.data.push(T::from(data));
         address
     }
 
     // TODO(gw): Change this to do incremental updates, which means
     // there is no need to copy all this data during every scroll!
     pub fn build(&self) -> Vec<T> {
-        let item_size = mem::size_of::<T>();
-        debug_assert!(item_size % 16 == 0);
-        let vecs_per_item = item_size / 16;
-        let items_per_row = MAX_VERTEX_TEXTURE_WIDTH / vecs_per_item;
+        let items_per_row = L::items_per_row::<T>();
 
         let mut items = self.data.clone();
 
         // Extend the data array to be a multiple of the row size.
         // This ensures memory safety when the array is passed to
         // OpenGL to upload to the GPU.
         while items.len() % items_per_row != 0 {
             items.push(T::default());
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -13,19 +13,22 @@ use offscreen_gl_context::{ColorAttachme
 use profiler::BackendProfileCounters;
 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, MixBlendMode, NativeFontHandle};
+use webrender_traits::{ImageFormat, NativeFontHandle, MixBlendMode};
 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
 // to be added to an atlas). The texture cache
 // manages the allocation and freeing of these
 // IDs, and the rendering thread maintains a
@@ -185,16 +188,17 @@ pub enum TextureSampler {
     Data16,
     Data32,
     Data64,
     Data128,
     Layers,
     RenderTasks,
     Geometry,
     ResourceRects,
+    Gradients,
 }
 
 impl TextureSampler {
     pub fn color(n: usize) -> TextureSampler {
         match n {
             0 => TextureSampler::Color0,
             1 => TextureSampler::Color1,
             2 => TextureSampler::Color2,
@@ -264,16 +268,19 @@ pub enum ClipAttribute {
     Position,
     // instance frequency
     RenderTaskIndex,
     LayerIndex,
     DataIndex,
     SegmentIndex,
 }
 
+// A packed RGBA8 color ordered for vertex data or similar.
+// Use PackedTexel instead if intending to upload to a texture.
+
 #[derive(Debug, Clone, Copy)]
 #[repr(C)]
 pub struct PackedColor {
     pub r: u8,
     pub g: u8,
     pub b: u8,
     pub a: u8,
 }
@@ -284,16 +291,39 @@ impl PackedColor {
             r: (0.5 + color.r * COLOR_FLOAT_TO_FIXED).floor() as u8,
             g: (0.5 + color.g * COLOR_FLOAT_TO_FIXED).floor() as u8,
             b: (0.5 + color.b * COLOR_FLOAT_TO_FIXED).floor() as u8,
             a: (0.5 + color.a * COLOR_FLOAT_TO_FIXED).floor() as u8,
         }
     }
 }
 
+// RGBA8 textures currently pack texels in BGRA format for upload.
+// PackedTexel abstracts away this difference from PackedColor.
+
+#[derive(Debug, Clone, Copy)]
+#[repr(C)]
+pub struct PackedTexel {
+    pub b: u8,
+    pub g: u8,
+    pub r: u8,
+    pub a: u8,
+}
+
+impl PackedTexel {
+    pub fn from_color(color: &ColorF) -> PackedTexel {
+        PackedTexel {
+            b: (0.5 + color.b * COLOR_FLOAT_TO_FIXED).floor() as u8,
+            g: (0.5 + color.g * COLOR_FLOAT_TO_FIXED).floor() as u8,
+            r: (0.5 + color.r * COLOR_FLOAT_TO_FIXED).floor() as u8,
+            a: (0.5 + color.a * COLOR_FLOAT_TO_FIXED).floor() as u8,
+        }
+    }
+}
+
 #[derive(Debug, Clone, Copy)]
 #[repr(C)]
 pub struct PackedVertex {
     pub pos: [f32; 2],
 }
 
 #[derive(Debug)]
 #[repr(C)]
@@ -337,20 +367,45 @@ impl DebugColorVertex {
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum RenderTargetMode {
     None,
     SimpleRenderTarget,
     LayerRenderTarget(i32),      // Number of texture layers
 }
 
 pub enum TextureUpdateOp {
-    Create(u32, u32, ImageFormat, TextureFilter, RenderTargetMode, Option<Arc<Vec<u8>>>),
-    Update(u32, u32, u32, u32, Arc<Vec<u8>>, Option<u32>),
-    Grow(u32, u32, ImageFormat, TextureFilter, RenderTargetMode),
-    Free
+    Create {
+      width: u32,
+      height: u32,
+      format: ImageFormat,
+      filter: TextureFilter,
+      mode: RenderTargetMode,
+      data: Option<ImageData>,
+    },
+    Update {
+        page_pos_x: u32,    // the texture page position which we want to upload
+        page_pos_y: u32,
+        width: u32,
+        height: u32,
+        data: Arc<Vec<u8>>,
+        stride: Option<u32>,
+    },
+    UpdateForExternalBuffer {
+        rect: DeviceUintRect,
+        id: ExternalImageId,
+        stride: Option<u32>,
+    },
+    Grow {
+        width: u32,
+        height: u32,
+        format: ImageFormat,
+        filter: TextureFilter,
+        mode: RenderTargetMode,
+    },
+    Free,
 }
 
 pub type ExternalImageUpdateList = Vec<ExternalImageId>;
 
 pub struct TextureUpdate {
     pub id: CacheTextureId,
     pub op: TextureUpdateOp,
 }
@@ -430,12 +485,32 @@ pub enum LowLevelFilterOp {
     HueRotate(i32),
     Invert(Au),
     Opacity(Au),
     Saturate(Au),
     Sepia(Au),
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub enum CompositionOp {
-    MixBlend(MixBlendMode),
-    Filter(LowLevelFilterOp),
-}
\ No newline at end of file
+pub enum HardwareCompositeOp {
+    Multiply,
+    Max,
+    Min,
+}
+
+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,
+        }
+    }
+}
--- a/gfx/webrender/src/layer.rs
+++ b/gfx/webrender/src/layer.rs
@@ -114,17 +114,26 @@ impl Layer {
         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 = self.local_transform.inverse().unwrap();
+        let inv_transform = match self.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());
 
         self.combined_local_viewport_rect = viewport_rect;
         self.world_viewport_transform = parent_world_transform.pre_mul(&self.local_transform);
--- a/gfx/webrender/src/lib.rs
+++ b/gfx/webrender/src/lib.rs
@@ -43,46 +43,50 @@
 //! [notifier]: struct.Renderer.html#method.set_render_notifier
 
 #[macro_use]
 extern crate lazy_static;
 #[macro_use]
 extern crate log;
 #[macro_use]
 extern crate bitflags;
+#[macro_use]
+extern crate thread_profiler;
 
 mod batch_builder;
 mod debug_colors;
 mod debug_font_data;
 mod debug_render;
 mod device;
 mod frame;
+mod frame_builder;
 mod freelist;
 mod geometry;
 mod gpu_store;
 mod internal_types;
 mod layer;
 mod mask_cache;
 mod prim_store;
 mod profiler;
 mod record;
 mod render_backend;
+mod render_task;
 mod resource_cache;
 mod scene;
 mod scroll_tree;
 mod spring;
 mod texture_cache;
 mod tiling;
 mod util;
 
 mod shader_source {
     include!(concat!(env!("OUT_DIR"), "/shaders.rs"));
 }
 
-pub use record::{ApiRecordingReceiver, set_recording_detour, WEBRENDER_RECORDING_HEADER};
+pub use record::{ApiRecordingReceiver, BinaryRecorder, WEBRENDER_RECORDING_HEADER};
 
 mod platform {
     #[cfg(target_os="macos")]
     pub use platform::macos::font;
     #[cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))]
     pub use platform::unix::font;
     #[cfg(target_os = "windows")]
     pub use platform::windows::font;
@@ -122,10 +126,13 @@ extern crate gleam;
 extern crate num_traits;
 //extern crate notify;
 extern crate time;
 extern crate webrender_traits;
 extern crate offscreen_gl_context;
 extern crate byteorder;
 extern crate threadpool;
 
+#[cfg(any(target_os="macos", target_os="windows"))]
+extern crate gamma_lut;
+
 pub use renderer::{ExternalImage, ExternalImageSource, ExternalImageHandler};
 pub use renderer::{Renderer, RendererOptions};
--- a/gfx/webrender/src/mask_cache.rs
+++ b/gfx/webrender/src/mask_cache.rs
@@ -1,136 +1,144 @@
 /* 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 gpu_store::{GpuStore, GpuStoreAddress};
+use gpu_store::GpuStoreAddress;
 use prim_store::{ClipData, GpuBlock32, PrimitiveStore};
 use prim_store::{CLIP_DATA_GPU_SIZE, MASK_DATA_GPU_SIZE};
-use util::{rect_from_points_f, TransformedRect};
+use renderer::VertexDataStore;
+use util::{MatrixHelpers, TransformedRect};
 use webrender_traits::{AuxiliaryLists, BorderRadius, ClipRegion, ComplexClipRegion, ImageMask};
 use webrender_traits::{DeviceIntRect, DeviceIntSize, LayerRect, LayerToWorldTransform};
 
-const MAX_COORD: f32 = 1.0e+16;
-
 #[derive(Clone, Debug)]
 pub enum ClipSource {
     NoClip,
     Complex(LayerRect, f32),
     Region(ClipRegion),
 }
 
 impl ClipSource {
-    pub fn to_rect(&self) -> Option<LayerRect> {
+    pub fn image_mask(&self) -> Option<ImageMask> {
         match self {
             &ClipSource::NoClip => None,
-            &ClipSource::Complex(rect, _) => Some(rect),
-            &ClipSource::Region(ref region) => Some(region.main),
-        }
-    }
-}
-impl<'a> From<&'a ClipRegion> for ClipSource {
-    fn from(clip_region: &'a ClipRegion) -> ClipSource {
-        if clip_region.is_complex() {
-            ClipSource::Region(clip_region.clone())
-        } else {
-            ClipSource::NoClip
+            &ClipSource::Complex(..) => None,
+            &ClipSource::Region(ref region) => region.image_mask,
         }
     }
 }
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 pub struct ClipAddressRange {
     pub start: GpuStoreAddress,
-    pub item_count: u32,
+    item_count: u32,
 }
 
 #[derive(Clone, Debug)]
 pub struct MaskCacheInfo {
     pub clip_range: ClipAddressRange,
+    pub effective_clip_count: u32,
     pub image: Option<(ImageMask, GpuStoreAddress)>,
     pub local_rect: Option<LayerRect>,
     pub local_inner: Option<LayerRect>,
     pub inner_rect: DeviceIntRect,
     pub outer_rect: DeviceIntRect,
+    pub is_aligned: bool,
 }
 
 impl MaskCacheInfo {
     /// Create a new mask cache info. It allocates the GPU store data but leaves
     /// it unitialized for the following `update()` call to deal with.
     pub fn new(source: &ClipSource,
-               clip_store: &mut GpuStore<GpuBlock32>)
+               extra_clip: bool,
+               clip_store: &mut VertexDataStore<GpuBlock32>)
                -> Option<MaskCacheInfo> {
         let (image, clip_range) = match source {
             &ClipSource::NoClip => return None,
-            &ClipSource::Complex(..) => (
-                None,
+            &ClipSource::Complex(..) => {
+                (None,
                 ClipAddressRange {
                     start: clip_store.alloc(CLIP_DATA_GPU_SIZE),
                     item_count: 1,
-                }
-            ),
-            &ClipSource::Region(ref region) => (
-                region.image_mask.map(|info|
-                    (info, clip_store.alloc(MASK_DATA_GPU_SIZE))
-                ),
+                })
+            },
+            &ClipSource::Region(ref region) => {
+                let count = region.complex.length + if extra_clip {1} else {0};
+                (region.image_mask.map(|info|
+                    (info, clip_store.alloc(MASK_DATA_GPU_SIZE))),
                 ClipAddressRange {
-                    start: if region.complex.length > 0 {
-                        clip_store.alloc(CLIP_DATA_GPU_SIZE * region.complex.length)
+                    start: if count > 0 {
+                        clip_store.alloc(CLIP_DATA_GPU_SIZE * count)
                     } else {
                         GpuStoreAddress(0)
                     },
-                    item_count: region.complex.length as u32,
-                }
-            ),
+                    item_count: count as u32,
+                })
+            },
         };
 
         Some(MaskCacheInfo {
             clip_range: clip_range,
+            effective_clip_count: clip_range.item_count,
             image: image,
             local_rect: None,
             local_inner: None,
             inner_rect: DeviceIntRect::zero(),
             outer_rect: DeviceIntRect::zero(),
+            is_aligned: true,
         })
     }
 
     pub fn update(&mut self,
                   source: &ClipSource,
                   transform: &LayerToWorldTransform,
-                  clip_store: &mut GpuStore<GpuBlock32>,
+                  clip_store: &mut VertexDataStore<GpuBlock32>,
                   device_pixel_ratio: f32,
                   aux_lists: &AuxiliaryLists) {
 
+        self.is_aligned = transform.can_losslessly_transform_and_perspective_project_a_2d_rect();
+
         if self.local_rect.is_none() {
             let mut local_rect;
             let mut local_inner: Option<LayerRect>;
             match source {
                 &ClipSource::NoClip => unreachable!(),
                 &ClipSource::Complex(rect, radius) => {
                     let slice = clip_store.get_slice_mut(self.clip_range.start, CLIP_DATA_GPU_SIZE);
                     let data = ClipData::uniform(rect, radius);
                     PrimitiveStore::populate_clip_data(slice, data);
                     debug_assert_eq!(self.clip_range.item_count, 1);
                     local_rect = Some(rect);
                     local_inner = ComplexClipRegion::new(rect, BorderRadius::uniform(radius))
                                                     .get_inner_rect();
                 }
                 &ClipSource::Region(ref region) => {
-                    local_rect = Some(LayerRect::from_untyped(&rect_from_points_f(-MAX_COORD, -MAX_COORD, MAX_COORD, MAX_COORD)));
+                    local_rect = Some(region.main);
                     local_inner = match region.image_mask {
                         Some(ref mask) if !mask.repeat => {
                             local_rect = local_rect.and_then(|r| r.intersection(&mask.rect));
                             None
                         },
                         Some(_) => None,
                         None => local_rect,
                     };
+
                     let clips = aux_lists.complex_clip_regions(&region.complex);
-                    assert_eq!(self.clip_range.item_count, clips.len() as u32);
+                    self.effective_clip_count = if !self.is_aligned && self.clip_range.item_count > clips.len() as u32 {
+                        // we have an extra clip rect coming from the transformed layer
+                        assert_eq!(self.clip_range.item_count, clips.len() as u32 + 1);
+                        let address = GpuStoreAddress(self.clip_range.start.0 + (CLIP_DATA_GPU_SIZE * clips.len()) as i32);
+                        let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE);
+                        PrimitiveStore::populate_clip_data(slice, ClipData::uniform(region.main, 0.0));
+                        self.clip_range.item_count
+                    } else {
+                        clips.len() as u32
+                    };
+
                     let slice = clip_store.get_slice_mut(self.clip_range.start, CLIP_DATA_GPU_SIZE * clips.len());
                     for (clip, chunk) in clips.iter().zip(slice.chunks_mut(CLIP_DATA_GPU_SIZE)) {
                         let data = ClipData::from_clip_region(clip);
                         PrimitiveStore::populate_clip_data(chunk, data);
                         local_rect = local_rect.and_then(|r| r.intersection(&clip.rect));
                         local_inner = local_inner.and_then(|r| clip.get_inner_rect()
                                                                    .and_then(|ref inner| r.intersection(&inner)));
                     }
@@ -149,9 +157,16 @@ impl MaskCacheInfo {
             let transformed = TransformedRect::new(inner_rect,
                                                    &transform,
                                                    device_pixel_ratio);
             transformed.inner_rect
         } else {
             DeviceIntRect::new(self.outer_rect.origin, DeviceIntSize::zero())
         }
     }
+
+    /// Check if this `MaskCacheInfo` actually carries any masks. `effective_clip_count`
+    /// can change during the `update` call depending on the transformation, so the mask may
+    /// appear to be empty.
+    pub fn is_masking(&self) -> bool {
+        self.image.is_some() || self.effective_clip_count != 0
+    }
 }
--- a/gfx/webrender/src/platform/macos/font.rs
+++ b/gfx/webrender/src/platform/macos/font.rs
@@ -11,22 +11,23 @@ use core_graphics::data_provider::CGData
 use core_graphics::font::{CGFont, CGGlyph};
 use core_graphics::geometry::{CGPoint, CGSize, CGRect};
 use core_text::font::CTFont;
 use core_text::font_descriptor::kCTFontDefaultOrientation;
 use core_text;
 use std::collections::HashMap;
 use std::collections::hash_map::Entry;
 use webrender_traits::{ColorU, FontKey, FontRenderMode, GlyphDimensions};
-
-pub type NativeFontHandle = CGFont;
+use webrender_traits::{GlyphKey, GlyphOptions, SubpixelPoint};
+use gamma_lut::{GammaLut, Color as ColorLut};
 
 pub struct FontContext {
     cg_fonts: HashMap<FontKey, CGFont>,
     ct_fonts: HashMap<(FontKey, Au), CTFont>,
+    gamma_lut: GammaLut,
 }
 
 pub struct RasterizedGlyph {
     pub width: u32,
     pub height: u32,
     pub bytes: Vec<u8>,
 }
 
@@ -64,42 +65,69 @@ fn supports_subpixel_aa() -> bool {
     cg_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0);
     let point = CGPoint {x: -1., y: 0.};
     let glyph = '|' as CGGlyph;
     ct_font.draw_glyphs(&[glyph], &[point], cg_context.clone());
     let data = cg_context.data();
     data[0] != data[1] || data[1] != data[2]
 }
 
-fn get_glyph_metrics(ct_font: &CTFont, glyph: CGGlyph) -> GlyphMetrics {
+fn get_glyph_metrics(ct_font: &CTFont,
+                     glyph: CGGlyph,
+                     subpixel_point: &SubpixelPoint) -> GlyphMetrics {
     let bounds = ct_font.get_bounding_rects_for_glyphs(kCTFontDefaultOrientation, &[glyph]);
 
-    let rasterized_left = bounds.origin.x.floor() as i32;
-    let rasterized_width =
-        (bounds.origin.x - (rasterized_left as f64) + bounds.size.width).ceil() as u32;
-    let rasterized_descent = (-bounds.origin.y).ceil() as i32;
-    let rasterized_ascent = (bounds.size.height + bounds.origin.y).ceil() as i32;
-    let rasterized_height = (rasterized_descent + rasterized_ascent) as u32;
+    let (x_offset, y_offset) = subpixel_point.to_f64();
+
+    // First round out to pixel boundaries
+    // CG Origin is bottom left
+    let mut left = bounds.origin.x.floor() as i32;
+    let mut bottom = bounds.origin.y.floor() as i32;
+    let mut right = (bounds.origin.x
+                    + bounds.size.width
+                    + x_offset).ceil() as i32;
+    let mut top = (bounds.origin.y
+                  + bounds.size.height
+                  + y_offset).ceil() as i32;
 
-    GlyphMetrics {
-        rasterized_ascent: rasterized_ascent,
-        rasterized_descent: rasterized_descent,
-        rasterized_left: rasterized_left,
-        rasterized_width: rasterized_width,
-        rasterized_height: rasterized_height,
-    }
+    // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
+    // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
+    // is not currently known, as CG dilates the outlines by some percentage.
+    // This is taken from Skia.
+    left -= 1;
+    bottom -= 1;
+    right += 1;
+    top += 1;
+
+    let width = right - left;
+    let height = top - bottom;
+
+    let metrics = GlyphMetrics {
+        rasterized_left: left,
+        rasterized_width: width as u32,
+        rasterized_height: height as u32,
+        rasterized_ascent: top,
+        rasterized_descent: -bottom,
+    };
+
+    metrics
 }
 
 impl FontContext {
     pub fn new() -> FontContext {
         debug!("Test for subpixel AA support: {}", supports_subpixel_aa());
 
+        // Force CG to use sRGB color space to gamma correct.
+        let contrast = 0.0;
+        let gamma = 0.0;
+
         FontContext {
             cg_fonts: HashMap::new(),
             ct_fonts: HashMap::new(),
+            gamma_lut: GammaLut::new(contrast, gamma, gamma),
         }
     }
 
     pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: &[u8]) {
         if self.cg_fonts.contains_key(font_key) {
             return
         }
 
@@ -134,62 +162,81 @@ impl FontContext {
                         size.to_f64_px());
                 entry.insert(ct_font.clone());
                 Some(ct_font)
             }
         }
     }
 
     pub fn get_glyph_dimensions(&mut self,
-                                font_key: FontKey,
-                                size: Au,
-                                character: u32) -> Option<GlyphDimensions> {
-        self.get_ct_font(font_key, size).and_then(|ref ct_font| {
-            let glyph = character as CGGlyph;
-            let metrics = get_glyph_metrics(ct_font, glyph);
+                                key: &GlyphKey) -> Option<GlyphDimensions> {
+        self.get_ct_font(key.font_key, key.size).and_then(|ref ct_font| {
+            let glyph = key.index as CGGlyph;
+            let metrics = get_glyph_metrics(ct_font, glyph, &key.subpixel_point);
             if metrics.rasterized_width == 0 || metrics.rasterized_height == 0 {
                 None
             } else {
                 Some(GlyphDimensions {
                     left: metrics.rasterized_left,
                     top: metrics.rasterized_ascent,
                     width: metrics.rasterized_width as u32,
                     height: metrics.rasterized_height as u32,
                 })
             }
         })
     }
 
+    // Assumes the pixels here are linear values from CG
+    fn gamma_correct_pixels(&self, pixels: &mut Vec<u8>, width: usize,
+                            height: usize, render_mode: FontRenderMode,
+                            color: ColorU) {
+        // Then convert back to gamma corrected values.
+        let color_lut = ColorLut::new(color.r,
+                                     color.g,
+                                     color.b,
+                                     color.a);
+        match render_mode {
+            FontRenderMode::Alpha => {
+                self.gamma_lut.preblend_grayscale_bgra(pixels, width,
+                                                       height, color_lut);
+            },
+            FontRenderMode::Subpixel => {
+                self.gamma_lut.preblend_bgra(pixels, width, height, color_lut);
+            },
+            _ => {} // Again, give mono untouched since only the alpha matters.
+        }
+    }
+
     #[allow(dead_code)]
     fn print_glyph_data(&mut self, data: &Vec<u8>, width: usize, height: usize) {
         // Rust doesn't have step_by support on stable :(
+        println!("Width is: {:?} height: {:?}", width, height);
         for i in 0..height {
             let current_height = i * width * 4;
 
             for pixel in data[current_height .. current_height + (width * 4)].chunks(4) {
                 let b = pixel[0];
                 let g = pixel[1];
                 let r = pixel[2];
                 let a = pixel[3];
                 print!("({}, {}, {}, {}) ", r, g, b, a);
             }
             println!("");
         }
     }
 
     pub fn rasterize_glyph(&mut self,
-                           font_key: FontKey,
-                           size: Au,
-                           color: ColorU,
-                           character: u32,
-                           render_mode: FontRenderMode) -> Option<RasterizedGlyph> {
-        match self.get_ct_font(font_key, size) {
+                           key: &GlyphKey,
+                           render_mode: FontRenderMode,
+                           _glyph_options: Option<GlyphOptions>)
+                           -> Option<RasterizedGlyph> {
+        match self.get_ct_font(key.font_key, key.size) {
             Some(ref ct_font) => {
-                let glyph = character as CGGlyph;
-                let metrics = get_glyph_metrics(ct_font, glyph);
+                let glyph = key.index as CGGlyph;
+                let metrics = get_glyph_metrics(ct_font, glyph, &key.subpixel_point);
                 if metrics.rasterized_width == 0 || metrics.rasterized_height == 0 {
                     return Some(RasterizedGlyph::blank())
                 }
 
                 let context_flags = match render_mode {
                     FontRenderMode::Subpixel => kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst,
                     FontRenderMode::Alpha | FontRenderMode::Mono => kCGImageAlphaPremultipliedLast,
                 };
@@ -228,24 +275,31 @@ impl FontContext {
                     FontRenderMode::Alpha => (true, false),
                     FontRenderMode::Mono => (false, false),
                 };
 
                 // These are always true in Gecko, even for non-AA fonts
                 cg_context.set_allows_font_subpixel_positioning(true);
                 cg_context.set_should_subpixel_position_fonts(true);
 
+                // Don't quantize because we're doing it already.
+                cg_context.set_allows_font_subpixel_quantization(false);
+                cg_context.set_should_subpixel_quantize_fonts(false);
+
                 cg_context.set_allows_font_smoothing(smooth);
                 cg_context.set_should_smooth_fonts(smooth);
                 cg_context.set_allows_antialiasing(antialias);
                 cg_context.set_should_antialias(antialias);
 
+                let (x_offset, y_offset) = key.subpixel_point.to_f64();
+
+                // CG Origin is bottom left, WR is top left. Need -y offset
                 let rasterization_origin = CGPoint {
-                    x: -metrics.rasterized_left as f64,
-                    y: metrics.rasterized_descent as f64,
+                    x: -metrics.rasterized_left as f64 + x_offset,
+                    y: metrics.rasterized_descent as f64 - y_offset,
                 };
 
                 // Always draw black text on a white background
                 // Fill the background
                 cg_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0);
                 let rect = CGRect {
                     origin: CGPoint {
                         x: 0.0,
@@ -260,16 +314,24 @@ impl FontContext {
 
                 // Set the text color
                 cg_context.set_rgb_fill_color(0.0, 0.0, 0.0, 1.0);
                 cg_context.set_text_drawing_mode(CGTextDrawingMode::CGTextFill);
                 ct_font.draw_glyphs(&[glyph], &[rasterization_origin], cg_context.clone());
 
                 let mut rasterized_pixels = cg_context.data().to_vec();
 
+                // Convert to linear space for subpixel AA.
+                // We explicitly do not do this for grayscale AA
+                if render_mode == FontRenderMode::Subpixel {
+                    self.gamma_lut.coregraphics_convert_to_linear_bgra(&mut rasterized_pixels,
+                                                                       metrics.rasterized_width as usize,
+                                                                       metrics.rasterized_height as usize);
+                }
+
                 // We need to invert the pixels back since right now
                 // transparent pixels are actually opaque white.
                 for i in 0..metrics.rasterized_height {
                     let current_height = (i * metrics.rasterized_width * 4) as usize;
                     let end_row = current_height + (metrics.rasterized_width as usize * 4);
 
                     for mut pixel in rasterized_pixels[current_height .. end_row].chunks_mut(4) {
                         pixel[0] = 255 - pixel[0];
@@ -282,16 +344,22 @@ impl FontContext {
                                 assert_eq!(pixel[0], pixel[1]);
                                 assert_eq!(pixel[0], pixel[2]);
                                 pixel[0]
                             }
                         }; // end match
                     } // end row
                 } // end height
 
+                self.gamma_correct_pixels(&mut rasterized_pixels,
+                                          metrics.rasterized_width as usize,
+                                          metrics.rasterized_height as usize,
+                                          render_mode,
+                                          key.color);
+
                 Some(RasterizedGlyph {
                     width: metrics.rasterized_width,
                     height: metrics.rasterized_height,
                     bytes: rasterized_pixels,
                 })
             }
             None => {
                 return Some(RasterizedGlyph::blank());
--- a/gfx/webrender/src/platform/unix/font.rs
+++ b/gfx/webrender/src/platform/unix/font.rs
@@ -1,14 +1,16 @@
 /* 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 webrender_traits::{FontKey, ColorU, FontRenderMode, GlyphDimensions, NativeFontHandle};
+use webrender_traits::{FontKey, FontRenderMode, GlyphDimensions};
+use webrender_traits::{NativeFontHandle, GlyphOptions};
+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};
 
@@ -35,17 +37,16 @@ fn float_to_fixed(before: usize, f: f64)
 }
 
 fn float_to_fixed_ft(f: f64) -> i32 {
     float_to_fixed(6, f)
 }
 
 impl FontContext {
     pub fn new() -> FontContext {
-//        let _pf = util::ProfileScope::new("  FontContext::new");
         let mut lib: FT_Library = ptr::null_mut();
         unsafe {
             let result = FT_Init_FreeType(&mut lib);
             if !result.succeeded() {
                 panic!("Unable to initialize FreeType library {:?}", result);
             }
 
             // TODO(gw): Check result of this to determine if freetype build supports subpixel.
@@ -106,46 +107,43 @@ impl FontContext {
                 assert!(!slot_ptr.is_null());
                 return Some(slot_ptr);
             }
         }
 
         None
     }
 
-    pub fn get_glyph_dimensions(&self,
-                                font_key: FontKey,
-                                size: Au,
-                                character: u32) -> Option<GlyphDimensions> {
-        self.load_glyph(font_key, size, character).and_then(|slot| {
+     pub fn get_glyph_dimensions(&mut self,
+                                 key: &GlyphKey) -> Option<GlyphDimensions> {
+        self.load_glyph(key.font_key, key.size, key.index).and_then(|slot| {
             let metrics = unsafe { &(*slot).metrics };
             if metrics.width == 0 || metrics.height == 0 {
                 None
             } else {
                 Some(GlyphDimensions {
                     left: (metrics.horiBearingX >> 6) as i32,
                     top: (metrics.horiBearingY >> 6) as i32,
                     width: (metrics.width >> 6) as u32,
                     height: (metrics.height >> 6) as u32,
                 })
             }
         })
     }
 
     pub fn rasterize_glyph(&mut self,
-                           font_key: FontKey,
-                           size: Au,
-                           color: ColorU,
-                           character: u32,
-                           render_mode: FontRenderMode) -> Option<RasterizedGlyph> {
+                           key: &GlyphKey,
+                           render_mode: FontRenderMode,
+                           _glyph_options: Option<GlyphOptions>)
+                           -> Option<RasterizedGlyph> {
         let mut glyph = None;
 
-        if let Some(slot) = self.load_glyph(font_key,
-                                            size,
-                                            character) {
+        if let Some(slot) = self.load_glyph(key.font_key,
+                                            key.size,
+                                            key.index) {
             let render_mode = match render_mode {
                 FontRenderMode::Mono => FT_Render_Mode::FT_RENDER_MODE_MONO,
                 FontRenderMode::Alpha => FT_Render_Mode::FT_RENDER_MODE_NORMAL,
                 FontRenderMode::Subpixel => FT_Render_Mode::FT_RENDER_MODE_LCD,
             };
 
             let result = unsafe { FT_Render_Glyph(slot, render_mode) };
 
--- a/gfx/webrender/src/platform/windows/font.rs
+++ b/gfx/webrender/src/platform/windows/font.rs
@@ -1,41 +1,118 @@
 /* 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 std::collections::HashMap;
-use webrender_traits::{FontKey, ColorU, FontRenderMode, GlyphDimensions};
+use webrender_traits::{FontKey, FontRenderMode, GlyphDimensions};
+use webrender_traits::{GlyphKey, GlyphOptions};
+use gamma_lut::{GammaLut, Color as ColorLut};
 
 use dwrote;
 
 lazy_static! {
     static ref DEFAULT_FONT_DESCRIPTOR: dwrote::FontDescriptor = dwrote::FontDescriptor {
         family_name: "Arial".to_owned(),
         weight: dwrote::FontWeight::Regular,
         stretch: dwrote::FontStretch::Normal,
         style: dwrote::FontStyle::Normal,
     };
 }
 
 pub struct FontContext {
     fonts: HashMap<FontKey, dwrote::FontFace>,
+    gamma_lut: GammaLut,
+    gdi_gamma_lut: GammaLut,
 }
 
 pub struct RasterizedGlyph {
     pub width: u32,
     pub height: u32,
     pub bytes: Vec<u8>,
 }
 
+fn dwrite_texture_type(render_mode: FontRenderMode) ->
+                       dwrote::DWRITE_TEXTURE_TYPE {
+    match render_mode {
+        FontRenderMode::Mono => dwrote::DWRITE_TEXTURE_ALIASED_1x1 ,
+        FontRenderMode::Alpha |
+        FontRenderMode::Subpixel => dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1,
+    }
+}
+
+fn dwrite_measure_mode(render_mode: FontRenderMode, options: Option<GlyphOptions>) ->
+                       dwrote::DWRITE_MEASURING_MODE {
+    if let Some(GlyphOptions{ force_gdi_rendering: true, .. }) = options {
+        return dwrote::DWRITE_MEASURING_MODE_GDI_CLASSIC;
+    }
+
+    match render_mode {
+        FontRenderMode::Mono => dwrote::DWRITE_MEASURING_MODE_GDI_NATURAL,
+        FontRenderMode::Alpha |
+        FontRenderMode::Subpixel => dwrote::DWRITE_MEASURING_MODE_NATURAL,
+    }
+}
+
+fn dwrite_render_mode(font_face: &dwrote::FontFace,
+                      render_mode: FontRenderMode,
+                      em_size: f32,
+                      measure_mode: dwrote::DWRITE_MEASURING_MODE,
+                      options: Option<GlyphOptions>) ->
+                      dwrote::DWRITE_RENDERING_MODE {
+    if let Some(GlyphOptions{ force_gdi_rendering: true, .. }) = options {
+        return dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC;
+    }
+
+    let dwrite_render_mode = match render_mode {
+        FontRenderMode::Mono => dwrote::DWRITE_RENDERING_MODE_ALIASED,
+        FontRenderMode::Alpha |
+        FontRenderMode::Subpixel => {
+            font_face.get_recommended_rendering_mode_default_params(em_size,
+                                                                    1.0,
+                                                                    measure_mode)
+        },
+    };
+
+    if dwrite_render_mode  == dwrote::DWRITE_RENDERING_MODE_OUTLINE {
+        // Outline mode is not supported
+        return dwrote::DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
+    }
+
+    dwrite_render_mode
+}
+
+fn get_glyph_dimensions_with_analysis(analysis: dwrote::GlyphRunAnalysis,
+                                      texture_type: dwrote::DWRITE_TEXTURE_TYPE)
+                                      -> GlyphDimensions {
+    let bounds = analysis.get_alpha_texture_bounds(texture_type);
+
+    let width = (bounds.right - bounds.left) as u32;
+    let height = (bounds.bottom - bounds.top) as u32;
+    assert!(width > 0 && height > 0);
+    GlyphDimensions {
+        left: bounds.left,
+        top: -bounds.top,
+        width: width,
+        height: height,
+    }
+}
+
 impl FontContext {
     pub fn new() -> FontContext {
+        // These are the default values we use in Gecko.
+        // We use a gamma value of 2.3 for gdi fonts
+        // TODO: Fetch this data from Gecko itself.
+        let contrast = 1.0;
+        let gamma = 1.8;
+        let gdi_gamma = 2.3;
         FontContext {
             fonts: HashMap::new(),
+            gamma_lut: GammaLut::new(contrast, gamma, gamma),
+            gdi_gamma_lut: GammaLut::new(contrast, gdi_gamma, gdi_gamma),
         }
     }
 
     pub fn add_raw_font(&mut self, font_key: &FontKey, data: &[u8]) {
         if self.fonts.contains_key(font_key) {
             return
         }
 
@@ -55,138 +132,165 @@ 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);
     }
 
-    fn get_glyph_dimensions_and_maybe_rasterize(&self,
-                                                font_key: FontKey,
-                                                size: Au,
-                                                glyph: u32,
-                                                render_mode: Option<FontRenderMode>)
-                                                -> (Option<GlyphDimensions>, Option<RasterizedGlyph>)
-    {
-        let face = self.fonts.get(&font_key).unwrap();
-        let glyph = glyph as u16;
+    // 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;
 
-        let glyph = glyph as u16;
+            for pixel in data[current_height .. current_height + (width * 3)].chunks(3) {
+                let r = pixel[0];
+                let g = pixel[1];
+                let b = pixel[2];
+                print!("({}, {}, {}) ", r, g, b, );
+            }
+            println!("");
+        }
+    }
+
+    fn create_glyph_analysis(&self, key: &GlyphKey,
+                            render_mode: FontRenderMode,
+                            options: Option<GlyphOptions>) ->
+                            dwrote::GlyphRunAnalysis {
+        let face = self.fonts.get(&key.font_key).unwrap();
+        let glyph = key.index as u16;
         let advance = 0.0f32;
         let offset = dwrote::GlyphOffset { advanceOffset: 0.0, ascenderOffset: 0.0 };
 
         let glyph_run = dwrote::DWRITE_GLYPH_RUN {
             fontFace: unsafe { face.as_ptr() },
-            fontEmSize: size.to_f32_px(), // size in DIPs (1/96", same as CSS pixels)
+            fontEmSize: key.size.to_f32_px(), // size in DIPs (1/96", same as CSS pixels)
             glyphCount: 1,
             glyphIndices: &glyph,
             glyphAdvances: &advance,
             glyphOffsets: &offset,
             isSideways: 0,
             bidiLevel: 0,
         };
 
-        // dwrite requires DWRITE_RENDERING_MODE_ALIASED if the texture
-        // type is DWRITE_TEXTURE_ALIASED_1x1.  If CLEARTYPE_3x1,
-        // then the other modes can be used.
+        let dwrite_measure_mode = dwrite_measure_mode(render_mode, options);
+        let dwrite_render_mode = dwrite_render_mode(face,
+                                                    render_mode,
+                                                    key.size.to_f32_px(),
+                                                    dwrite_measure_mode,
+                                                    options);
 
-        // TODO(vlad): get_glyph_dimensions needs to take the render mode into account
-        // but the API doesn't give it to us right now.  Just assume subpixel.
-        let (r_mode, m_mode, tex_type) = match render_mode {
-            Some(FontRenderMode::Mono) => (dwrote::DWRITE_RENDERING_MODE_ALIASED,
-                                           dwrote::DWRITE_MEASURING_MODE_GDI_NATURAL,
-                                           dwrote::DWRITE_TEXTURE_ALIASED_1x1),
-            Some(FontRenderMode::Alpha) => (dwrote::DWRITE_RENDERING_MODE_GDI_NATURAL,
-                                            dwrote::DWRITE_MEASURING_MODE_GDI_NATURAL,
-                                            dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1),
-            Some(FontRenderMode::Subpixel) | None => (dwrote::DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL,
-                                                      dwrote::DWRITE_MEASURING_MODE_GDI_NATURAL,
-                                                      dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1),
-        };
+        let (x_offset, y_offset) = key.subpixel_point.to_f64();
+        let transform = Some(
+                        dwrote::DWRITE_MATRIX { m11: 1.0, m12: 0.0, m21: 0.0, m22: 1.0,
+                                                dx: x_offset as f32, dy: y_offset as f32 }
+                        );
 
-        // XX use the xform to handle subpixel positioning (what skia does), I believe that keeps
-        //let xform = dwrote::DWRITE_MATRIX { m11: 1.0, m12: 0.0, m21: 0.0, m22: 1.0, dx: 0.0, dy: 0.0 };
-        let analysis = dwrote::GlyphRunAnalysis::create(&glyph_run, 1.0, None, r_mode, m_mode, 0.0, 0.0);
-        let bounds = analysis.get_alpha_texture_bounds(tex_type);
+        dwrote::GlyphRunAnalysis::create(&glyph_run, 1.0, transform,
+                                         dwrite_render_mode,
+                                         dwrite_measure_mode,
+                                         0.0, 0.0)
+    }
 
-        let width = (bounds.right - bounds.left) as u32;
-        let height = (bounds.bottom - bounds.top) as u32;
-        let dims = GlyphDimensions {
-            left: bounds.left,
-            top: -bounds.top,
-            width: width,
-            height: height,
-        };
-
-        // if empty, then nothing
-        if dims.width == 0 || dims.height == 0 {
-            return (None, None);
-        }
+    // TODO: Pipe GlyphOptions into glyph_dimensions too
+    pub fn get_glyph_dimensions(&self,
+                                key: &GlyphKey)
+                                -> Option<GlyphDimensions> {
+        // Probably have to default to something else here.
+        let render_mode = FontRenderMode::Subpixel;
+        let analysis = self.create_glyph_analysis(key, render_mode, None);
 
-        // if we weren't asked to rasterize, we're done
-        if render_mode.is_none() {
-            return (Some(dims), None);
-        }
+        let texture_type = dwrite_texture_type(render_mode);
+        Some(get_glyph_dimensions_with_analysis(analysis, texture_type))
+    }
 
-        let pixels = analysis.create_alpha_texture(tex_type, bounds);
-        let rgba_pixels = match render_mode.unwrap() {
+    // DWRITE gives us values in RGB. WR doesn't really touch it after. Note, CG returns in BGR
+    // TODO: Decide whether all fonts should return RGB or BGR
+    fn convert_to_rgba(&self, pixels: &Vec<u8>, render_mode: FontRenderMode) -> Vec<u8> {
+        match render_mode {
             FontRenderMode::Mono => {
-                let mut rgba_pixels = vec![0; pixels.len() * 4];
+                let mut rgba_pixels: Vec<u8> = vec![0; pixels.len() * 4];
                 for i in 0..pixels.len() {
-                    rgba_pixels[i*4+0] = 0xff;
-                    rgba_pixels[i*4+1] = 0xff;
-                    rgba_pixels[i*4+2] = 0xff;
+                    rgba_pixels[i*4+0] = pixels[i];
+                    rgba_pixels[i*4+1] = pixels[i];
+                    rgba_pixels[i*4+2] = pixels[i];
                     rgba_pixels[i*4+3] = pixels[i];
                 }
                 rgba_pixels
             }
             FontRenderMode::Alpha => {
-                let mut rgba_pixels = vec![0; pixels.len()/3 * 4];
-                for i in 0..pixels.len()/3 {
+                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
-                    let alpha = (pixels[i*3+0] as u32 + pixels[i*3+0] as u32 + pixels[i*3+0] as u32) / 3;
-                    rgba_pixels[i*4+0] = 0xff;
-                    rgba_pixels[i*4+1] = 0xff;
-                    rgba_pixels[i*4+2] = 0xff;
-                    rgba_pixels[i*4+3] = alpha as u8;
+                    // 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;
+
+                    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 => {
-                let mut rgba_pixels = vec![0; pixels.len()/3 * 4];
-                for i in 0..pixels.len()/3 {
+                let length = pixels.len() / 3;
+                let mut rgba_pixels: Vec<u8> = vec![0; length * 4];
+                for i in 0..length {
                     rgba_pixels[i*4+0] = pixels[i*3+0];
                     rgba_pixels[i*4+1] = pixels[i*3+1];
                     rgba_pixels[i*4+2] = pixels[i*3+2];
                     rgba_pixels[i*4+3] = 0xff;
                 }
                 rgba_pixels
             }
-        };
-
-        (Some(dims), Some(RasterizedGlyph {
-            width: dims.width,
-            height: dims.height,
-            bytes: rgba_pixels,
-        }))
-    }
-
-    pub fn get_glyph_dimensions(&self,
-                                font_key: FontKey,
-                                size: Au,
-                                glyph: u32) -> Option<GlyphDimensions> {
-        let (maybe_dims, _) =
-            self.get_glyph_dimensions_and_maybe_rasterize(font_key, size, glyph, None);
-        maybe_dims
+        }
     }
 
     pub fn rasterize_glyph(&mut self,
-                           font_key: FontKey,
-                           size: Au,
-                           color: ColorU,
-                           glyph: u32,
-                           render_mode: FontRenderMode) -> Option<RasterizedGlyph> {
-        let (_, maybe_glyph) =
-            self.get_glyph_dimensions_and_maybe_rasterize(font_key, size, glyph, Some(render_mode));
-        maybe_glyph
+                           key: &GlyphKey,
+                           render_mode: FontRenderMode,
+                           glyph_options: Option<GlyphOptions>)
+                           -> Option<RasterizedGlyph> {
+        let analysis = self.create_glyph_analysis(key,
+                                                  render_mode,
+                                                  glyph_options);
+        let texture_type = dwrite_texture_type(render_mode);
+
+        let bounds = analysis.get_alpha_texture_bounds(texture_type);
+        let width = (bounds.right - bounds.left) as usize;
+        let height = (bounds.bottom - bounds.top) as usize;
+
+        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
+        };
+
+        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
@@ -1,29 +1,31 @@
 /* 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::Size2D;
-use gpu_store::{GpuStore, GpuStoreAddress};
-use internal_types::SourceTexture;
+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 std::mem;
 use std::usize;
-use tiling::{RenderTask, RenderTaskLocation};
 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};
 use webrender_traits::{LayerRect, LayerSize, LayerPoint};
-use webrender_traits::LayerToWorldTransform;
+use webrender_traits::{LayerToWorldTransform, GlyphInstance, GlyphOptions};
+use webrender_traits::{ExtendMode, GradientStop};
 
 pub const CLIP_DATA_GPU_SIZE: usize = 5;
 pub const MASK_DATA_GPU_SIZE: usize = 1;
 
 /// Stores two coordinates in texel space. The coordinates
 /// are stored in texel coordinates because the texture atlas
 /// may grow. Storing them as texel coords and normalizing
 /// the UVs in the vertex shader means nothing needs to be
@@ -70,17 +72,18 @@ pub struct PrimitiveIndex(pub usize);
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
 pub enum PrimitiveKind {
     Rectangle,
     TextRun,
     Image,
     YuvImage,
     Border,
-    Gradient,
+    AlignedGradient,
+    AngleGradient,
     RadialGradient,
     BoxShadow,
 }
 
 /// Geometry description for simple rectangular primitives, uploaded to the GPU.
 #[derive(Debug, Clone)]
 pub struct PrimitiveGeometry {
     pub local_rect: LayerRect,
@@ -211,65 +214,166 @@ pub struct BoxShadowPrimitiveGpu {
     pub bs_rect: LayerRect,
     pub color: ColorF,
     pub border_radius: f32,
     pub edge_size: f32,
     pub blur_radius: f32,
     pub inverted: f32,
 }
 
-#[repr(u32)]
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub enum GradientType {
-    Horizontal,
-    Vertical,
-    Rotated,
-}
-
 #[derive(Debug, Clone)]
 #[repr(C)]
-pub struct GradientStop {
+pub struct GradientStopGpu {
     color: ColorF,
     offset: f32,
     padding: [f32; 3],
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 pub struct GradientPrimitiveGpu {
     pub start_point: LayerPoint,
     pub end_point: LayerPoint,
-    pub kind: f32,
+    pub extend_mode: f32,
     pub padding: [f32; 3],
 }
 
 #[derive(Debug)]
 pub struct GradientPrimitiveCpu {
     pub stops_range: ItemRange,
-    pub kind: GradientType,
+    pub extend_mode: ExtendMode,
     pub reverse_stops: bool,
     pub cache_dirty: bool,
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 pub struct RadialGradientPrimitiveGpu {
     pub start_center: LayerPoint,
     pub end_center: LayerPoint,
     pub start_radius: f32,
     pub end_radius: f32,
-    pub padding: [f32; 2],
+    pub extend_mode: f32,
+    pub padding: [f32; 1],
 }
 
 #[derive(Debug)]
 pub struct RadialGradientPrimitiveCpu {
     pub stops_range: ItemRange,
+    pub extend_mode: ExtendMode,
     pub cache_dirty: bool,
 }
 
+// The number of entries in a gradient data table.
+pub const GRADIENT_DATA_RESOLUTION: usize = 128;
+
+#[derive(Debug, Clone, Copy)]
+#[repr(C)]
+// An entry in a gradient data table representing a segment of the gradient color space.
+pub struct GradientDataEntry {
+    pub start_color: PackedTexel,
+    pub end_color: PackedTexel,
+}
+
+#[repr(C)]
+// A table of gradient entries, with two colors per entry, that specify the start and end color
+// within the segment of the gradient space represented by that entry. To lookup a gradient result,
+// first the entry index is calculated to determine which two colors to interpolate between, then
+// the offset within that entry bucket is used to interpolate between the two colors in that entry.
+// This layout preserves hard stops, as the end color for a given entry can differ from the start
+// color for the following entry, despite them being adjacent. Colors are stored within in BGRA8
+// format for texture upload.
+pub struct GradientData {
+    pub colors: [GradientDataEntry; GRADIENT_DATA_RESOLUTION],
+}
+
+impl Default for GradientData {
+    fn default() -> GradientData {
+        GradientData {
+            colors: unsafe { mem::uninitialized() }
+        }
+    }
+}
+
+impl Clone for GradientData {
+    fn clone(&self) -> GradientData {
+        GradientData {
+            colors: self.colors,
+        }
+    }
+}
+
+impl GradientData {
+    // Generate a color ramp between the start and end indexes from a start color to an end color.
+    fn fill_colors(&mut self, start_idx: usize, end_idx: usize, start_color: &ColorF, end_color: &ColorF) -> usize {
+        if start_idx >= end_idx {
+            return start_idx;
+        }
+
+        // Calculate the color difference for individual steps in the ramp.
+        let inv_steps = 1.0 / (end_idx - start_idx) as f32;
+        let step_r = (end_color.r - start_color.r) * inv_steps;
+        let step_g = (end_color.g - start_color.g) * inv_steps;
+        let step_b = (end_color.b - start_color.b) * inv_steps;
+        let step_a = (end_color.a - start_color.a) * inv_steps;
+
+        let mut cur_color = *start_color;
+        let mut cur_packed_color = PackedTexel::from_color(&cur_color);
+
+        // Walk the ramp writing start and end colors for each entry.
+        for entry in &mut self.colors[start_idx..end_idx] {
+            entry.start_color = cur_packed_color;
+
+            cur_color.r += step_r;
+            cur_color.g += step_g;
+            cur_color.b += step_b;
+            cur_color.a += step_a;
+            cur_packed_color = PackedTexel::from_color(&cur_color);
+            entry.end_color = cur_packed_color;
+        }
+
+        end_idx
+    }
+
+    // Compute an entry index based on a gradient stop offset.
+    #[inline]
+    fn get_index(offset: f32) -> usize {
+        (offset.max(0.0).min(1.0) * GRADIENT_DATA_RESOLUTION as f32).round() as usize
+    }
+
+    // Build the gradient data from the supplied stops, reversing them if necessary.
+    fn build(&mut self, src_stops: &[GradientStop], reverse_stops: bool) {
+        let mut cur_idx = 0usize;
+        let mut cur_color = if let Some(src) = src_stops.first() {
+            src.color
+        } else {
+            ColorF::new(0.0, 0.0, 0.0, 0.0)
+        };
+
+        if reverse_stops {
+            // If the gradient is reversed, then ensure the stops are processed in reverse order
+            // and that the offsets are inverted.
+            for src in src_stops.iter().rev() {
+                cur_idx = self.fill_colors(cur_idx, Self::get_index(1.0 - src.offset),
+                                           &cur_color, &src.color);
+                cur_color = src.color;
+            }
+        } else {
+            for src in src_stops {
+                cur_idx = self.fill_colors(cur_idx, Self::get_index(src.offset),
+                                           &cur_color, &src.color);
+                cur_color = src.color;
+            }
+        }
+
+        // Fill out any remaining entries in the gradient.
+        self.fill_colors(cur_idx, GRADIENT_DATA_RESOLUTION, &cur_color, &cur_color);
+    }
+}
+
 #[derive(Debug, Clone)]
 #[repr(C)]
 struct InstanceRect {
     rect: LayerRect,
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
@@ -280,21 +384,22 @@ pub struct TextRunPrimitiveGpu {
 #[derive(Debug, Clone)]
 pub struct TextRunPrimitiveCpu {
     pub font_key: FontKey,
     pub logical_font_size: Au,
     pub blur_radius: Au,
     pub glyph_range: ItemRange,
     pub cache_dirty: bool,
     // TODO(gw): Maybe make this an Arc for sharing with resource cache
-    pub glyph_indices: Vec<u32>,
+    pub glyph_instances: Vec<GlyphInstance>,
     pub color_texture_id: SourceTexture,
     pub color: ColorF,
     pub render_mode: FontRenderMode,
     pub resource_address: GpuStoreAddress,
+    pub glyph_options: Option<GlyphOptions>,
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 struct GlyphPrimitive {
     offset: LayerPoint,
     padding: LayerPoint,
 }
@@ -423,63 +528,66 @@ impl ClipData {
 
 #[derive(Debug)]
 pub enum PrimitiveContainer {
     Rectangle(RectanglePrimitive),
     TextRun(TextRunPrimitiveCpu, TextRunPrimitiveGpu),
     Image(ImagePrimitiveCpu, ImagePrimitiveGpu),
     YuvImage(YuvImagePrimitiveCpu, YuvImagePrimitiveGpu),
     Border(BorderPrimitiveCpu, BorderPrimitiveGpu),
-    Gradient(GradientPrimitiveCpu, GradientPrimitiveGpu),
+    AlignedGradient(GradientPrimitiveCpu, GradientPrimitiveGpu),
+    AngleGradient(GradientPrimitiveCpu, GradientPrimitiveGpu),
     RadialGradient(RadialGradientPrimitiveCpu, RadialGradientPrimitiveGpu),
     BoxShadow(BoxShadowPrimitiveGpu, Vec<LayerRect>),
 }
 
 pub struct PrimitiveStore {
     // CPU side information only