Bug 1374730 - Update webrender to cset 519e51986308fc11d6ba6771f1c11ea6a3133921. r?jrmuizel draft
authorKartikaya Gupta <kgupta@mozilla.com>
Mon, 10 Jul 2017 07:19:51 -0400
changeset 606125 07be72f3fbd9024038f45d06dc1860dcd4097dc9
parent 606124 91c943f7373722ad4e122d98a2ddd6c79708b732
child 606126 ee0efd4a024dbfebe96a19436bea87189dcb2af7
push id67608
push userkgupta@mozilla.com
push dateMon, 10 Jul 2017 11:23:08 +0000
reviewersjrmuizel
bugs1374730
milestone56.0a1
Bug 1374730 - Update webrender to cset 519e51986308fc11d6ba6771f1c11ea6a3133921. r?jrmuizel MozReview-Commit-ID: 81YYW87APLn
gfx/doc/README.webrender
gfx/webrender/Cargo.toml
gfx/webrender/benches/coalesce.rs
gfx/webrender/examples/animation.rs
gfx/webrender/examples/basic.rs
gfx/webrender/examples/blob.rs
gfx/webrender/examples/common/boilerplate.rs
gfx/webrender/examples/nested_display_list.rs
gfx/webrender/examples/scrolling.rs
gfx/webrender/examples/yuv.rs
gfx/webrender/res/cs_clip_border.vs.glsl
gfx/webrender/res/cs_clip_image.vs.glsl
gfx/webrender/res/cs_clip_rectangle.vs.glsl
gfx/webrender/res/cs_text_run.vs.glsl
gfx/webrender/res/prim_shared.glsl
gfx/webrender/src/border.rs
gfx/webrender/src/clip_scroll_node.rs
gfx/webrender/src/clip_scroll_tree.rs
gfx/webrender/src/debug_colors.rs
gfx/webrender/src/debug_render.rs
gfx/webrender/src/device.rs
gfx/webrender/src/ellipse.rs
gfx/webrender/src/frame.rs
gfx/webrender/src/frame_builder.rs
gfx/webrender/src/glyph_rasterizer.rs
gfx/webrender/src/gpu_cache.rs
gfx/webrender/src/gpu_store.rs
gfx/webrender/src/internal_types.rs
gfx/webrender/src/lib.rs
gfx/webrender/src/mask_cache.rs
gfx/webrender/src/platform/macos/font.rs
gfx/webrender/src/platform/unix/font.rs
gfx/webrender/src/platform/windows/font.rs
gfx/webrender/src/prim_store.rs
gfx/webrender/src/profiler.rs
gfx/webrender/src/record.rs
gfx/webrender/src/render_backend.rs
gfx/webrender/src/render_task.rs
gfx/webrender/src/renderer.rs
gfx/webrender/src/resource_cache.rs
gfx/webrender/src/scene.rs
gfx/webrender/src/spring.rs
gfx/webrender/src/texture_cache.rs
gfx/webrender/src/tiling.rs
gfx/webrender/src/util.rs
gfx/webrender/src/webgl_stubs.rs
gfx/webrender/src/webgl_types.rs
gfx/webrender_api/Cargo.toml
gfx/webrender_api/src/api.rs
gfx/webrender_api/src/channel.rs
gfx/webrender_api/src/channel_ipc.rs
gfx/webrender_api/src/channel_mpsc.rs
gfx/webrender_api/src/color.rs
gfx/webrender_api/src/display_item.rs
gfx/webrender_api/src/display_list.rs
gfx/webrender_api/src/font.rs
gfx/webrender_api/src/image.rs
gfx/webrender_api/src/lib.rs
gfx/webrender_api/src/units.rs
gfx/webrender_api/src/webgl.rs
gfx/webrender_traits/Cargo.toml
gfx/webrender_traits/src/api.rs
gfx/webrender_traits/src/channel.rs
gfx/webrender_traits/src/channel_ipc.rs
gfx/webrender_traits/src/channel_mpsc.rs
gfx/webrender_traits/src/color.rs
gfx/webrender_traits/src/display_item.rs
gfx/webrender_traits/src/display_list.rs
gfx/webrender_traits/src/font.rs
gfx/webrender_traits/src/image.rs
gfx/webrender_traits/src/lib.rs
gfx/webrender_traits/src/units.rs
gfx/webrender_traits/src/webgl.rs
--- 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: 1d6348023a4a4fdd89dce038640c5da906005acc
+Latest Commit: 519e51986308fc11d6ba6771f1c11ea6a3133921
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -1,38 +1,38 @@
 [package]
 name = "webrender"
-version = "0.43.0"
+version = "0.47.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 build = "build.rs"
 
 [features]
 default = ["freetype-lib", "webgl"]
 freetype-lib = ["freetype/servo-freetype-sys"]
 profiler = ["thread_profiler/thread_profiler"]
-webgl = ["offscreen_gl_context", "webrender_traits/webgl"]
+webgl = ["offscreen_gl_context", "webrender_api/webgl"]
 
 [dependencies]
 app_units = "0.5"
 bincode = "0.8"
 bit-set = "0.4"
 byteorder = "1.0"
-euclid = "0.15"
+euclid = "0.15.1"
 fnv = "1.0"
-gleam = "0.4.3"
+gleam = "0.4.7"
 lazy_static = "0.2"
 log = "0.3"
 num-traits = "0.1.32"
 offscreen_gl_context = {version = "0.11", features = ["serde", "osmesa"], optional = true}
 time = "0.1"
 rayon = "0.8"
-webrender_traits = {path = "../webrender_traits"}
-bitflags = "0.7"
+webrender_api = {path = "../webrender_api"}
+bitflags = "0.9"
 gamma-lut = "0.2"
 thread_profiler = "0.1.1"
 plane-split = "0.6"
 
 [dev-dependencies]
 angle = {git = "https://github.com/servo/angle", branch = "servo"}
 rand = "0.3"                # for the benchmarks
 servo-glutin = "0.11"     # for the example apps
@@ -40,9 +40,9 @@ servo-glutin = "0.11"     # for the exam
 [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.4"
 
 [target.'cfg(target_os = "macos")'.dependencies]
 core-graphics = "0.8.0"
-core-text = { version = "5.0.1", features = ["lion"] }
+core-text = { version = "6.1", default-features = false }
--- a/gfx/webrender/benches/coalesce.rs
+++ b/gfx/webrender/benches/coalesce.rs
@@ -1,19 +1,18 @@
 #![feature(test)]
 
 extern crate rand;
 extern crate test;
 extern crate webrender;
-extern crate webrender_traits;
 
 use rand::Rng;
 use test::Bencher;
 use webrender::TexturePage;
-use webrender_traits::{DeviceUintSize as Size};
+use webrender::api::{DeviceUintSize as Size};
 
 #[bench]
 fn bench_coalesce(b: &mut Bencher) {
     let mut rng = rand::thread_rng();
     let mut page = TexturePage::new_dummy(Size::new(10000, 10000));
     let mut test_page = TexturePage::new_dummy(Size::new(10000, 10000));
     while page.allocate(&Size::new(rng.gen_range(1, 100), rng.gen_range(1, 100))).is_some() {}
     b.iter(|| {
--- a/gfx/webrender/examples/animation.rs
+++ b/gfx/webrender/examples/animation.rs
@@ -1,52 +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/. */
 
 extern crate gleam;
 extern crate glutin;
 extern crate webrender;
-extern crate webrender_traits;
 
 #[macro_use]
 extern crate lazy_static;
 
 #[path="common/boilerplate.rs"]
 mod boilerplate;
 
 use boilerplate::HandyDandyRectBuilder;
 use std::sync::Mutex;
-use webrender_traits::*;
+use webrender::api::*;
 
 // This example creates a 100x100 white rect and allows the user to move it
 // around by using the arrow keys. It does this by using the animation API.
 
 fn body(_api: &RenderApi,
         builder: &mut DisplayListBuilder,
         _pipeline_id: &PipelineId,
-        _layout_size: &LayoutSize)
-{
+        _layout_size: &LayoutSize) {
     // Create a 100x100 stacking context with an animatable transform property.
     // Note the magic "42" we use as the animation key. That is used to update
     // the transform in the keyboard event handler code.
     let bounds = (0,0).to(100, 100);
-    builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
+    builder.push_stacking_context(ScrollPolicy::Scrollable,
                                   bounds,
                                   Some(PropertyBinding::Binding(PropertyBindingKey::new(42))),
                                   TransformStyle::Flat,
                                   None,
-                                  webrender_traits::MixBlendMode::Normal,
+                                  MixBlendMode::Normal,
                                   Vec::new());
 
     // Fill it with a white rect
-    let clip = builder.push_clip_region(&bounds, vec![], None);
-    builder.push_rect(bounds,
-                      clip,
-                      ColorF::new(1.0, 1.0, 1.0, 1.0));
+    builder.push_rect(bounds, None, ColorF::new(1.0, 1.0, 1.0, 1.0));
 
     builder.pop_stacking_context();
 }
 
 lazy_static! {
     static ref TRANSFORM: Mutex<LayoutTransform> = Mutex::new(LayoutTransform::identity());
 }
 
--- a/gfx/webrender/examples/basic.rs
+++ b/gfx/webrender/examples/basic.rs
@@ -2,31 +2,32 @@
  * 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/. */
 
 extern crate app_units;
 extern crate euclid;
 extern crate gleam;
 extern crate glutin;
 extern crate webrender;
-extern crate webrender_traits;
+
+#[macro_use]
+extern crate lazy_static;
+
+#[path="common/boilerplate.rs"]
+mod boilerplate;
 
 use app_units::Au;
-use gleam::gl;
+use boilerplate::HandyDandyRectBuilder;
+use euclid::vec2;
 use glutin::TouchPhase;
 use std::collections::HashMap;
-use std::env;
 use std::fs::File;
 use std::io::Read;
-use std::path::PathBuf;
-use webrender_traits::{ClipRegionToken, ColorF, DisplayListBuilder, Epoch, GlyphInstance};
-use webrender_traits::{DeviceIntPoint, DeviceUintSize, LayoutPoint, LayoutRect, LayoutSize};
-use webrender_traits::{ImageData, ImageDescriptor, ImageFormat};
-use webrender_traits::{PipelineId, RenderApi, TransformStyle, BoxShadowClipMode};
-use euclid::vec2;
+use std::sync::Mutex;
+use webrender::api::*;
 
 #[derive(Debug)]
 enum Gesture {
     None,
     Pan,
     Zoom,
 }
 
@@ -163,164 +164,80 @@ impl TouchState {
 
 fn load_file(name: &str) -> Vec<u8> {
     let mut file = File::open(name).unwrap();
     let mut buffer = vec![];
     file.read_to_end(&mut buffer).unwrap();
     buffer
 }
 
-struct Notifier {
-    window_proxy: glutin::WindowProxy,
-}
-
-impl Notifier {
-    fn new(window_proxy: glutin::WindowProxy) -> Notifier {
-        Notifier {
-            window_proxy: window_proxy,
-        }
-    }
-}
-
-impl webrender_traits::RenderNotifier for Notifier {
-    fn new_frame_ready(&mut self) {
-        #[cfg(not(target_os = "android"))]
-        self.window_proxy.wakeup_event_loop();
-    }
-
-    fn new_scroll_frame_ready(&mut self, _composite_needed: bool) {
-        #[cfg(not(target_os = "android"))]
-        self.window_proxy.wakeup_event_loop();
-    }
-}
-
-fn push_sub_clip(api: &RenderApi, builder: &mut DisplayListBuilder, bounds: &LayoutRect)
-                 -> ClipRegionToken {
-    let mask_image = api.generate_image_key();
-    api.add_image(mask_image,
-                  ImageDescriptor::new(2, 2, ImageFormat::A8, true),
-                  ImageData::new(vec![0, 80, 180, 255]),
-                  None);
-    let mask = webrender_traits::ImageMask {
-        image: mask_image,
-        rect: LayoutRect::new(LayoutPoint::new(75.0, 75.0), LayoutSize::new(100.0, 100.0)),
-        repeat: false,
-    };
-    let complex = webrender_traits::ComplexClipRegion::new(
-        LayoutRect::new(LayoutPoint::new(50.0, 50.0), LayoutSize::new(100.0, 100.0)),
-        webrender_traits::BorderRadius::uniform(20.0));
-
-    builder.push_clip_region(bounds, vec![complex], Some(mask))
+fn main() {
+    boilerplate::main_wrapper(body, event_handler, None);
 }
 
-
-fn main() {
-    let args: Vec<String> = env::args().collect();
-    let res_path = if args.len() > 1 {
-        Some(PathBuf::from(&args[1]))
-    } else {
-        None
-    };
-
-    let window = glutin::WindowBuilder::new()
-                .with_title("WebRender Sample")
-                .with_multitouch()
-                .with_gl(glutin::GlRequest::GlThenGles {
-                    opengl_version: (3, 2),
-                    opengles_version: (3, 0)
-                })
-                .build()
-                .unwrap();
-
-    unsafe {
-        window.make_current().ok();
-    }
-
-    let gl = match gl::GlType::default() {
-        gl::GlType::Gl => unsafe { gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
-        gl::GlType::Gles => unsafe { gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
-    };
-
-    println!("OpenGL version {}", gl.get_string(gl::VERSION));
-    println!("Shader resource path: {:?}", res_path);
-
-    let (width, height) = window.get_inner_size_pixels().unwrap();
-
-    let opts = webrender::RendererOptions {
-        resource_override_path: res_path,
-        debug: true,
-        precache_shaders: true,
-        device_pixel_ratio: window.hidpi_factor(),
-        .. Default::default()
-    };
-
-    let size = DeviceUintSize::new(width, height);
-    let (mut renderer, sender) = webrender::renderer::Renderer::new(gl, opts, size).unwrap();
-    let api = sender.create_api();
-
-    let notifier = Box::new(Notifier::new(window.create_window_proxy()));
-    renderer.set_render_notifier(notifier);
-
-    let epoch = Epoch(0);
-    let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0);
-
-    let pipeline_id = PipelineId(0, 0);
-    let layout_size = LayoutSize::new(width as f32, height as f32);
-    let mut builder = webrender_traits::DisplayListBuilder::new(pipeline_id, layout_size);
-
-    let bounds = LayoutRect::new(LayoutPoint::zero(), layout_size);
-    builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
+fn body(api: &RenderApi,
+        builder: &mut DisplayListBuilder,
+        _pipeline_id: &PipelineId,
+        layout_size: &LayoutSize) {
+    let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
+    builder.push_stacking_context(ScrollPolicy::Scrollable,
                                   bounds,
                                   None,
                                   TransformStyle::Flat,
                                   None,
-                                  webrender_traits::MixBlendMode::Normal,
+                                  MixBlendMode::Normal,
                                   Vec::new());
 
-    let clip = push_sub_clip(&api, &mut builder, &bounds);
-    builder.push_rect(LayoutRect::new(LayoutPoint::new(100.0, 100.0), LayoutSize::new(100.0, 100.0)),
-                      clip,
-                      ColorF::new(0.0, 1.0, 0.0, 1.0));
+    let image_mask_key = api.generate_image_key();
+    api.add_image(image_mask_key,
+                  ImageDescriptor::new(2, 2, ImageFormat::A8, true),
+                  ImageData::new(vec![0, 80, 180, 255]),
+                  None);
+    let mask = ImageMask {
+        image: image_mask_key,
+        rect: (75, 75).by(100, 100),
+        repeat: false,
+    };
+    let complex = ComplexClipRegion::new((50, 50).to(150, 150), BorderRadius::uniform(20.0));
+    let id = builder.define_clip(None, bounds, vec![complex], Some(mask));
+    builder.push_clip_id(id);
 
-    let clip = push_sub_clip(&api, &mut builder, &bounds);
-    builder.push_rect(LayoutRect::new(LayoutPoint::new(250.0, 100.0), LayoutSize::new(100.0, 100.0)),
-                      clip,
-                      ColorF::new(0.0, 1.0, 0.0, 1.0));
-    let border_side = webrender_traits::BorderSide {
+    let bounds = (100, 100).to(200, 200);
+    builder.push_rect(bounds, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
+
+    let bounds = (250, 100).to(350, 200);
+    builder.push_rect(bounds, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
+    let border_side = BorderSide {
         color: ColorF::new(0.0, 0.0, 1.0, 1.0),
-        style: webrender_traits::BorderStyle::Groove,
+        style: BorderStyle::Groove,
     };
-    let border_widths = webrender_traits::BorderWidths {
+    let border_widths = BorderWidths {
         top: 10.0,
         left: 10.0,
         bottom: 10.0,
         right: 10.0,
     };
-    let border_details = webrender_traits::BorderDetails::Normal(webrender_traits::NormalBorder {
+    let border_details = BorderDetails::Normal(NormalBorder {
         top: border_side,
         right: border_side,
         bottom: border_side,
         left: border_side,
-        radius: webrender_traits::BorderRadius::uniform(20.0),
+        radius: BorderRadius::uniform(20.0),
     });
 
-    let clip = push_sub_clip(&api, &mut builder, &bounds);
-    builder.push_border(LayoutRect::new(LayoutPoint::new(100.0, 100.0), LayoutSize::new(100.0, 100.0)),
-                        clip,
-                        border_widths,
-                        border_details);
+    let bounds = (100, 100).to(200, 200);
+    builder.push_border(bounds, None, border_widths, border_details);
 
 
     if false { // draw text?
         let font_key = api.generate_font_key();
         let font_bytes = load_file("res/FreeSans.ttf");
         api.add_raw_font(font_key, font_bytes, 0);
 
-        let text_bounds = LayoutRect::new(LayoutPoint::new(100.0, 200.0), LayoutSize::new(700.0, 300.0));
-
+        let text_bounds = (100, 200).by(700, 300);
         let glyphs = vec![
             GlyphInstance {
                 index: 48,
                 point: LayoutPoint::new(100.0, 100.0),
             },
             GlyphInstance {
                 index: 68,
                 point: LayoutPoint::new(150.0, 100.0),
@@ -362,96 +279,65 @@ fn main() {
                 point: LayoutPoint::new(600.0, 100.0),
             },
             GlyphInstance {
                 index: 17,
                 point: LayoutPoint::new(650.0, 100.0),
             },
         ];
 
-        let clip = builder.push_clip_region(&bounds, Vec::new(), None);
         builder.push_text(text_bounds,
-                          clip,
+                          None,
                           &glyphs,
                           font_key,
                           ColorF::new(1.0, 1.0, 0.0, 1.0),
                           Au::from_px(32),
                           0.0,
                           None);
     }
 
     if false { // draw box shadow?
-        let rect = LayoutRect::new(LayoutPoint::new(0.0, 0.0), LayoutSize::new(0.0, 0.0));
-        let simple_box_bounds = LayoutRect::new(LayoutPoint::new(20.0, 200.0),
-                                                LayoutSize::new(50.0, 50.0));
+        let rect = LayoutRect::zero();
+        let simple_box_bounds = (20, 200).by(50, 50);
         let offset = vec2(10.0, 10.0);
         let color = ColorF::new(1.0, 1.0, 1.0, 1.0);
         let blur_radius = 0.0;
         let spread_radius = 0.0;
         let simple_border_radius = 8.0;
         let box_shadow_type = BoxShadowClipMode::Inset;
-        let full_screen_clip = builder.push_clip_region(&bounds, Vec::new(), None);
 
         builder.push_box_shadow(rect,
-                                full_screen_clip,
+                                Some(LocalClip::from(bounds)),
                                 simple_box_bounds,
                                 offset,
                                 color,
                                 blur_radius,
                                 spread_radius,
                                 simple_border_radius,
                                 box_shadow_type);
     }
 
+    builder.pop_clip_id();
     builder.pop_stacking_context();
-
-    api.set_display_list(
-        Some(root_background_color),
-        epoch,
-        LayoutSize::new(width as f32, height as f32),
-        builder.finalize(),
-        true);
-    api.set_root_pipeline(pipeline_id);
-    api.generate_frame(None);
-
-    let mut touch_state = TouchState::new();
+}
 
-    'outer: for event in window.wait_events() {
-        let mut events = Vec::new();
-        events.push(event);
-
-        for event in window.poll_events() {
-            events.push(event);
-        }
+lazy_static! {
+    static ref TOUCH_STATE: Mutex<TouchState> = Mutex::new(TouchState::new());
+}
 
-        for event in events {
-            match event {
-                glutin::Event::Closed |
-                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
-                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Q)) => break 'outer,
-                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
-                                             _, Some(glutin::VirtualKeyCode::P)) => {
-                    let enable_profiler = !renderer.get_profiler_enabled();
-                    renderer.set_profiler_enabled(enable_profiler);
+fn event_handler(event: &glutin::Event, api: &RenderApi) {
+    match *event {
+        glutin::Event::Touch(touch) => {
+            match TOUCH_STATE.lock().unwrap().handle_event(touch) {
+                TouchResult::Pan(pan) => {
+                    api.set_pan(pan);
                     api.generate_frame(None);
                 }
-                glutin::Event::Touch(touch) => {
-                    match touch_state.handle_event(touch) {
-                        TouchResult::Pan(pan) => {
-                            api.set_pan(pan);
-                            api.generate_frame(None);
-                        }
-                        TouchResult::Zoom(zoom) => {
-                            api.set_pinch_zoom(webrender_traits::ZoomFactor::new(zoom));
-                            api.generate_frame(None);
-                        }
-                        TouchResult::None => {}
-                    }
+                TouchResult::Zoom(zoom) => {
+                    api.set_pinch_zoom(ZoomFactor::new(zoom));
+                    api.generate_frame(None);
                 }
-                _ => ()
+                TouchResult::None => {}
             }
         }
-
-        renderer.update();
-        renderer.render(DeviceUintSize::new(width, height));
-        window.swap_buffers().ok();
+        _ => ()
     }
 }
--- a/gfx/webrender/examples/blob.rs
+++ b/gfx/webrender/examples/blob.rs
@@ -2,60 +2,59 @@
  * 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/. */
 
 extern crate app_units;
 extern crate euclid;
 extern crate gleam;
 extern crate glutin;
 extern crate webrender;
-extern crate webrender_traits;
 extern crate rayon;
 
 #[path="common/boilerplate.rs"]
 mod boilerplate;
 
 use boilerplate::HandyDandyRectBuilder;
 use rayon::ThreadPool;
 use rayon::Configuration as ThreadPoolConfig;
 use std::collections::HashMap;
 use std::collections::hash_map::Entry;
 use std::sync::Arc;
 use std::sync::mpsc::{channel, Sender, Receiver};
-use webrender_traits as wt;
+use webrender::api;
 
 // This example shows how to implement a very basic BlobImageRenderer that can only render
 // a checkerboard pattern.
 
 // The deserialized command list internally used by this example is just a color.
-type ImageRenderingCommands = wt::ColorU;
+type ImageRenderingCommands = api::ColorU;
 
 // Serialize/deserialze the blob.
 // Ror real usecases you should probably use serde rather than doing it by hand.
 
-fn serialize_blob(color: wt::ColorU) -> Vec<u8> {
+fn serialize_blob(color: api::ColorU) -> Vec<u8> {
     vec![color.r, color.g, color.b, color.a]
 }
 
 fn deserialize_blob(blob: &[u8]) -> Result<ImageRenderingCommands, ()> {
     let mut iter = blob.iter();
     return match (iter.next(), iter.next(), iter.next(), iter.next()) {
-        (Some(&r), Some(&g), Some(&b), Some(&a)) => Ok(wt::ColorU::new(r, g, b, a)),
-        (Some(&a), None, None, None) => Ok(wt::ColorU::new(a, a, a, a)),
+        (Some(&r), Some(&g), Some(&b), Some(&a)) => Ok(api::ColorU::new(r, g, b, a)),
+        (Some(&a), None, None, None) => Ok(api::ColorU::new(a, a, a, a)),
         _ => Err(()),
     }
 }
 
 // This is the function that applies the deserialized drawing commands and generates
 // actual image data.
 fn render_blob(
     commands: Arc<ImageRenderingCommands>,
-    descriptor: &wt::BlobImageDescriptor,
-    tile: Option<wt::TileOffset>,
-) -> wt::BlobImageResult {
+   descriptor: &api::BlobImageDescriptor,
+   tile: Option<api::TileOffset>
+) -> api::BlobImageResult {
     let color = *commands;
 
     // Allocate storage for the result. Right now the resource cache expects the
     // tiles to have have no stride or offset.
     let mut texels = Vec::with_capacity((descriptor.width * descriptor.height * 4) as usize);
 
     // Generate a per-tile pattern to see it in the demo. For a real use case it would not
     // make sense for the rendered content to depend on its tile.
@@ -72,97 +71,97 @@ fn render_blob(
             let y2 = y + descriptor.offset.y as u32;
 
             // Render a simple checkerboard pattern
             let checker = if (x2 % 20 >= 10) != (y2 % 20 >= 10) { 1 } else { 0 };
             // ..nested in the per-tile cherkerboard pattern
             let tc = if tile_checker { 0 } else { (1 - checker) * 40 };
 
             match descriptor.format {
-                wt::ImageFormat::BGRA8 => {
+                api::ImageFormat::BGRA8 => {
                     texels.push(color.b * checker + tc);
                     texels.push(color.g * checker + tc);
                     texels.push(color.r * checker + tc);
                     texels.push(color.a * checker + tc);
                 }
-                wt::ImageFormat::A8 => {
+                api::ImageFormat::A8 => {
                     texels.push(color.a * checker + tc);
                 }
                 _ => {
-                    return Err(wt::BlobImageError::Other(format!(
+                    return Err(api::BlobImageError::Other(format!(
                         "Usupported image format {:?}",
                         descriptor.format
                     )));
                 }
             }
         }
     }
 
-    Ok(wt::RasterizedBlobImage {
+    Ok(api::RasterizedBlobImage {
         data: texels,
         width: descriptor.width,
         height: descriptor.height,
     })
 }
 
 struct CheckerboardRenderer {
     // We are going to defer the rendering work to worker threads.
     // Using a pre-built Arc<ThreadPool> rather than creating our own threads
     // makes it possible to share the same thread pool as the glyph renderer (if we
     // want to).
     workers: Arc<ThreadPool>,
 
     // the workers will use an mpsc channel to communicate the result.
-    tx: Sender<(wt::BlobImageRequest, wt::BlobImageResult)>,
-    rx: Receiver<(wt::BlobImageRequest, wt::BlobImageResult)>,
+    tx: Sender<(api::BlobImageRequest, api::BlobImageResult)>,
+    rx: Receiver<(api::BlobImageRequest, api::BlobImageResult)>,
 
     // The deserialized drawing commands.
     // In this example we store them in Arcs. This isn't necessary since in this simplified
     // case the command list is a simple 32 bits value and would be cheap to clone before sending
     // to the workers. But in a more realistic scenario the commands would typically be bigger
     // and more expensive to clone, so let's pretend it is also the case here.
-    image_cmds: HashMap<wt::ImageKey, Arc<ImageRenderingCommands>>,
+    image_cmds: HashMap<api::ImageKey, Arc<ImageRenderingCommands>>,
 
     // The images rendered in the current frame (not kept here between frames).
-    rendered_images: HashMap<wt::BlobImageRequest, Option<wt::BlobImageResult>>,
+    rendered_images: HashMap<api::BlobImageRequest, Option<api::BlobImageResult>>,
 }
 
 impl CheckerboardRenderer {
     fn new(workers: Arc<ThreadPool>) -> Self {
         let (tx, rx) = channel();
         CheckerboardRenderer {
             image_cmds: HashMap::new(),
             rendered_images: HashMap::new(),
-            workers: workers,
-            tx: tx,
-            rx: rx,
+            workers,
+            tx,
+            rx,
         }
     }
 }
 
-impl wt::BlobImageRenderer for CheckerboardRenderer {
-    fn add(&mut self, key: wt::ImageKey, cmds: wt::BlobImageData, _: Option<wt::TileSize>) {
+impl api::BlobImageRenderer for CheckerboardRenderer {
+    fn add(&mut self, key: api::ImageKey, cmds: api::BlobImageData, _: Option<api::TileSize>) {
         self.image_cmds.insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
     }
 
-    fn update(&mut self, key: wt::ImageKey, cmds: wt::BlobImageData) {
+    fn update(&mut self, key: api::ImageKey, cmds: api::BlobImageData) {
         // Here, updating is just replacing the current version of the commands with
         // the new one (no incremental updates).
         self.image_cmds.insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
     }
 
-    fn delete(&mut self, key: wt::ImageKey) {
+    fn delete(&mut self, key: api::ImageKey) {
         self.image_cmds.remove(&key);
     }
 
     fn request(&mut self,
-               resources: &wt::BlobImageResources,
-               request: wt::BlobImageRequest,
-               descriptor: &wt::BlobImageDescriptor,
-               _dirty_rect: Option<wt::DeviceUintRect>) {
+               _resources: &api::BlobImageResources,
+               request: api::BlobImageRequest,
+               descriptor: &api::BlobImageDescriptor,
+               _dirty_rect: Option<api::DeviceUintRect>) {
         // This method is where we kick off our rendering jobs.
         // It should avoid doing work on the calling thread as much as possible.
         // In this example we will use the thread pool to render individual tiles.
 
         // Gather the input data to send to a worker thread.
         let cmds = Arc::clone(&self.image_cmds.get(&request.key).unwrap());
         let tx = self.tx.clone();
         let descriptor = descriptor.clone();
@@ -174,25 +173,25 @@ impl wt::BlobImageRenderer for Checkerbo
 
         // Add None in the map of rendered images. This makes it possible to differentiate
         // between commands that aren't finished yet (entry in the map is equal to None) and
         // keys that have never been requested (entry not in the map), which would cause deadlocks
         // if we were to block upon receing their result in resolve!
         self.rendered_images.insert(request, None);
     }
 
-    fn resolve(&mut self, request: wt::BlobImageRequest) -> wt::BlobImageResult {
+    fn resolve(&mut self, request: api::BlobImageRequest) -> api::BlobImageResult {
         // In this method we wait until the work is complete on the worker threads and
         // gather the results.
 
         // First look at whether we have already received the rendered image
         // that we are looking for.
         match self.rendered_images.entry(request) {
             Entry::Vacant(_) => {
-                return Err(wt::BlobImageError::InvalidKey);
+                return Err(api::BlobImageError::InvalidKey);
             }
             Entry::Occupied(entry) => {
                 // None means we haven't yet received the result.
                 if entry.get().is_some() {
                     let result = entry.remove();
                     return result.unwrap();
                 }
             }
@@ -203,76 +202,73 @@ impl wt::BlobImageRenderer for Checkerbo
             if req == request {
                 // There it is!
                 return result
             }
             self.rendered_images.insert(req, Some(result));
         }
 
         // If we break out of the loop above it means the channel closed unexpectedly.
-        Err(wt::BlobImageError::Other("Channel closed".into()))
+        Err(api::BlobImageError::Other("Channel closed".into()))
     }
-    fn delete_font(&mut self, font: wt::FontKey) {}
+    fn delete_font(&mut self, _font: api::FontKey) { }
 }
 
-fn body(api: &wt::RenderApi,
-        builder: &mut wt::DisplayListBuilder,
-        _pipeline_id: &wt::PipelineId,
-        layout_size: &wt::LayoutSize)
-{
+fn body(api: &api::RenderApi,
+        builder: &mut api::DisplayListBuilder,
+        _pipeline_id: &api::PipelineId,
+        layout_size: &api::LayoutSize) {
     let blob_img1 = api.generate_image_key();
     api.add_image(
         blob_img1,
-        wt::ImageDescriptor::new(500, 500, wt::ImageFormat::BGRA8, true),
-        wt::ImageData::new_blob_image(serialize_blob(wt::ColorU::new(50, 50, 150, 255))),
+        api::ImageDescriptor::new(500, 500, api::ImageFormat::BGRA8, true),
+        api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 50, 150, 255))),
         Some(128),
     );
 
     let blob_img2 = api.generate_image_key();
     api.add_image(
         blob_img2,
-        wt::ImageDescriptor::new(200, 200, wt::ImageFormat::BGRA8, true),
-        wt::ImageData::new_blob_image(serialize_blob(wt::ColorU::new(50, 150, 50, 255))),
+        api::ImageDescriptor::new(200, 200, api::ImageFormat::BGRA8, true),
+        api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 150, 50, 255))),
         None,
     );
 
-    let bounds = wt::LayoutRect::new(wt::LayoutPoint::zero(), *layout_size);
-    builder.push_stacking_context(wt::ScrollPolicy::Scrollable,
+    let bounds = api::LayoutRect::new(api::LayoutPoint::zero(), *layout_size);
+    builder.push_stacking_context(api::ScrollPolicy::Scrollable,
                                   bounds,
                                   None,
-                                  wt::TransformStyle::Flat,
+                                  api::TransformStyle::Flat,
                                   None,
-                                  wt::MixBlendMode::Normal,
+                                  api::MixBlendMode::Normal,
                                   Vec::new());
 
-    let clip = builder.push_clip_region(&bounds, vec![], None);
     builder.push_image(
         (30, 30).by(500, 500),
-        clip,
-        wt::LayoutSize::new(500.0, 500.0),
-        wt::LayoutSize::new(0.0, 0.0),
-        wt::ImageRendering::Auto,
+        Some(api::LocalClip::from(bounds)),
+        api::LayoutSize::new(500.0, 500.0),
+        api::LayoutSize::new(0.0, 0.0),
+        api::ImageRendering::Auto,
         blob_img1,
     );
 
-    let clip = builder.push_clip_region(&bounds, vec![], None);
     builder.push_image(
         (600, 600).by(200, 200),
-        clip,
-        wt::LayoutSize::new(200.0, 200.0),
-        wt::LayoutSize::new(0.0, 0.0),
-        wt::ImageRendering::Auto,
+        Some(api::LocalClip::from(bounds)),
+        api::LayoutSize::new(200.0, 200.0),
+        api::LayoutSize::new(0.0, 0.0),
+        api::ImageRendering::Auto,
         blob_img2,
     );
 
     builder.pop_stacking_context();
 }
 
 fn event_handler(_event: &glutin::Event,
-                 _api: &wt::RenderApi)
+                 _api: &api::RenderApi)
 {
 }
 
 fn main() {
     let worker_config = ThreadPoolConfig::new().thread_name(|idx|{
         format!("WebRender:Worker#{}", idx)
     });
 
--- a/gfx/webrender/examples/common/boilerplate.rs
+++ b/gfx/webrender/examples/common/boilerplate.rs
@@ -2,26 +2,26 @@
  * 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 gleam::gl;
 use glutin;
 use std::env;
 use std::path::PathBuf;
 use webrender;
-use webrender_traits::*;
+use webrender::api::*;
 
 struct Notifier {
     window_proxy: glutin::WindowProxy,
 }
 
 impl Notifier {
     fn new(window_proxy: glutin::WindowProxy) -> Notifier {
         Notifier {
-            window_proxy: window_proxy,
+            window_proxy,
         }
     }
 }
 
 impl RenderNotifier for Notifier {
     fn new_frame_ready(&mut self) {
         #[cfg(not(target_os = "android"))]
         self.window_proxy.wakeup_event_loop();
--- a/gfx/webrender/examples/nested_display_list.rs
+++ b/gfx/webrender/examples/nested_display_list.rs
@@ -1,82 +1,81 @@
 /* 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/. */
 
 extern crate gleam;
 extern crate glutin;
 extern crate webrender;
-extern crate webrender_traits;
 
 #[macro_use]
 extern crate lazy_static;
 
 #[path="common/boilerplate.rs"]
 mod boilerplate;
 
 use boilerplate::HandyDandyRectBuilder;
 use std::sync::Mutex;
-use webrender_traits::*;
+use webrender::api::*;
 
 fn body(_api: &RenderApi,
         builder: &mut DisplayListBuilder,
         pipeline_id: &PipelineId,
-        layout_size: &LayoutSize)
-{
+        layout_size: &LayoutSize) {
     let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
-    builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
+    builder.push_stacking_context(ScrollPolicy::Scrollable,
                                   bounds,
                                   None,
                                   TransformStyle::Flat,
                                   None,
-                                  webrender_traits::MixBlendMode::Normal,
+                                  MixBlendMode::Normal,
                                   Vec::new());
 
     let outer_scroll_frame_rect = (100, 100).to(600, 400);
-    let token = builder.push_clip_region(&outer_scroll_frame_rect, vec![], None);
-    builder.push_rect(outer_scroll_frame_rect,
-                      token, ColorF::new(1.0, 1.0, 1.0, 1.0));
-    let token = builder.push_clip_region(&outer_scroll_frame_rect, vec![], None);
-    let nested_clip_id = builder.define_clip((100, 100).to(1000, 1000), token, None);
+    builder.push_rect(outer_scroll_frame_rect, None, ColorF::new(1.0, 1.0, 1.0, 1.0));
+
+    let nested_clip_id = builder.define_scroll_frame(None,
+                                                     (100, 100).to(1000, 1000),
+                                                     outer_scroll_frame_rect,
+                                                     vec![],
+                                                     None);
     builder.push_clip_id(nested_clip_id);
 
-    let mut builder2 = webrender_traits::DisplayListBuilder::new(*pipeline_id, *layout_size);
-    let mut builder3 = webrender_traits::DisplayListBuilder::new(*pipeline_id, *layout_size);
+    let mut builder2 = DisplayListBuilder::new(*pipeline_id, *layout_size);
+    let mut builder3 = DisplayListBuilder::new(*pipeline_id, *layout_size);
 
     let rect = (110, 110).to(210, 210);
-    let token = builder3.push_clip_region(&rect, vec![], None);
-    builder3.push_rect(rect, token, ColorF::new(0.0, 1.0, 0.0, 1.0));
+    builder3.push_rect(rect, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
 
     // A fixed position rectangle should be fixed to the reference frame that starts
     // in the outer display list.
-    builder3.push_stacking_context(webrender_traits::ScrollPolicy::Fixed,
+    builder3.push_stacking_context(ScrollPolicy::Fixed,
                                   (220, 110).to(320, 210),
                                   None,
                                   TransformStyle::Flat,
                                   None,
-                                  webrender_traits::MixBlendMode::Normal,
+                                  MixBlendMode::Normal,
                                   Vec::new());
     let rect = (0, 0).to(100, 100);
-    let token = builder3.push_clip_region(&rect, vec![], None);
-    builder3.push_rect(rect, token, ColorF::new(0.0, 1.0, 0.0, 1.0));
+    builder3.push_rect(rect, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
     builder3.pop_stacking_context();
 
     // Now we push an inner scroll frame that should have the same id as the outer one,
     // but the WebRender nested display list replacement code should convert it into
     // a unique ClipId.
     let inner_scroll_frame_rect = (330, 110).to(530, 360);
-    let token = builder3.push_clip_region(&inner_scroll_frame_rect, vec![], None);
-    builder3.push_rect(inner_scroll_frame_rect, token, ColorF::new(1.0, 0.0, 1.0, 0.5));
-    let token = builder3.push_clip_region(&inner_scroll_frame_rect, vec![], None);
-    let inner_nested_clip_id = builder3.define_clip((330, 110).to(2000, 2000), token, None);
+    builder3.push_rect(inner_scroll_frame_rect, None, ColorF::new(1.0, 0.0, 1.0, 0.5));
+    let inner_nested_clip_id = builder3.define_scroll_frame(None,
+                                                            (330, 110).to(2000, 2000),
+                                                            inner_scroll_frame_rect,
+                                                            vec![],
+                                                            None);
     builder3.push_clip_id(inner_nested_clip_id);
     let rect = (340, 120).to(440, 220);
-    let token = builder3.push_clip_region(&rect, vec![], None);
-    builder3.push_rect(rect, token, ColorF::new(0.0, 1.0, 0.0, 1.0));
+    builder3.push_rect(rect, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
     builder3.pop_clip_id();
 
     let (_, _, built_list) = builder3.finalize();
     builder2.push_nested_display_list(&built_list);
     let (_, _, built_list) = builder2.finalize();
     builder.push_nested_display_list(&built_list);
 
     builder.pop_clip_id();
--- a/gfx/webrender/examples/scrolling.rs
+++ b/gfx/webrender/examples/scrolling.rs
@@ -1,119 +1,108 @@
 /* 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/. */
 
 extern crate gleam;
 extern crate glutin;
 extern crate webrender;
-extern crate webrender_traits;
 
 #[macro_use]
 extern crate lazy_static;
 
 #[path="common/boilerplate.rs"]
 mod boilerplate;
 
 use boilerplate::HandyDandyRectBuilder;
 use std::sync::Mutex;
-use webrender_traits::*;
+use webrender::api::*;
 
 fn body(_api: &RenderApi,
         builder: &mut DisplayListBuilder,
-        pipeline_id: &PipelineId,
-        layout_size: &LayoutSize)
-{
+        _pipeline_id: &PipelineId,
+        layout_size: &LayoutSize) {
     let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
-    builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
+    builder.push_stacking_context(ScrollPolicy::Scrollable,
                                   bounds,
                                   None,
                                   TransformStyle::Flat,
                                   None,
-                                  webrender_traits::MixBlendMode::Normal,
+                                  MixBlendMode::Normal,
                                   Vec::new());
 
     if true {   // scrolling and clips stuff
         // let's make a scrollbox
         let scrollbox = (0, 0).to(300, 400);
-        builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
+        builder.push_stacking_context(ScrollPolicy::Scrollable,
                                       LayoutRect::new(LayoutPoint::new(10.0, 10.0),
                                                       LayoutSize::zero()),
                                       None,
                                       TransformStyle::Flat,
                                       None,
-                                      webrender_traits::MixBlendMode::Normal,
+                                      MixBlendMode::Normal,
                                       Vec::new());
         // set the scrolling clip
-        let clip = builder.push_clip_region(&scrollbox, vec![], None);
-        let clip_id = builder.define_clip((0, 0).to(1000, 1000),
-                                          clip,
-                                          Some(ClipId::new(42, *pipeline_id)));
+        let clip_id = builder.define_scroll_frame(None,
+                                                  (0, 0).by(1000, 1000),
+                                                  scrollbox,
+                                                  vec![],
+                                                  None);
         builder.push_clip_id(clip_id);
+
         // now put some content into it.
         // start with a white background
-        let clip = builder.push_clip_region(&(0, 0).to(1000, 1000), vec![], None);
-        builder.push_rect((0, 0).to(500, 500),
-                          clip,
-                          ColorF::new(1.0, 1.0, 1.0, 1.0));
+        builder.push_rect((0, 0).to(1000, 1000), None, ColorF::new(1.0, 1.0, 1.0, 1.0));
+
         // let's make a 50x50 blue square as a visual reference
-        let clip = builder.push_clip_region(&(0, 0).to(50, 50), vec![], None);
-        builder.push_rect((0, 0).to(50, 50),
-                          clip,
-                          ColorF::new(0.0, 0.0, 1.0, 1.0));
+        builder.push_rect((0, 0).to(50, 50), None, ColorF::new(0.0, 0.0, 1.0, 1.0));
+
         // and a 50x50 green square next to it with an offset clip
         // to see what that looks like
-        let clip = builder.push_clip_region(&(60, 10).to(110, 60), vec![], None);
         builder.push_rect((50, 0).to(100, 50),
-                          clip,
+                          Some(LocalClip::from((60, 10).to(110, 60))),
                           ColorF::new(0.0, 1.0, 0.0, 1.0));
 
         // Below the above rectangles, set up a nested scrollbox. It's still in
         // the same stacking context, so note that the rects passed in need to
         // be relative to the stacking context.
-        let clip = builder.push_clip_region(&(0, 100).to(200, 300), vec![], None);
-        let nested_clip_id = builder.define_clip((0, 100).to(300, 400),
-                                                 clip,
-                                                 Some(ClipId::new(43, *pipeline_id)));
+        let nested_clip_id = builder.define_scroll_frame(None,
+                                                         (0, 100).to(300, 400),
+                                                         (0, 100).to(200, 300),
+                                                         vec![],
+                                                         None);
         builder.push_clip_id(nested_clip_id);
+
         // give it a giant gray background just to distinguish it and to easily
         // visually identify the nested scrollbox
-        let clip = builder.push_clip_region(&(-1000, -1000).to(5000, 5000), vec![], None);
-        builder.push_rect((-1000, -1000).to(5000, 5000),
-                          clip,
-                          ColorF::new(0.5, 0.5, 0.5, 1.0));
+        builder.push_rect((-1000, -1000).to(5000, 5000), None, ColorF::new(0.5, 0.5, 0.5, 1.0));
+
         // add a teal square to visualize the scrolling/clipping behaviour
         // as you scroll the nested scrollbox with WASD keys
-        let clip = builder.push_clip_region(&(0, 100).to(50, 150), vec![], None);
-        builder.push_rect((0, 100).to(50, 150),
-                          clip,
-                          ColorF::new(0.0, 1.0, 1.0, 1.0));
+        builder.push_rect((0, 100).to(50, 150), None, ColorF::new(0.0, 1.0, 1.0, 1.0));
+
         // just for good measure add another teal square in the bottom-right
         // corner of the nested scrollframe content, which can be scrolled into
         // view by the user
-        let clip = builder.push_clip_region(&(250, 350).to(300, 400), vec![], None);
-        builder.push_rect((250, 350).to(300, 400),
-                          clip,
-                          ColorF::new(0.0, 1.0, 1.0, 1.0));
+        builder.push_rect((250, 350).to(300, 400), None, ColorF::new(0.0, 1.0, 1.0, 1.0));
+
         builder.pop_clip_id(); // nested_clip_id
 
         builder.pop_clip_id(); // clip_id
         builder.pop_stacking_context();
     }
 
     builder.pop_stacking_context();
 }
 
 lazy_static! {
     static ref CURSOR_POSITION: Mutex<WorldPoint> = Mutex::new(WorldPoint::zero());
 }
 
-fn event_handler(event: &glutin::Event,
-                 api: &RenderApi)
-{
+fn event_handler(event: &glutin::Event, api: &RenderApi) {
     match *event {
         glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
             let offset = match key {
                  glutin::VirtualKeyCode::Down => (0.0, -10.0),
                  glutin::VirtualKeyCode::Up => (0.0, 10.0),
                  glutin::VirtualKeyCode::Right => (-10.0, 0.0),
                  glutin::VirtualKeyCode::Left => (10.0, 0.0),
                  _ => return,
--- a/gfx/webrender/examples/yuv.rs
+++ b/gfx/webrender/examples/yuv.rs
@@ -2,28 +2,27 @@
  * 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/. */
 
 extern crate app_units;
 extern crate euclid;
 extern crate gleam;
 extern crate glutin;
 extern crate webrender;
-extern crate webrender_traits;
+
+#[macro_use]
+extern crate lazy_static;
 
-use gleam::gl;
+#[path="common/boilerplate.rs"]
+mod boilerplate;
+
 use glutin::TouchPhase;
 use std::collections::HashMap;
-use std::env;
-use std::path::PathBuf;
-use webrender_traits::{ColorF, Epoch};
-use webrender_traits::{DeviceIntPoint, DeviceUintSize, LayoutPoint, LayoutRect, LayoutSize};
-use webrender_traits::{ImageData, ImageDescriptor, ImageFormat, ImageRendering};
-use webrender_traits::{PipelineId, TransformStyle};
-use webrender_traits::{YuvColorSpace, YuvData};
+use std::sync::Mutex;
+use webrender::api::*;
 
 #[derive(Debug)]
 enum Gesture {
     None,
     Pan,
     Zoom,
 }
 
@@ -153,102 +152,31 @@ impl TouchState {
                 self.current_gesture = Gesture::None;
             }
         }
 
         TouchResult::None
     }
 }
 
-struct Notifier {
-    window_proxy: glutin::WindowProxy,
-}
-
-impl Notifier {
-    fn new(window_proxy: glutin::WindowProxy) -> Notifier {
-        Notifier {
-            window_proxy: window_proxy,
-        }
-    }
-}
-
-impl webrender_traits::RenderNotifier for Notifier {
-    fn new_frame_ready(&mut self) {
-        #[cfg(not(target_os = "android"))]
-        self.window_proxy.wakeup_event_loop();
-    }
-
-    fn new_scroll_frame_ready(&mut self, _composite_needed: bool) {
-        #[cfg(not(target_os = "android"))]
-        self.window_proxy.wakeup_event_loop();
-    }
+fn main() {
+    boilerplate::main_wrapper(body, event_handler, None);
 }
 
-fn main() {
-    let args: Vec<String> = env::args().collect();
-    let res_path = if args.len() > 1 {
-        Some(PathBuf::from(&args[1]))
-    } else {
-        None
-    };
-
-    let window = glutin::WindowBuilder::new()
-                .with_title("WebRender Sample")
-                .with_multitouch()
-                .with_gl(glutin::GlRequest::GlThenGles {
-                    opengl_version: (3, 2),
-                    opengles_version: (3, 0)
-                })
-                .build()
-                .unwrap();
-
-    unsafe {
-        window.make_current().ok();
-    }
-
-    let gl = match gl::GlType::default() {
-        gl::GlType::Gl => unsafe { gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
-        gl::GlType::Gles => unsafe { gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
-    };
-
-    println!("OpenGL version {}", gl.get_string(gl::VERSION));
-    println!("Shader resource path: {:?}", res_path);
-
-    let (width, height) = window.get_inner_size_pixels().unwrap();
-
-    let opts = webrender::RendererOptions {
-        resource_override_path: res_path,
-        debug: true,
-        precache_shaders: true,
-        blob_image_renderer: None,
-        device_pixel_ratio: window.hidpi_factor(),
-        .. Default::default()
-    };
-
-    let size = DeviceUintSize::new(width, height);
-    let (mut renderer, sender) = webrender::renderer::Renderer::new(gl, opts, size).unwrap();
-    let api = sender.create_api();
-
-    let notifier = Box::new(Notifier::new(window.create_window_proxy()));
-    renderer.set_render_notifier(notifier);
-
-    let epoch = Epoch(0);
-    let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0);
-
-    let pipeline_id = PipelineId(0, 0);
-    let layout_size = LayoutSize::new(width as f32, height as f32);
-    let mut builder = webrender_traits::DisplayListBuilder::new(pipeline_id, layout_size);
-
-    let bounds = LayoutRect::new(LayoutPoint::zero(), layout_size);
-    builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
+fn body(api: &RenderApi,
+        builder: &mut DisplayListBuilder,
+        _pipeline_id: &PipelineId,
+        layout_size: &LayoutSize) {
+    let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
+    builder.push_stacking_context(ScrollPolicy::Scrollable,
                                   bounds,
                                   None,
                                   TransformStyle::Flat,
                                   None,
-                                  webrender_traits::MixBlendMode::Normal,
+                                  MixBlendMode::Normal,
                                   Vec::new());
 
 
     let yuv_chanel1 = api.generate_image_key();
     let yuv_chanel2 = api.generate_image_key();
     let yuv_chanel2_1 = api.generate_image_key();
     let yuv_chanel3 = api.generate_image_key();
     api.add_image(
@@ -271,81 +199,50 @@ fn main() {
     );
     api.add_image(
         yuv_chanel3,
         ImageDescriptor::new(100, 100, ImageFormat::A8, true),
         ImageData::new(vec![127; 100 * 100]),
         None,
     );
 
-    let clip = builder.push_clip_region(&bounds, vec![], None);
     builder.push_yuv_image(
         LayoutRect::new(LayoutPoint::new(100.0, 0.0), LayoutSize::new(100.0, 100.0)),
-        clip,
+        Some(LocalClip::from(bounds)),
         YuvData::NV12(yuv_chanel1, yuv_chanel2),
         YuvColorSpace::Rec601,
         ImageRendering::Auto,
     );
 
-    let clip = builder.push_clip_region(&bounds, vec![], None);
     builder.push_yuv_image(
         LayoutRect::new(LayoutPoint::new(300.0, 0.0), LayoutSize::new(100.0, 100.0)),
-        clip,
+        Some(LocalClip::from(bounds)),
         YuvData::PlanarYCbCr(yuv_chanel1, yuv_chanel2_1, yuv_chanel3),
         YuvColorSpace::Rec601,
         ImageRendering::Auto,
     );
 
     builder.pop_stacking_context();
+}
 
-    api.set_display_list(
-        Some(root_background_color),
-        epoch,
-        LayoutSize::new(width as f32, height as f32),
-        builder.finalize(),
-        true);
-    api.set_root_pipeline(pipeline_id);
-    api.generate_frame(None);
+lazy_static! {
+    static ref TOUCH_STATE: Mutex<TouchState> = Mutex::new(TouchState::new());
+}
 
-    let mut touch_state = TouchState::new();
 
-    'outer: for event in window.wait_events() {
-        let mut events = Vec::new();
-        events.push(event);
-
-        for event in window.poll_events() {
-            events.push(event);
-        }
-
-        for event in events {
-            match event {
-                glutin::Event::Closed |
-                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
-                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Q)) => break 'outer,
-                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
-                                             _, Some(glutin::VirtualKeyCode::P)) => {
-                    let enable_profiler = !renderer.get_profiler_enabled();
-                    renderer.set_profiler_enabled(enable_profiler);
+fn event_handler(event: &glutin::Event, api: &RenderApi) {
+    match *event {
+        glutin::Event::Touch(touch) => {
+            match TOUCH_STATE.lock().unwrap().handle_event(touch) {
+                TouchResult::Pan(pan) => {
+                    api.set_pan(pan);
                     api.generate_frame(None);
                 }
-                glutin::Event::Touch(touch) => {
-                    match touch_state.handle_event(touch) {
-                        TouchResult::Pan(pan) => {
-                            api.set_pan(pan);
-                            api.generate_frame(None);
-                        }
-                        TouchResult::Zoom(zoom) => {
-                            api.set_pinch_zoom(webrender_traits::ZoomFactor::new(zoom));
-                            api.generate_frame(None);
-                        }
-                        TouchResult::None => {}
-                    }
+                TouchResult::Zoom(zoom) => {
+                    api.set_pinch_zoom(ZoomFactor::new(zoom));
+                    api.generate_frame(None);
                 }
-                _ => ()
+                TouchResult::None => {}
             }
         }
-
-        renderer.update();
-        renderer.render(DeviceUintSize::new(width, height));
-        window.swap_buffers().ok();
+        _ => ()
     }
 }
-
--- a/gfx/webrender/res/cs_clip_border.vs.glsl
+++ b/gfx/webrender/res/cs_clip_border.vs.glsl
@@ -17,42 +17,42 @@
 struct BorderCorner {
     RectWithSize rect;
     vec2 clip_center;
     int corner;
     int clip_mode;
 };
 
 BorderCorner fetch_border_corner(int index) {
-    vec4 data[2] = fetch_data_2(index);
+    vec4 data[2] = fetch_from_resource_cache_2(index);
     return BorderCorner(RectWithSize(data[0].xy, data[0].zw),
                         data[1].xy,
                         int(data[1].z),
                         int(data[1].w));
 }
 
 // Per-dash clip information.
 struct BorderClipDash {
     vec4 point_tangent_0;
     vec4 point_tangent_1;
 };
 
 BorderClipDash fetch_border_clip_dash(int index) {
-    vec4 data[2] = fetch_data_2(index);
+    vec4 data[2] = fetch_from_resource_cache_2(index);
     return BorderClipDash(data[0], data[1]);
 }
 
 // Per-dot clip information.
 struct BorderClipDot {
     vec3 center_radius;
 };
 
 BorderClipDot fetch_border_clip_dot(int index) {
-    vec4 data[2] = fetch_data_2(index);
-    return BorderClipDot(data[0].xyz);
+    vec4 data = fetch_from_resource_cache_1(index);
+    return BorderClipDot(data.xyz);
 }
 
 void main(void) {
     CacheClipInstance cci = fetch_clip_item(gl_InstanceID);
     ClipArea area = fetch_clip_area(cci.render_task_index);
     Layer layer = fetch_layer(cci.layer_index);
 
     // Fetch the header information for this corner clip.
@@ -80,25 +80,25 @@ void main(void) {
             case CORNER_BOTTOM_LEFT:
                 sign_modifier = vec2(-1.0, 1.0);
                 break;
         };
 
         switch (corner.clip_mode) {
             case CLIP_MODE_DASH: {
                 // Fetch the information about this particular dash.
-                BorderClipDash dash = fetch_border_clip_dash(cci.data_index + cci.segment_index);
+                BorderClipDash dash = fetch_border_clip_dash(cci.data_index + 2 + 2 * (cci.segment_index - 1));
                 vPoint_Tangent0 = dash.point_tangent_0 * sign_modifier.xyxy;
                 vPoint_Tangent1 = dash.point_tangent_1 * sign_modifier.xyxy;
                 vDotParams = vec3(0.0);
                 vAlphaMask = vec2(0.0, 1.0);
                 break;
             }
             case CLIP_MODE_DOT: {
-                BorderClipDot cdot = fetch_border_clip_dot(cci.data_index + cci.segment_index);
+                BorderClipDot cdot = fetch_border_clip_dot(cci.data_index + 2 + (cci.segment_index - 1));
                 vPoint_Tangent0 = vec4(1.0);
                 vPoint_Tangent1 = vec4(1.0);
                 vDotParams = vec3(cdot.center_radius.xy * sign_modifier, cdot.center_radius.z);
                 vAlphaMask = vec2(1.0, 1.0);
                 break;
             }
         }
     }
--- a/gfx/webrender/res/cs_clip_image.vs.glsl
+++ b/gfx/webrender/res/cs_clip_image.vs.glsl
@@ -3,18 +3,18 @@
  * 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 ImageMaskData {
     RectWithSize local_rect;
 };
 
 ImageMaskData fetch_mask_data(int index) {
-    vec4 data[2] = fetch_data_2(index);
-    return ImageMaskData(RectWithSize(data[0].xy, data[0].zw));
+    vec4 data = fetch_from_resource_cache_1(index);
+    return ImageMaskData(RectWithSize(data.xy, data.zw));
 }
 
 void main(void) {
     CacheClipInstance cci = fetch_clip_item(gl_InstanceID);
     ClipArea area = fetch_clip_area(cci.render_task_index);
     Layer layer = fetch_layer(cci.layer_index);
     ImageMaskData mask = fetch_mask_data(cci.data_index);
     RectWithSize local_rect = mask.local_rect;
--- a/gfx/webrender/res/cs_clip_rectangle.vs.glsl
+++ b/gfx/webrender/res/cs_clip_rectangle.vs.glsl
@@ -4,46 +4,46 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 struct ClipRect {
     RectWithSize rect;
     vec4 mode;
 };
 
 ClipRect fetch_clip_rect(int index) {
-    vec4 data[2] = fetch_data_2(index);
+    vec4 data[2] = fetch_from_resource_cache_2(index);
     return ClipRect(RectWithSize(data[0].xy, data[0].zw), data[1]);
 }
 
 struct ClipCorner {
     RectWithSize rect;
     vec4 outer_inner_radius;
 };
 
 ClipCorner fetch_clip_corner(int index) {
-    vec4 data[2] = fetch_data_2(index);
+    vec4 data[2] = fetch_from_resource_cache_2(index);
     return ClipCorner(RectWithSize(data[0].xy, data[0].zw), data[1]);
 }
 
 struct ClipData {
     ClipRect rect;
     ClipCorner top_left;
     ClipCorner top_right;
     ClipCorner bottom_left;
     ClipCorner bottom_right;
 };
 
 ClipData fetch_clip(int index) {
     ClipData clip;
 
     clip.rect = fetch_clip_rect(index + 0);
-    clip.top_left = fetch_clip_corner(index + 1);
-    clip.top_right = fetch_clip_corner(index + 2);
-    clip.bottom_left = fetch_clip_corner(index + 3);
-    clip.bottom_right = fetch_clip_corner(index + 4);
+    clip.top_left = fetch_clip_corner(index + 2);
+    clip.top_right = fetch_clip_corner(index + 4);
+    clip.bottom_left = fetch_clip_corner(index + 6);
+    clip.bottom_right = fetch_clip_corner(index + 8);
 
     return clip;
 }
 
 void main(void) {
     CacheClipInstance cci = fetch_clip_item(gl_InstanceID);
     ClipArea area = fetch_clip_area(cci.render_task_index);
     Layer layer = fetch_layer(cci.layer_index);
--- a/gfx/webrender/res/cs_text_run.vs.glsl
+++ b/gfx/webrender/res/cs_text_run.vs.glsl
@@ -26,13 +26,14 @@ void main(void) {
 
     vec2 texture_size = vec2(textureSize(sColor0, 0));
     vec2 st0 = res.uv_rect.xy / texture_size;
     vec2 st1 = res.uv_rect.zw / texture_size;
 
     vec2 pos = mix(local_rect.xy,
                    local_rect.xy + local_rect.zw,
                    aPosition.xy);
-	vUv = mix(st0, st1, aPosition.xy);
-	vColor = text.color;
+
+    vUv = mix(st0, st1, aPosition.xy);
+    vColor = text.color;
 
     gl_Position = uTransform * vec4(pos, 0.0, 1.0);
 }
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -31,17 +31,17 @@
 #define PST_BOTTOM_LEFT  6
 #define PST_LEFT         7
 
 #define BORDER_LEFT      0
 #define BORDER_TOP       1
 #define BORDER_RIGHT     2
 #define BORDER_BOTTOM    3
 
-// Border styles as defined in webrender_traits/types.rs
+// Border styles as defined in webrender_api/types.rs
 #define BORDER_STYLE_NONE         0
 #define BORDER_STYLE_SOLID        1
 #define BORDER_STYLE_DOUBLE       2
 #define BORDER_STYLE_DOTTED       3
 #define BORDER_STYLE_DASHED       4
 #define BORDER_STYLE_HIDDEN       5
 #define BORDER_STYLE_GROOVE       6
 #define BORDER_STYLE_RIDGE        7
@@ -135,35 +135,26 @@ vec4[2] fetch_from_resource_cache_2(int 
 #define VECS_PER_PRIM_HEADER        2
 #define VECS_PER_TEXT_RUN           1
 #define VECS_PER_GRADIENT           3
 #define VECS_PER_GRADIENT_STOP      2
 
 uniform HIGHP_SAMPLER_FLOAT sampler2D sLayers;
 uniform HIGHP_SAMPLER_FLOAT sampler2D sRenderTasks;
 
-uniform HIGHP_SAMPLER_FLOAT sampler2D sData32;
-
 // Instanced attributes
 in ivec4 aData0;
 in ivec4 aData1;
 
 // get_fetch_uv is a macro to work around a macOS Intel driver parsing bug.
 // TODO: convert back to a function once the driver issues are resolved, if ever.
 // https://github.com/servo/webrender/pull/623
 // https://github.com/servo/servo/issues/13953
 #define get_fetch_uv(i, vpi)  ivec2(vpi * (i % (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi)), i / (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi))
 
-vec4[2] fetch_data_2(int index) {
-    ivec2 uv = get_fetch_uv(index, 2);
-    return vec4[2](
-        texelFetchOffset(sData32, uv, 0, ivec2(0, 0)),
-        texelFetchOffset(sData32, uv, 0, ivec2(1, 0))
-    );
-}
 
 vec4[8] fetch_from_resource_cache_8(int address) {
     ivec2 uv = get_resource_cache_uv(address);
     return vec4[8](
         texelFetchOffset(sResourceCache, uv, 0, ivec2(0, 0)),
         texelFetchOffset(sResourceCache, uv, 0, ivec2(1, 0)),
         texelFetchOffset(sResourceCache, uv, 0, ivec2(2, 0)),
         texelFetchOffset(sResourceCache, uv, 0, ivec2(3, 0)),
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -1,20 +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/. */
 
+use api::{BorderSide, BorderStyle, BorderWidths, ClipAndScrollInfo, ColorF, LayerPoint, LayerRect};
+use api::{LayerSize, LocalClip, NormalBorder};
 use ellipse::Ellipse;
+use gpu_cache::GpuDataRequest;
 use frame_builder::FrameBuilder;
-use mask_cache::{ClipSource};
-use prim_store::{BorderPrimitiveCpu, GpuBlock32, PrimitiveContainer};
+use mask_cache::ClipSource;
+use prim_store::{BorderPrimitiveCpu, PrimitiveContainer};
 use tiling::PrimitiveFlags;
 use util::{lerp, pack_as_float};
-use webrender_traits::{BorderSide, BorderStyle, BorderWidths, ClipAndScrollInfo, ClipRegion};
-use webrender_traits::{ColorF, LayerPoint, LayerRect, LayerSize, NormalBorder};
 
 #[repr(u8)]
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub enum BorderCornerInstance {
     Single,     // Single instance needed - corner styles are same or similar.
     Double,     // Different corner styles. Draw two instances, one per style.
 }
 
@@ -74,17 +75,17 @@ impl BorderCornerKind {
                                              border_rect.size.height -
                                              size.height);
                 let clip_center = origin + LayerSize::new(size.width, 0.0);
                 (origin, clip_center)
             }
         };
         let clip_data = BorderCornerClipData {
             corner_rect: LayerRect::new(origin, size),
-            clip_center: clip_center,
+            clip_center,
             corner: pack_as_float(corner as u32),
             kind: pack_as_float(kind as u32),
         };
         BorderCornerKind::Mask(clip_data,
                                radius,
                                LayerSize::new(width0, width1),
                                kind)
     }
@@ -220,33 +221,33 @@ impl NormalBorderHelpers for NormalBorde
 }
 
 impl FrameBuilder {
     fn add_normal_border_primitive(&mut self,
                                    rect: &LayerRect,
                                    border: &NormalBorder,
                                    widths: &BorderWidths,
                                    clip_and_scroll: ClipAndScrollInfo,
-                                   clip_region: &ClipRegion,
+                                   local_clip: &LocalClip,
                                    corner_instances: [BorderCornerInstance; 4],
                                    extra_clips: &[ClipSource]) {
         let radius = &border.radius;
         let left = &border.left;
         let right = &border.right;
         let top = &border.top;
         let bottom = &border.bottom;
 
         // 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 prim_cpu = BorderPrimitiveCpu {
-            corner_instances: corner_instances,
+            corner_instances,
 
             // TODO(gw): In the future, we will build these on demand
             //           from the deserialized display list, rather
             //           than creating it immediately.
             gpu_blocks: [
                 [ pack_as_float(left.style as u32),
                   pack_as_float(top.style as u32),
                   pack_as_float(right.style as u32),
@@ -267,31 +268,31 @@ impl FrameBuilder {
                   radius.bottom_right.height,
                   radius.bottom_left.width,
                   radius.bottom_left.height ].into(),
             ],
         };
 
         self.add_primitive(clip_and_scroll,
                            &rect,
-                           clip_region,
+                           local_clip,
                            extra_clips,
                            PrimitiveContainer::Border(prim_cpu));
     }
 
     // TODO(gw): This allows us to move border types over to the
     // simplified shader model one at a time. Once all borders
     // are converted, this can be removed, along with the complex
     // border code path.
     pub fn add_normal_border(&mut self,
                              rect: &LayerRect,
                              border: &NormalBorder,
                              widths: &BorderWidths,
                              clip_and_scroll: ClipAndScrollInfo,
-                             clip_region: &ClipRegion) {
+                             local_clip: &LocalClip) {
         // 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).
@@ -361,44 +362,44 @@ impl FrameBuilder {
             let p1 = rect.bottom_right();
             let rect_width = rect.size.width;
             let rect_height = rect.size.height;
 
             // Add a solid rectangle for each visible edge/corner combination.
             if top_edge == BorderEdgeKind::Solid {
                 self.add_solid_rectangle(clip_and_scroll,
                                          &LayerRect::new(p0, LayerSize::new(rect_width, top_len)),
-                                         clip_region,
+                                         local_clip,
                                          &border.top.color,
                                          PrimitiveFlags::None);
             }
             if left_edge == BorderEdgeKind::Solid {
                 self.add_solid_rectangle(clip_and_scroll,
                                          &LayerRect::new(LayerPoint::new(p0.x, p0.y + top_len),
                                                          LayerSize::new(left_len,
                                                                         rect_height - top_len - bottom_len)),
-                                         clip_region,
+                                         local_clip,
                                          &border.left.color,
                                          PrimitiveFlags::None);
             }
             if right_edge == BorderEdgeKind::Solid {
                 self.add_solid_rectangle(clip_and_scroll,
                                          &LayerRect::new(LayerPoint::new(p1.x - right_len,
                                                                          p0.y + top_len),
                                                          LayerSize::new(right_len,
                                                                         rect_height - top_len - bottom_len)),
-                                         clip_region,
+                                         local_clip,
                                          &border.right.color,
                                          PrimitiveFlags::None);
             }
             if bottom_edge == BorderEdgeKind::Solid {
                 self.add_solid_rectangle(clip_and_scroll,
                                          &LayerRect::new(LayerPoint::new(p0.x, p1.y - bottom_len),
                                                          LayerSize::new(rect_width, bottom_len)),
-                                         clip_region,
+                                         local_clip,
                                          &border.bottom.color,
                                          PrimitiveFlags::None);
             }
         } else {
             // Create clip masks for border corners, if required.
             let mut extra_clips = Vec::new();
             let mut corner_instances = [BorderCornerInstance::Single; 4];
 
@@ -417,17 +418,17 @@ impl FrameBuilder {
                     _ => {}
                 }
             }
 
             self.add_normal_border_primitive(rect,
                                              border,
                                              widths,
                                              clip_and_scroll,
-                                             clip_region,
+                                             local_clip,
                                              corner_instances,
                                              &extra_clips);
         }
     }
 }
 
 pub trait BorderSideHelpers {
     fn border_color(&self,
@@ -530,48 +531,48 @@ impl BorderCornerClipSource {
 
                 // Add space for one extra dot since they are centered at the
                 // start of the arc.
                 (ellipse, 1 + max_dot_count.ceil() as usize)
             }
         };
 
         BorderCornerClipSource {
-            kind: kind,
-            corner_data: corner_data,
-            max_clip_count: max_clip_count,
+            kind,
+            corner_data,
+            max_clip_count,
             actual_clip_count: 0,
-            ellipse: ellipse,
-            widths: widths,
+            ellipse,
+            widths,
         }
     }
 
-    pub fn populate_gpu_data(&mut self, slice: &mut [GpuBlock32]) {
-        let (header, clips) = slice.split_first_mut().unwrap();
-        *header = self.corner_data.into();
+    pub fn write(&mut self, mut request: GpuDataRequest) {
+        self.corner_data.write(&mut request);
 
         match self.kind {
             BorderCornerClipKind::Dash => {
                 // Get the correct dash arc length.
                 self.actual_clip_count = self.max_clip_count;
                 let dash_arc_length = 0.5 * self.ellipse.total_arc_length / (self.actual_clip_count - 1) as f32;
                 let mut current_arc_length = -0.5 * dash_arc_length;
-                for dash_index in 0..self.actual_clip_count {
+                for _ in 0..self.actual_clip_count {
                     let arc_length0 = current_arc_length;
                     current_arc_length += dash_arc_length;
 
                     let arc_length1 = current_arc_length;
                     current_arc_length += dash_arc_length;
 
                     let dash_data = BorderCornerDashClipData::new(arc_length0,
                                                                   arc_length1,
                                                                   &self.ellipse);
+                    dash_data.write(&mut request);
+                }
 
-                    clips[dash_index] = dash_data.into();
-                }
+                assert_eq!(request.close(), 2 + 2 * self.actual_clip_count);
             }
             BorderCornerClipKind::Dot => {
                 let mut forward_dots = Vec::new();
                 let mut back_dots = Vec::new();
                 let mut leftover_arc_length = 0.0;
 
                 // Alternate between adding dots at the start and end of the
                 // ellipse arc. This ensures that we always end up with an exact
@@ -618,37 +619,36 @@ impl BorderCornerClipSource {
                         back_dots.push(dot);
                     }
                 }
 
                 // Now step through the dots, and distribute any extra
                 // leftover space on the arc between them evenly. Once
                 // the final arc position is determined, generate the correct
                 // arc positions and angles that get passed to the clip shader.
-                self.actual_clip_count = 0;
-                let dot_count = forward_dots.len() + back_dots.len();
-                let extra_space_per_dot = leftover_arc_length / (dot_count - 1) as f32;
+                self.actual_clip_count = forward_dots.len() + back_dots.len();
+                let extra_space_per_dot = leftover_arc_length / (self.actual_clip_count - 1) as f32;
 
                 for (i, dot) in forward_dots.iter().enumerate() {
                     let extra_dist = i as f32 * extra_space_per_dot;
                     let dot = BorderCornerDotClipData::new(dot.arc_pos + extra_dist,
                                                            0.5 * dot.diameter,
                                                            &self.ellipse);
-                    clips[self.actual_clip_count] = dot.into();
-                    self.actual_clip_count += 1;
+                    dot.write(&mut request);
                 }
 
                 for (i, dot) in back_dots.iter().enumerate() {
                     let extra_dist = i as f32 * extra_space_per_dot;
                     let dot = BorderCornerDotClipData::new(dot.arc_pos - extra_dist,
                                                            0.5 * dot.diameter,
                                                            &self.ellipse);
-                    clips[self.actual_clip_count] = dot.into();
-                    self.actual_clip_count += 1;
+                    dot.write(&mut request);
                 }
+
+                assert_eq!(request.close(), 2 + self.actual_clip_count);
             }
         }
     }
 }
 
 /// Represents the common GPU data for writing a
 /// clip mask for a border corner.
 #[derive(Debug, Copy, Clone, PartialEq)]
@@ -661,16 +661,23 @@ pub struct BorderCornerClipData {
     clip_center: LayerPoint,
     /// The shader needs to know which corner, to
     /// be able to flip the dash tangents to the
     /// right orientation.
     corner: f32,        // Of type BorderCorner enum
     kind: f32,          // Of type BorderCornerClipKind enum
 }
 
+impl BorderCornerClipData {
+    fn write(&self, request: &mut GpuDataRequest) {
+        request.push(self.corner_rect);
+        request.push([self.clip_center.x, self.clip_center.y, self.corner, self.kind]);
+    }
+}
+
 /// Represents the GPU data for drawing a single dash
 /// to a clip mask. A dash clip is defined by two lines.
 /// We store a point on the ellipse curve, and a tangent
 /// to that point, which allows for efficient line-distance
 /// calculations in the fragment shader.
 #[derive(Debug, Clone)]
 #[repr(C)]
 pub struct BorderCornerDashClipData {
@@ -692,49 +699,58 @@ impl BorderCornerDashClipData {
 
         BorderCornerDashClipData {
             point0: p0,
             tangent0: t0,
             point1: p1,
             tangent1: t1,
         }
     }
+
+    fn write(&self, request: &mut GpuDataRequest) {
+        request.push([self.point0.x, self.point0.y,
+                      self.tangent0.x, self.tangent0.y]);
+        request.push([self.point1.x, self.point1.y,
+                      self.tangent1.x, self.tangent1.y]);
+    }
 }
 
 /// Represents the GPU data for drawing a single dot
 /// to a clip mask.
 #[derive(Debug, Clone)]
 #[repr(C)]
 pub struct BorderCornerDotClipData {
     pub center: LayerPoint,
     pub radius: f32,
-    pub padding: [f32; 5],
 }
 
 impl BorderCornerDotClipData {
     pub fn new(arc_length: f32,
                radius: f32,
                ellipse: &Ellipse) -> BorderCornerDotClipData {
         let theta = ellipse.find_angle_for_arc_length(arc_length);
         let (center, _) = ellipse.get_point_and_tangent(theta);
 
         BorderCornerDotClipData {
-            center: center,
-            radius: radius,
-            padding: [0.0; 5],
+            center,
+            radius,
         }
     }
+
+    fn write(&self, request: &mut GpuDataRequest) {
+        request.push([self.center.x, self.center.y, self.radius, 0.0]);
+    }
 }
 
 #[derive(Copy, Clone, Debug)]
 struct DotInfo {
     arc_pos: f32,
     diameter: f32,
 }
 
 impl DotInfo {
     fn new(arc_pos: f32, diameter: f32) -> DotInfo {
         DotInfo {
-            arc_pos: arc_pos,
-            diameter: diameter,
+            arc_pos,
+            diameter,
         }
     }
 }
--- a/gfx/webrender/src/clip_scroll_node.rs
+++ b/gfx/webrender/src/clip_scroll_node.rs
@@ -1,74 +1,69 @@
 /* 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 api::{ClipId, DeviceIntRect, LayerPixel, LayerPoint, LayerRect, LayerSize};
+use api::{LayerToScrollTransform, LayerToWorldTransform, LayerVector2D, PipelineId};
+use api::{ScrollClamping, ScrollEventPhase, ScrollLocation, WorldPoint};
 use geometry::ray_intersects_rect;
-use mask_cache::{ClipSource, MaskCacheInfo, RegionMode};
-use prim_store::GpuBlock32;
-use renderer::VertexDataStore;
+use mask_cache::{ClipRegion, ClipSource, MaskCacheInfo};
 use spring::{DAMPING, STIFFNESS, Spring};
 use tiling::PackedLayerIndex;
-use util::TransformedRectKind;
-use webrender_traits::{ClipId, ClipRegion, DeviceIntRect, LayerPixel, LayerPoint, LayerRect};
-use webrender_traits::{LayerSize, LayerToScrollTransform, LayerToWorldTransform, PipelineId};
-use webrender_traits::{ScrollClamping, ScrollEventPhase, ScrollLayerRect, ScrollLocation};
-use webrender_traits::{WorldPoint, LayerVector2D};
-use webrender_traits::{as_scroll_parent_vector};
+use util::{MatrixHelpers, TransformedRectKind};
 
 #[cfg(target_os = "macos")]
 const CAN_OVERSCROLL: bool = true;
 
 #[cfg(not(target_os = "macos"))]
 const CAN_OVERSCROLL: bool = false;
 
 #[derive(Clone, Debug)]
 pub struct ClipInfo {
     /// The ClipSource for this node, which is used to generate mask_cache_info.
     pub clip_sources: Vec<ClipSource>,
 
     /// The MaskCacheInfo for this node, which is produced by processing the
     /// provided ClipSource.
-    pub mask_cache_info: Option<MaskCacheInfo>,
+    pub mask_cache_info: MaskCacheInfo,
 
     /// The packed layer index for this node, which is used to render a clip mask
     /// for it, if necessary.
     pub packed_layer_index: PackedLayerIndex,
 
     /// The final transformed rectangle of this clipping region for this node,
     /// which depends on the screen rectangle and the transformation of all of
     /// the parents.
     pub screen_bounding_rect: Option<(TransformedRectKind, DeviceIntRect)>,
+
+    /// The biggest final transformed rectangle that is completely inside the
+    /// clipping region for this node.
+    pub screen_inner_rect: DeviceIntRect,
+
+    /// A rectangle which defines the rough boundaries of this clip in reference
+    /// frame relative coordinates (with no scroll offsets).
+    pub clip_rect: LayerRect,
 }
 
 impl ClipInfo {
-    pub fn new(clip_region: &ClipRegion,
-               clip_store: &mut VertexDataStore<GpuBlock32>,
-               packed_layer_index: PackedLayerIndex,)
-               -> ClipInfo {
-        // We pass true here for the MaskCacheInfo because this type of
-        // mask needs an extra clip for the clip rectangle.
-        let clip_sources = vec![ClipSource::Region(clip_region.clone(), RegionMode::IncludeRect)];
+    pub fn new(clip_region: ClipRegion, packed_layer_index: PackedLayerIndex) -> ClipInfo {
+        let clip_rect = LayerRect::new(clip_region.origin, clip_region.main.size);
+        let clip_sources = vec![ClipSource::Region(clip_region)];
         ClipInfo {
-            mask_cache_info: MaskCacheInfo::new(&clip_sources, clip_store),
-            clip_sources: clip_sources,
-            packed_layer_index: packed_layer_index,
+            mask_cache_info: MaskCacheInfo::new(&clip_sources),
+            clip_sources,
+            packed_layer_index,
             screen_bounding_rect: None,
+            screen_inner_rect: DeviceIntRect::zero(),
+            clip_rect: clip_rect,
         }
     }
+}
 
-    pub fn is_masking(&self) -> bool {
-        match self.mask_cache_info {
-            Some(ref info) => info.is_masking(),
-            _ => false,
-        }
-    }
-
-}
 #[derive(Clone, Debug)]
 pub enum NodeType {
     /// Transform for this layer, relative to parent reference frame. A reference
     /// frame establishes a new coordinate space in the tree.
     ReferenceFrame(LayerToScrollTransform),
 
     /// Other nodes just do clipping, but no transformation.
     Clip(ClipInfo),
@@ -86,17 +81,20 @@ pub struct ClipScrollNode {
     /// Viewing rectangle in the coordinate system of the parent reference frame.
     pub local_viewport_rect: LayerRect,
 
     /// Clip rect of this node - typically the same as viewport rect, except
     /// in overscroll cases.
     pub local_clip_rect: LayerRect,
 
     /// Viewport rectangle clipped against parent layer(s) viewport rectangles.
-    /// This is in the coordinate system which starts at our origin.
+    /// This is in the coordinate system of the node origin.
+    /// Precisely, it combines the local clipping rectangles of all the parent
+    /// nodes on the way to the root, including those of `ClipRegion` rectangles.
+    /// The combined clip is lossy/concervative on `ReferenceFrame` nodes.
     pub combined_local_viewport_rect: LayerRect,
 
     /// World transform for the viewport rect itself. This is the parent
     /// reference frame transformation plus the scrolling offsets provided by
     /// the nodes in between the reference frame and this node.
     pub world_viewport_transform: LayerToWorldTransform,
 
     /// World transform for content transformed by this node.
@@ -118,86 +116,82 @@ pub struct ClipScrollNode {
 
     /// Whether or not this node is a reference frame.
     pub node_type: NodeType,
 }
 
 impl ClipScrollNode {
     pub fn new_scroll_frame(pipeline_id: PipelineId,
                             parent_id: ClipId,
-                            content_rect: &LayerRect,
-                            frame_rect: &LayerRect)
+                            frame_rect: &LayerRect,
+                            content_size: &LayerSize)
                             -> ClipScrollNode {
         ClipScrollNode {
-            content_size: content_rect.size,
+            content_size: *content_size,
             local_viewport_rect: *frame_rect,
             local_clip_rect: *frame_rect,
             combined_local_viewport_rect: LayerRect::zero(),
             world_viewport_transform: LayerToWorldTransform::identity(),
             world_content_transform: LayerToWorldTransform::identity(),
             reference_frame_relative_scroll_offset: LayerVector2D::zero(),
             parent: Some(parent_id),
             children: Vec::new(),
-            pipeline_id: pipeline_id,
+            pipeline_id,
             node_type: NodeType::ScrollFrame(ScrollingState::new()),
         }
     }
 
-    pub fn new(pipeline_id: PipelineId,
-               parent_id: ClipId,
-               content_rect: &LayerRect,
-               clip_rect: &LayerRect,
-               clip_info: ClipInfo)
-               -> ClipScrollNode {
-        // FIXME(mrobinson): We don't yet handle clipping rectangles that don't start at the origin
-        // of the node.
-        let local_viewport_rect = LayerRect::new(content_rect.origin, clip_rect.size);
+    pub fn new(pipeline_id: PipelineId, parent_id: ClipId, clip_info: ClipInfo) -> ClipScrollNode {
         ClipScrollNode {
-            content_size: content_rect.size,
-            local_viewport_rect: local_viewport_rect,
-            local_clip_rect: local_viewport_rect,
+            content_size: clip_info.clip_rect.size,
+            local_viewport_rect: clip_info.clip_rect,
+            local_clip_rect: clip_info.clip_rect,
             combined_local_viewport_rect: LayerRect::zero(),
             world_viewport_transform: LayerToWorldTransform::identity(),
             world_content_transform: LayerToWorldTransform::identity(),
             reference_frame_relative_scroll_offset: LayerVector2D::zero(),
             parent: Some(parent_id),
             children: Vec::new(),
-            pipeline_id: pipeline_id,
+            pipeline_id,
             node_type: NodeType::Clip(clip_info),
         }
     }
 
     pub fn new_reference_frame(parent_id: Option<ClipId>,
                                local_viewport_rect: &LayerRect,
                                content_size: LayerSize,
                                local_transform: &LayerToScrollTransform,
                                pipeline_id: PipelineId)
                                -> ClipScrollNode {
         ClipScrollNode {
-            content_size: content_size,
+            content_size,
             local_viewport_rect: *local_viewport_rect,
             local_clip_rect: *local_viewport_rect,
             combined_local_viewport_rect: LayerRect::zero(),
             world_viewport_transform: LayerToWorldTransform::identity(),
             world_content_transform: LayerToWorldTransform::identity(),
             reference_frame_relative_scroll_offset: LayerVector2D::zero(),
             parent: parent_id,
             children: Vec::new(),
-            pipeline_id: pipeline_id,
+            pipeline_id,
             node_type: NodeType::ReferenceFrame(*local_transform),
         }
     }
 
     pub fn add_child(&mut self, child: ClipId) {
         self.children.push(child);
     }
 
     pub fn finalize(&mut self, new_scrolling: &ScrollingState) {
         match self.node_type {
-            NodeType::ReferenceFrame(_) | NodeType::Clip(_) => (),
+            NodeType::ReferenceFrame(_) | NodeType::Clip(_) => {
+                if new_scrolling.offset != LayerVector2D::zero() {
+                    warn!("Tried to scroll a non-scroll node.");
+                }
+            }
             NodeType::ScrollFrame(ref mut scrolling) => *scrolling = *new_scrolling,
         }
     }
 
     pub fn set_scroll_origin(&mut self, origin: &LayerPoint, clamp: ScrollClamping) -> bool {
         let scrollable_height = self.scrollable_height();
         let scrollable_width = self.scrollable_width();
 
@@ -229,63 +223,40 @@ impl ClipScrollNode {
         scrolling.offset = new_offset;
         scrolling.bouncing_back = false;
         scrolling.started_bouncing_back = false;
         true
     }
 
     pub fn update_transform(&mut self,
                             parent_reference_frame_transform: &LayerToWorldTransform,
-                            parent_combined_viewport_rect: &ScrollLayerRect,
+                            parent_combined_viewport_rect: &LayerRect,
                             parent_scroll_offset: LayerVector2D,
                             parent_accumulated_scroll_offset: LayerVector2D) {
-        self.reference_frame_relative_scroll_offset = match self.node_type {
-            NodeType::ReferenceFrame(_) => LayerVector2D::zero(),
-            NodeType::Clip(_) | NodeType::ScrollFrame(..) => parent_accumulated_scroll_offset,
-        };
+
+        let scrolled_parent_combined_clip = parent_combined_viewport_rect
+            .translate(&-parent_scroll_offset);
 
-        let local_transform = match self.node_type {
-            NodeType::ReferenceFrame(transform) => transform,
-            NodeType::Clip(_) | NodeType::ScrollFrame(..) => LayerToScrollTransform::identity(),
-        };
-
-        let inv_transform = match local_transform.inverse() {
-            Some(transform) => transform,
-            None => {
-                // If a transform function causes the current transformation matrix of an object
-                // to be non-invertible, the object and its content do not get displayed.
-                self.combined_local_viewport_rect = LayerRect::zero();
-                return;
+        let (local_transform, combined_clip, reference_frame_scroll_offset) = match self.node_type {
+            NodeType::ReferenceFrame(transform) => {
+                let combined_clip = transform.with_destination::<LayerPixel>()
+                                             .inverse_rect_footprint(&scrolled_parent_combined_clip);
+                (transform, combined_clip, LayerVector2D::zero())
+            }
+            NodeType::Clip(_) | NodeType::ScrollFrame(_) => {
+                // Move the parent's viewport into the local space (of the node origin)
+                // and intersect with the local clip rectangle to get the local viewport.
+                let combined_clip = scrolled_parent_combined_clip.intersection(&self.local_clip_rect)
+                                                                 .unwrap_or(LayerRect::zero());
+                (LayerToScrollTransform::identity(), combined_clip, parent_accumulated_scroll_offset)
             }
         };
 
-        // We are trying to move the combined viewport rectangle of our parent nodes into the
-        // coordinate system of this node, so we must invert our transformation (only for
-        // reference frames) and then apply the scroll offset the parent layer. The combined
-        // local viewport rect doesn't include scrolling offsets so the only one that matters
-        // is the relative offset between us and the parent.
-        let parent_combined_viewport_in_local_space =
-            inv_transform.pre_translate(-as_scroll_parent_vector(&parent_scroll_offset).to_3d())
-                         .transform_rect(parent_combined_viewport_rect);
-
-        // Now that we have the combined viewport rectangle of the parent nodes in local space,
-        // we do the intersection and get our combined viewport rect in the coordinate system
-        // starting from our origin.
-        self.combined_local_viewport_rect = match self.node_type {
-            NodeType::Clip(_) | NodeType::ScrollFrame(..) => {
-                parent_combined_viewport_in_local_space.intersection(&self.local_clip_rect)
-                                                       .unwrap_or(LayerRect::zero())
-            }
-            NodeType::ReferenceFrame(_) => parent_combined_viewport_in_local_space,
-        };
-
-        // HACK: prevent the code above for non-AA transforms, it's incorrect.
-        if (local_transform.m13, local_transform.m23) != (0.0, 0.0) {
-            self.combined_local_viewport_rect = self.local_clip_rect;
-        }
+        self.combined_local_viewport_rect = combined_clip;
+        self.reference_frame_relative_scroll_offset = reference_frame_scroll_offset;
 
         // The transformation for this viewport in world coordinates is the transformation for
         // our parent reference frame, plus any accumulated scrolling offsets from nodes
         // between our reference frame and this node. For reference frames, we also include
         // whatever local transformation this reference frame provides. This can be combined
         // with the local_viewport_rect to get its position in world space.
         self.world_viewport_transform =
             parent_reference_frame_transform
--- a/gfx/webrender/src/clip_scroll_tree.rs
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -2,20 +2,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use clip_scroll_node::{ClipScrollNode, NodeType, ScrollingState};
 use fnv::FnvHasher;
 use print_tree::PrintTree;
 use std::collections::{HashMap, HashSet};
 use std::hash::BuildHasherDefault;
-use webrender_traits::{ClipId, LayerPoint, LayerRect, LayerToScrollTransform};
-use webrender_traits::{LayerToWorldTransform, PipelineId, ScrollClamping, ScrollEventPhase};
-use webrender_traits::{ScrollLayerRect, ScrollLayerState, ScrollLocation, WorldPoint};
-use webrender_traits::{as_scroll_parent_rect, LayerVector2D};
+use api::{ClipId, LayerPoint, LayerRect, LayerToScrollTransform};
+use api::{LayerToWorldTransform, PipelineId, ScrollClamping, ScrollEventPhase};
+use api::{LayerVector2D, ScrollLayerState, ScrollLocation, WorldPoint};
 
 pub type ScrollStates = HashMap<ClipId, ScrollingState, BuildHasherDefault<FnvHasher>>;
 
 pub struct ClipScrollTree {
     pub nodes: HashMap<ClipId, ClipScrollNode, BuildHasherDefault<FnvHasher>>,
     pub pending_scroll_offsets: HashMap<ClipId, (LayerPoint, ScrollClamping)>,
 
     /// The ClipId of the currently scrolling node. Used to allow the same
@@ -226,72 +225,74 @@ impl ClipScrollTree {
         if self.nodes.is_empty() {
             return;
         }
 
         let root_reference_frame_id = self.root_reference_frame_id();
         let root_viewport = self.nodes[&root_reference_frame_id].local_clip_rect;
         self.update_node_transform(root_reference_frame_id,
                                    &LayerToWorldTransform::create_translation(pan.x, pan.y, 0.0),
-                                   &as_scroll_parent_rect(&root_viewport),
+                                   &root_viewport,
                                    LayerVector2D::zero(),
                                    LayerVector2D::zero());
     }
 
     fn update_node_transform(&mut self,
                              layer_id: ClipId,
                              parent_reference_frame_transform: &LayerToWorldTransform,
-                             parent_viewport_rect: &ScrollLayerRect,
+                             parent_viewport_rect: &LayerRect,
                              parent_scroll_offset: LayerVector2D,
                              parent_accumulated_scroll_offset: LayerVector2D) {
         // TODO(gw): This is an ugly borrow check workaround to clone these.
         //           Restructure this to avoid the clones!
         let (reference_frame_transform,
-             viewport_rect,
+             combined_local_viewport_rect,
              scroll_offset,
              accumulated_scroll_offset,
              node_children) = {
-            match self.nodes.get_mut(&layer_id) {
-                Some(node) => {
-                    node.update_transform(parent_reference_frame_transform,
-                                          parent_viewport_rect,
-                                          parent_scroll_offset,
-                                          parent_accumulated_scroll_offset);
+
+            let mut node = match self.nodes.get_mut(&layer_id) {
+                Some(node) => node,
+                None => return,
+            };
+            node.update_transform(parent_reference_frame_transform,
+                                  parent_viewport_rect,
+                                  parent_scroll_offset,
+                                  parent_accumulated_scroll_offset);
 
-                    // The transformation we are passing is the transformation of the parent
-                    // reference frame and the offset is the accumulated offset of all the nodes
-                    // between us and the parent reference frame. If we are a reference frame,
-                    // we need to reset both these values.
-                    let (transform, offset, accumulated_scroll_offset) = match node.node_type {
-                        NodeType::ReferenceFrame(..) =>
-                            (node.world_viewport_transform,
-                             LayerVector2D::zero(),
-                             LayerVector2D::zero()),
-                        _ => {
-                            let scroll_offset = node.scroll_offset();
-                            (*parent_reference_frame_transform,
-                             scroll_offset,
-                             scroll_offset + parent_accumulated_scroll_offset)
-                        }
-                    };
+            // The transformation we are passing is the transformation of the parent
+            // reference frame and the offset is the accumulated offset of all the nodes
+            // between us and the parent reference frame. If we are a reference frame,
+            // we need to reset both these values.
+            let (reference_frame_transform, scroll_offset, accumulated_scroll_offset) = match node.node_type {
+                NodeType::ReferenceFrame(..) =>
+                    (node.world_viewport_transform,
+                     LayerVector2D::zero(),
+                     LayerVector2D::zero()),
+                NodeType::Clip(..) =>
+                    (*parent_reference_frame_transform,
+                     LayerVector2D::zero(),
+                     parent_accumulated_scroll_offset),
+                NodeType::ScrollFrame(ref scrolling) =>
+                    (*parent_reference_frame_transform,
+                     scrolling.offset,
+                     scrolling.offset + parent_accumulated_scroll_offset),
+            };
 
-                    (transform,
-                     as_scroll_parent_rect(&node.combined_local_viewport_rect),
-                     offset,
-                     accumulated_scroll_offset,
-                     node.children.clone())
-                }
-                None => return,
-            }
+            (reference_frame_transform,
+             node.combined_local_viewport_rect,
+             scroll_offset,
+             accumulated_scroll_offset,
+             node.children.clone())
         };
 
         for child_layer_id in node_children {
             self.update_node_transform(child_layer_id,
                                        &reference_frame_transform,
-                                       &viewport_rect,
+                                       &combined_local_viewport_rect,
                                        scroll_offset,
                                        accumulated_scroll_offset);
         }
     }
 
     pub fn tick_scrolling_bounce_animations(&mut self) {
         for (_, node) in &mut self.nodes {
             node.tick_scrolling_bounce_animation()
@@ -308,17 +309,16 @@ impl ClipScrollTree {
             };
 
             node.finalize(&scrolling_state);
 
             if let Some((pending_offset, clamping)) = self.pending_scroll_offsets.remove(clip_id) {
                 node.set_scroll_origin(&pending_offset, clamping);
             }
         }
-
     }
 
     pub fn generate_new_clip_id(&mut self, pipeline_id: PipelineId) -> ClipId {
         let new_id = ClipId::DynamicallyAddedNode(self.current_new_node_item, pipeline_id);
         self.current_new_node_item += 1;
         new_id
     }
 
@@ -360,16 +360,17 @@ impl ClipScrollTree {
 
     fn print_node(&self, id: &ClipId, pt: &mut PrintTree) {
         let node = self.nodes.get(id).unwrap();
 
         match node.node_type {
             NodeType::Clip(ref info) => {
                 pt.new_level("Clip".to_owned());
                 pt.add_item(format!("screen_bounding_rect: {:?}", info.screen_bounding_rect));
+                pt.add_item(format!("screen_inner_rect: {:?}", info.screen_inner_rect));
 
                 pt.new_level(format!("Clip Sources [{}]", info.clip_sources.len()));
                 for source in &info.clip_sources {
                     pt.add_item(format!("{:?}", source));
                 }
                 pt.end_level();
             }
             NodeType::ReferenceFrame(ref transform) => {
@@ -377,19 +378,19 @@ impl ClipScrollTree {
             }
             NodeType::ScrollFrame(scrolling_info) => {
                 pt.new_level(format!("ScrollFrame"));
                 pt.add_item(format!("scroll.offset: {:?}", scrolling_info.offset));
             }
         }
 
         pt.add_item(format!("content_size: {:?}", node.content_size));
-        pt.add_item(format!("combined_local_viewport_rect: {:?}", node.combined_local_viewport_rect));
         pt.add_item(format!("local_viewport_rect: {:?}", node.local_viewport_rect));
         pt.add_item(format!("local_clip_rect: {:?}", node.local_clip_rect));
+        pt.add_item(format!("combined_local_viewport_rect: {:?}", node.combined_local_viewport_rect));
         pt.add_item(format!("world_viewport_transform: {:?}", node.world_viewport_transform));
         pt.add_item(format!("world_content_transform: {:?}", node.world_content_transform));
 
         for child_id in &node.children {
             self.print_node(child_id, pt);
         }
 
         pt.end_level();
--- a/gfx/webrender/src/debug_colors.rs
+++ b/gfx/webrender/src/debug_colors.rs
@@ -1,15 +1,15 @@
 /* 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/. */
 
 #![allow(dead_code)]
 
-use webrender_traits::ColorF;
+use api::ColorF;
 
 // A subset of the standard CSS colors, useful for defining GPU tag colors etc.
 
 pub const INDIGO: ColorF = ColorF { r: 0.294117647059, g: 0.0, b: 0.509803921569, a: 1.0 };
 pub const GOLD: ColorF = ColorF { r: 1.0, g: 0.843137254902, b: 0.0, a: 1.0 };
 pub const FIREBRICK: ColorF = ColorF { r: 0.698039215686, g: 0.133333333333, b: 0.133333333333, a: 1.0 };
 pub const INDIANRED: ColorF = ColorF { r: 0.803921568627, g: 0.360784313725, b: 0.360784313725, a: 1.0 };
 pub const YELLOW: ColorF = ColorF { r: 1.0, g: 1.0, b: 0.0, a: 1.0 };
--- a/gfx/webrender/src/debug_render.rs
+++ b/gfx/webrender/src/debug_render.rs
@@ -4,17 +4,17 @@
 
 use debug_font_data;
 use device::{Device, GpuMarker, ProgramId, VAOId, TextureId, VertexFormat};
 use device::{TextureFilter, VertexUsageHint, TextureTarget};
 use euclid::{Transform3D, Point2D, Size2D, Rect};
 use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, TextureSampler};
 use internal_types::{DebugFontVertex, DebugColorVertex, RenderTargetMode, PackedColor};
 use std::f32;
-use webrender_traits::{ColorF, ImageFormat, DeviceUintSize};
+use api::{ColorF, ImageFormat, DeviceUintSize};
 
 pub struct DebugRenderer {
     font_vertices: Vec<DebugFontVertex>,
     font_indices: Vec<u32>,
     font_program_id: ProgramId,
     font_vao: VAOId,
     font_texture_id: TextureId,
 
@@ -43,24 +43,24 @@ impl DebugRenderer {
                             TextureFilter::Linear,
                             RenderTargetMode::None,
                             Some(&debug_font_data::FONT_BITMAP));
 
         DebugRenderer {
             font_vertices: Vec::new(),
             font_indices: Vec::new(),
             line_vertices: Vec::new(),
-            tri_vao: tri_vao,
+            tri_vao,
             tri_vertices: Vec::new(),
             tri_indices: Vec::new(),
-            font_program_id: font_program_id,
-            color_program_id: color_program_id,
-            font_vao: font_vao,
-            line_vao: line_vao,
-            font_texture_id: font_texture_id,
+            font_program_id,
+            color_program_id,
+            font_vao,
+            line_vao,
+            font_texture_id,
         }
     }
 
     pub fn line_height(&self) -> f32 {
         debug_font_data::FONT_SIZE as f32 * 1.1
     }
 
     pub fn add_text(&mut self,
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -13,21 +13,22 @@ 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::ops::Add;
 use std::path::PathBuf;
+use std::ptr;
 use std::rc::Rc;
 //use std::sync::mpsc::{channel, Sender};
 //use std::thread;
-use webrender_traits::{ColorF, ImageFormat};
-use webrender_traits::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintSize};
+use api::{ColorF, ImageFormat};
+use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintSize};
 
 #[derive(Debug, Copy, Clone, PartialEq, Ord, Eq, PartialOrd)]
 pub struct FrameId(usize);
 
 impl FrameId {
     pub fn new(value: usize) -> FrameId {
         FrameId(value)
     }
@@ -46,21 +47,21 @@ const GL_FORMAT_A: gl::GLuint = gl::RED;
 
 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
 const GL_FORMAT_A: gl::GLuint = gl::ALPHA;
 
 const GL_FORMAT_BGRA_GL: gl::GLuint = gl::BGRA;
 
 const GL_FORMAT_BGRA_GLES: gl::GLuint = gl::BGRA_EXT;
 
-const SHADER_VERSION_GL: &'static str = "#version 150\n";
+const SHADER_VERSION_GL: &str = "#version 150\n";
 
-const SHADER_VERSION_GLES: &'static str = "#version 300 es\n";
+const SHADER_VERSION_GLES: &str = "#version 300 es\n";
 
-static SHADER_PREAMBLE: &'static str = "shared";
+static SHADER_PREAMBLE: &str = "shared";
 
 #[repr(u32)]
 pub enum DepthFunction {
     Less = gl::LESS,
     LessEqual = gl::LEQUAL,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
@@ -293,17 +294,17 @@ impl VertexFormat {
 
 impl TextureId {
     pub fn bind(&self, gl: &gl::Gl) {
         gl.bind_texture(self.target, self.name);
     }
 
     pub fn new(name: gl::GLuint, texture_target: TextureTarget) -> TextureId {
         TextureId {
-            name: name,
+            name,
             target: texture_target.to_gl_target(),
         }
     }
 
     pub fn invalid() -> TextureId {
         TextureId {
             name: 0,
             target: gl::TEXTURE_2D,
@@ -483,16 +484,19 @@ pub struct FBOId(gl::GLuint);
 pub struct VBOId(gl::GLuint);
 
 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
 struct IBOId(gl::GLuint);
 
 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
 pub struct UBOId(gl::GLuint);
 
+#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
+pub struct PBOId(gl::GLuint);
+
 const MAX_EVENTS_PER_FRAME: usize = 256;
 const MAX_PROFILE_FRAMES: usize = 4;
 
 pub trait NamedTag {
     fn get_label(&self) -> &str;
 }
 
 #[derive(Debug, Clone)]
@@ -512,28 +516,28 @@ pub struct GpuFrameProfile<T> {
 }
 
 impl<T> GpuFrameProfile<T> {
     fn new(gl: Rc<gl::Gl>) -> Self {
         match gl.get_type() {
             gl::GlType::Gl => {
                 let queries = gl.gen_queries(MAX_EVENTS_PER_FRAME as gl::GLint);
                 GpuFrameProfile {
-                    gl: gl,
-                    queries: queries,
+                    gl,
+                    queries,
                     samples: Vec::new(),
                     next_query: 0,
                     pending_query: 0,
                     frame_id: FrameId(0),
                     inside_frame: false,
                 }
             }
             gl::GlType::Gles => {
                 GpuFrameProfile {
-                    gl: gl,
+                    gl,
                     queries: Vec::new(),
                     samples: Vec::new(),
                     next_query: 0,
                     pending_query: 0,
                     frame_id: FrameId(0),
                     inside_frame: false,
                 }
             }
@@ -580,32 +584,32 @@ impl<T> GpuFrameProfile<T> {
         }
 
         let marker = GpuMarker::new(&self.gl, tag.get_label());
 
         if self.next_query < MAX_EVENTS_PER_FRAME {
             self.pending_query = self.queries[self.next_query];
             self.gl.begin_query(gl::TIME_ELAPSED, self.pending_query);
             self.samples.push(GpuSample {
-                tag: tag,
+                tag,
                 time_ns: 0,
             });
         } else {
             self.pending_query = 0;
         }
 
         self.next_query += 1;
         marker
     }
 
     fn add_marker_gles(&mut self, tag: T) -> GpuMarker
     where T: NamedTag {
         let marker = GpuMarker::new(&self.gl, tag.get_label());
         self.samples.push(GpuSample {
-            tag: tag,
+            tag,
             time_ns: 0,
         });
         marker
     }
 
     fn is_valid(&self) -> bool {
         self.next_query > 0 && self.next_query <= MAX_EVENTS_PER_FRAME
     }
@@ -810,17 +814,17 @@ impl FileWatcherThread {
                             }
                         }
                     }
                 }
             }
         });
 
         FileWatcherThread {
-            api_tx: api_tx,
+            api_tx,
         }
     }
 
     fn exit(&self) {
         self.api_tx.send(FileWatcherCmd::Exit).ok();
     }
 
     fn add_watch(&self, path: PathBuf) {
@@ -841,16 +845,17 @@ pub enum ShaderError {
 }
 
 pub struct Device {
     gl: Rc<gl::Gl>,
     // device state
     bound_textures: [TextureId; 16],
     bound_program: ProgramId,
     bound_vao: VAOId,
+    bound_pbo: PBOId,
     bound_read_fbo: FBOId,
     bound_draw_fbo: FBOId,
     default_read_fbo: gl::GLuint,
     default_draw_fbo: gl::GLuint,
     device_pixel_ratio: f32,
 
     // HW or API capabilties
     capabilities: Capabilities,
@@ -887,46 +892,47 @@ impl Device {
 
         let shader_preamble = get_shader_source(SHADER_PREAMBLE, &resource_override_path);
         //file_watcher.add_watch(resource_path);
 
         let max_ubo_size = gl.get_integer_v(gl::MAX_UNIFORM_BLOCK_SIZE) as usize;
         let max_texture_size = gl.get_integer_v(gl::MAX_TEXTURE_SIZE) as u32;
 
         Device {
-            gl: gl,
-            resource_override_path: resource_override_path,
+            gl,
+            resource_override_path,
             // This is initialized to 1 by default, but it is set
             // every frame by the call to begin_frame().
             device_pixel_ratio: 1.0,
             inside_frame: false,
 
             capabilities: Capabilities {
-                max_ubo_size: max_ubo_size,
+                max_ubo_size,
                 supports_multisampling: false, //TODO
             },
 
             bound_textures: [ TextureId::invalid(); 16 ],
             bound_program: ProgramId(0),
             bound_vao: VAOId(0),
+            bound_pbo: PBOId(0),
             bound_read_fbo: FBOId(0),
             bound_draw_fbo: FBOId(0),
             default_read_fbo: 0,
             default_draw_fbo: 0,
 
             textures: HashMap::default(),
             programs: HashMap::default(),
             vaos: HashMap::default(),
 
-            shader_preamble: shader_preamble,
+            shader_preamble,
 
             next_vao_id: 1,
             //file_watcher: file_watcher,
 
-            max_texture_size: max_texture_size,
+            max_texture_size,
             frame_id: FrameId(0),
         }
     }
 
     pub fn gl(&self) -> &gl::Gl {
         &*self.gl
     }
 
@@ -1001,16 +1007,18 @@ impl Device {
         self.clear_vertex_array();
 
         // FBO state
         self.bound_read_fbo = FBOId(self.default_read_fbo);
         self.bound_draw_fbo = FBOId(self.default_draw_fbo);
 
         // Pixel op state
         self.gl.pixel_store_i(gl::UNPACK_ALIGNMENT, 1);
+        self.bound_pbo = PBOId(0);
+        self.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, 0);
 
         // Default is sampler 0, always
         self.gl.active_texture(gl::TEXTURE0);
 
         self.frame_id
     }
 
     pub fn bind_texture(&mut self,
@@ -1084,17 +1092,17 @@ impl Device {
         for id in id_list {
             let texture_id = TextureId {
                 name: id,
                 target: target.to_gl_target(),
             };
 
             let texture = Texture {
                 gl: Rc::clone(&self.gl),
-                id: id,
+                id,
                 width: 0,
                 height: 0,
                 format: ImageFormat::Invalid,
                 filter: TextureFilter::Nearest,
                 mode: RenderTargetMode::None,
                 fbo_ids: vec![],
             };
 
@@ -1433,17 +1441,17 @@ impl Device {
         let program = Program {
             gl: Rc::clone(&self.gl),
             name: base_filename.to_owned(),
             id: pid,
             u_transform: -1,
             u_device_pixel_ratio: -1,
             vs_source: get_shader_source(&vs_name, &self.resource_override_path),
             fs_source: get_shader_source(&fs_name, &self.resource_override_path),
-            prefix: prefix,
+            prefix,
             vs_id: None,
             fs_id: None,
         };
 
         let program_id = ProgramId(pid);
 
         debug_assert!(self.programs.contains_key(&program_id) == false);
         self.programs.insert(program_id, program);
@@ -1551,21 +1559,16 @@ impl Device {
             self.gl.uniform_1i(u_layers, TextureSampler::Layers as i32);
         }
 
         let u_tasks = self.gl.get_uniform_location(program.id, "sRenderTasks");
         if u_tasks != -1 {
             self.gl.uniform_1i(u_tasks, TextureSampler::RenderTasks as i32);
         }
 
-        let u_data32 = self.gl.get_uniform_location(program.id, "sData32");
-        if u_data32 != -1 {
-            self.gl.uniform_1i(u_data32, TextureSampler::Data32 as i32);
-        }
-
         let u_resource_cache = self.gl.get_uniform_location(program.id, "sResourceCache");
         if u_resource_cache != -1 {
             self.gl.uniform_1i(u_resource_cache, TextureSampler::ResourceCache as i32);
         }
 
         Ok(())
     }
 
@@ -1623,16 +1626,79 @@ impl Device {
                     device_pixel_ratio: f32) {
         debug_assert!(self.inside_frame);
         self.gl.uniform_matrix_4fv(program.u_transform,
                                false,
                                &transform.to_row_major_array());
         self.gl.uniform_1f(program.u_device_pixel_ratio, device_pixel_ratio);
     }
 
+    pub fn create_pbo(&mut self) -> PBOId {
+        let id = self.gl.gen_buffers(1)[0];
+        PBOId(id)
+    }
+
+    pub fn destroy_pbo(&mut self, id: PBOId) {
+        self.gl.delete_buffers(&[id.0]);
+    }
+
+    pub fn bind_pbo(&mut self, pbo_id: Option<PBOId>) {
+        debug_assert!(self.inside_frame);
+        let pbo_id = pbo_id.unwrap_or(PBOId(0));
+
+        if self.bound_pbo != pbo_id {
+            self.bound_pbo = pbo_id;
+
+            self.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, pbo_id.0);
+        }
+    }
+
+    pub fn update_pbo_data<T>(&mut self, data: &[T]) {
+        debug_assert!(self.inside_frame);
+        debug_assert!(self.bound_pbo.0 != 0);
+
+        gl::buffer_data(&*self.gl,
+                        gl::PIXEL_UNPACK_BUFFER,
+                        data,
+                        gl::STREAM_DRAW);
+    }
+
+    pub fn orphan_pbo(&mut self, new_size: usize) {
+        debug_assert!(self.inside_frame);
+        debug_assert!(self.bound_pbo.0 != 0);
+
+        self.gl.buffer_data_untyped(gl::PIXEL_UNPACK_BUFFER,
+                                    new_size as isize,
+                                    ptr::null(),
+                                    gl::STREAM_DRAW);
+    }
+
+    pub fn update_texture_from_pbo(&mut self,
+                                   texture_id: TextureId,
+                                   x0: u32,
+                                   y0: u32,
+                                   width: u32,
+                                   height: u32,
+                                   offset: usize) {
+        debug_assert!(self.inside_frame);
+        debug_assert_eq!(self.textures.get(&texture_id).unwrap().format, ImageFormat::RGBAF32);
+
+        self.bind_texture(DEFAULT_TEXTURE, texture_id);
+
+        self.gl.tex_sub_image_2d_pbo(texture_id.target,
+                                     0,
+                                     x0 as gl::GLint,
+                                     y0 as gl::GLint,
+                                     width as gl::GLint,
+                                     height as gl::GLint,
+                                     gl::RGBA,
+                                     gl::FLOAT,
+                                     offset);
+    }
+
     pub fn update_texture(&mut self,
                           texture_id: TextureId,
                           x0: u32,
                           y0: u32,
                           width: u32,
                           height: u32,
                           stride: Option<u32>,
                           data: &[u8]) {
@@ -1723,23 +1789,23 @@ impl Device {
         self.gl.bind_vertex_array(vao_id);
 
         format.bind(self.gl(), main_vbo_id, instance_vbo_id, vertex_offset, instance_stride);
         ibo_id.bind(self.gl()); // force it to be a part of VAO
 
         let vao = VAO {
             gl: Rc::clone(&self.gl),
             id: vao_id,
-            ibo_id: ibo_id,
-            main_vbo_id: main_vbo_id,
-            instance_vbo_id: instance_vbo_id,
-            instance_stride: instance_stride,
-            owns_indices: owns_indices,
-            owns_vertices: owns_vertices,
-            owns_instances: owns_instances,
+            ibo_id,
+            main_vbo_id,
+            instance_vbo_id,
+            instance_stride,
+            owns_indices,
+            owns_vertices,
+            owns_instances,
         };
 
         self.gl.bind_vertex_array(0);
 
         let vao_id = VAOId(vao_id);
 
         debug_assert!(!self.vaos.contains_key(&vao_id));
         self.vaos.insert(vao_id, vao);
--- a/gfx/webrender/src/ellipse.rs
+++ b/gfx/webrender/src/ellipse.rs
@@ -1,13 +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/. */
 
-use webrender_traits::{LayerPoint, LayerSize};
+use api::{LayerPoint, LayerSize};
 use std::f32::consts::FRAC_PI_2;
 
 /// Number of steps to integrate arc length over.
 const STEP_COUNT: usize = 20;
 
 /// Represents an ellipse centred at a local space origin.
 #[derive(Debug, Clone)]
 pub struct Ellipse {
@@ -18,18 +18,18 @@ pub struct Ellipse {
 impl Ellipse {
     pub fn new(radius: LayerSize) -> Ellipse {
         // Approximate the total length of the first quadrant of this ellipse.
         let total_arc_length = get_simpson_length(FRAC_PI_2,
                                                   radius.width,
                                                   radius.height);
 
         Ellipse {
-            radius: radius,
-            total_arc_length: total_arc_length,
+            radius,
+            total_arc_length,
         }
     }
 
     /// Binary search to estimate the angle of an ellipse
     /// for a given arc length. This only searches over the
     /// first quadrant of an ellipse.
     pub fn find_angle_for_arc_length(&self, arc_length: f32) -> f32 {
         // Clamp arc length to [0, pi].
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -1,36 +1,36 @@
 /* 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 api::{BuiltDisplayList, BuiltDisplayListIter, ClipAndScrollInfo, ClipId, ColorF};
+use api::{ComplexClipRegion, DeviceUintRect, DeviceUintSize, DisplayItemRef, Epoch, FilterOp};
+use api::{ImageDisplayItem, ItemRange, LayerPoint, LayerRect, LayerSize, LayerToScrollTransform};
+use api::{LayerVector2D, LayoutSize, LayoutTransform, LocalClip, MixBlendMode, PipelineId};
+use api::{ScrollClamping, ScrollEventPhase, ScrollLayerState, ScrollLocation, ScrollPolicy};
+use api::{SpecificDisplayItem, StackingContext, TileOffset, TransformStyle, WorldPoint};
 use app_units::Au;
+use clip_scroll_tree::{ClipScrollTree, ScrollStates};
 use euclid::rect;
 use fnv::FnvHasher;
 use gpu_cache::GpuCache;
 use internal_types::{ANGLE_FLOAT_TO_FIXED, AxisDirection};
 use internal_types::{LowLevelFilterOp};
 use internal_types::{RendererFrame};
 use frame_builder::{FrameBuilder, FrameBuilderConfig};
-use clip_scroll_tree::{ClipScrollTree, ScrollStates};
+use mask_cache::ClipRegion;
 use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters};
 use resource_cache::ResourceCache;
 use scene::{Scene, SceneProperties};
 use std::cmp;
 use std::collections::HashMap;
 use std::hash::BuildHasherDefault;
 use tiling::{CompositeOps, DisplayListMap, PrimitiveFlags};
 use util::{ComplexClipRegionHelpers, subtract_rect};
-use webrender_traits::{BuiltDisplayList, BuiltDisplayListIter, ClipAndScrollInfo, ClipDisplayItem};
-use webrender_traits::{ClipId, ClipRegion, ColorF, DeviceUintRect, DeviceUintSize, DisplayItemRef};
-use webrender_traits::{Epoch, FilterOp, ImageDisplayItem, ItemRange, LayerPoint, LayerRect};
-use webrender_traits::{LayerSize, LayerToScrollTransform, LayoutSize, LayoutTransform, LayerVector2D};
-use webrender_traits::{MixBlendMode, PipelineId, ScrollClamping, ScrollEventPhase};
-use webrender_traits::{ScrollLayerState, ScrollLocation, ScrollPolicy, SpecificDisplayItem};
-use webrender_traits::{StackingContext, TileOffset, TransformStyle, 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 };
 
 /// Nested display lists cause two types of replacements to ClipIds inside the nesting:
 ///     1. References to the root scroll frame are replaced by the ClipIds that
@@ -92,19 +92,19 @@ struct FlattenContext<'a> {
 }
 
 impl<'a> FlattenContext<'a> {
     fn new(scene: &'a Scene,
            builder: &'a mut FrameBuilder,
            resource_cache: &'a mut ResourceCache)
            -> FlattenContext<'a> {
         FlattenContext {
-            scene: scene,
-            builder: builder,
-            resource_cache: resource_cache,
+            scene,
+            builder,
+            resource_cache,
             replacements: Vec::new(),
             nested_display_list_info: Vec::new(),
             current_nested_display_list_index: 0,
         }
     }
 
     fn push_nested_display_list_ids(&mut self, info: ClipAndScrollInfo) {
         self.current_nested_display_list_index += 1;
@@ -147,16 +147,30 @@ impl<'a> FlattenContext<'a> {
     /// position of a node. We can eventually remove this when clients start handling
     /// reference frames themselves. This method applies these replacements.
     fn apply_scroll_frame_id_replacement(&self, id: ClipId) -> ClipId {
         match self.replacements.last() {
             Some(&(to_replace, replacement)) if to_replace == id => replacement,
             _ => id,
         }
     }
+
+    fn get_complex_clips(&self,
+                         pipeline_id: PipelineId,
+                         complex_clips: ItemRange<ComplexClipRegion>)
+                         -> Vec<ComplexClipRegion> {
+        if complex_clips.is_empty() {
+            return vec![];
+        }
+
+        self.scene.display_lists.get(&pipeline_id)
+                                .expect("No display list?")
+                                .get(complex_clips)
+                                .collect()
+    }
 }
 
 // TODO: doc
 pub struct Frame {
     pub clip_scroll_tree: ClipScrollTree,
     pub pipeline_epoch_map: HashMap<PipelineId, Epoch, BuildHasherDefault<FnvHasher>>,
     id: FrameId,
     frame_builder_config: FrameBuilderConfig,
@@ -222,32 +236,16 @@ impl StackingContextHelpers for Stacking
                     filters.push(LowLevelFilterOp::Sepia(Au::from_f32_px(amount)));
                 }
             }
         }
         filters
     }
 }
 
-fn clip_intersection(original_rect: &LayerRect,
-                     region: &ClipRegion,
-                     display_list: &BuiltDisplayList)
-                     -> Option<LayerRect> {
-    if region.image_mask.is_some() {
-        return None;
-    }
-    let clips = display_list.get(region.complex_clips);
-    let base_rect = region.main.intersection(original_rect);
-    clips.fold(base_rect, |inner_combined, ccr| {
-        inner_combined.and_then(|combined| {
-            ccr.get_inner_rect_full().and_then(|ir| ir.intersection(&combined))
-        })
-    })
-}
-
 impl Frame {
     pub fn new(config: FrameBuilderConfig) -> Frame {
         Frame {
             pipeline_epoch_map: HashMap::default(),
             clip_scroll_tree: ClipScrollTree::new(),
             id: FrameId(0),
             frame_builder: None,
             frame_builder_config: config,
@@ -352,36 +350,49 @@ impl Frame {
 
         self.frame_builder = Some(frame_builder);
         self.clip_scroll_tree.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
     }
 
     fn flatten_clip<'a>(&mut self,
                         context: &mut FlattenContext,
                         pipeline_id: PipelineId,
-                        parent_id: ClipId,
-                        item: &ClipDisplayItem,
-                        content_rect: &LayerRect,
-                        clip: &ClipRegion) {
-        let clip_viewport = LayerRect::new(content_rect.origin, clip.main.size);
-        let new_clip_id = self.clip_scroll_tree.generate_new_clip_id(pipeline_id);
-        context.builder.add_clip_scroll_node(new_clip_id,
-                                             parent_id,
-                                             pipeline_id,
-                                             &clip_viewport,
-                                             clip,
-                                             &mut self.clip_scroll_tree);
-        let new_id = context.convert_new_id_to_neested(&item.id);
-        context.builder.add_scroll_frame(new_id,
-                                         new_clip_id,
+                        parent_id: &ClipId,
+                        new_clip_id: &ClipId,
+                        clip_region: ClipRegion) {
+        let new_clip_id = context.convert_new_id_to_neested(new_clip_id);
+        context.builder.add_clip_node(new_clip_id,
+                                      *parent_id,
+                                      pipeline_id,
+                                      clip_region,
+                                      &mut self.clip_scroll_tree);
+    }
+
+    fn flatten_scroll_frame<'a>(&mut self,
+                                context: &mut FlattenContext,
+                                pipeline_id: PipelineId,
+                                parent_id: &ClipId,
+                                new_scroll_frame_id: &ClipId,
+                                frame_rect: &LayerRect,
+                                content_rect: &LayerRect,
+                                clip_region: ClipRegion) {
+        let clip_id = self.clip_scroll_tree.generate_new_clip_id(pipeline_id);
+        context.builder.add_clip_node(clip_id,
+                                      *parent_id,
+                                      pipeline_id,
+                                      clip_region,
+                                      &mut self.clip_scroll_tree);
+
+        let new_scroll_frame_id = context.convert_new_id_to_neested(new_scroll_frame_id);
+        context.builder.add_scroll_frame(new_scroll_frame_id,
+                                         clip_id,
                                          pipeline_id,
-                                         &content_rect,
-                                         &clip_viewport,
+                                         &frame_rect,
+                                         &content_rect.size,
                                          &mut self.clip_scroll_tree);
-
     }
 
     fn flatten_stacking_context<'a>(&mut self,
                                     traversal: &mut BuiltDisplayListIter<'a>,
                                     pipeline_id: PipelineId,
                                     context: &mut FlattenContext,
                                     context_scroll_node_id: ClipId,
                                     mut reference_frame_relative_offset: LayerVector2D,
@@ -444,17 +455,16 @@ impl Frame {
             reference_frame_relative_offset = LayerVector2D::new(
                 reference_frame_relative_offset.x + bounds.origin.x,
                 reference_frame_relative_offset.y + bounds.origin.y);
         }
 
         context.builder.push_stacking_context(&reference_frame_relative_offset,
                                               pipeline_id,
                                               composition_operations,
-                                              *bounds,
                                               stacking_context.transform_style);
 
         self.flatten_items(traversal,
                            pipeline_id,
                            context,
                            reference_frame_relative_offset);
 
         if stacking_context.scroll_policy == ScrollPolicy::Fixed {
@@ -468,17 +478,16 @@ impl Frame {
 
         context.builder.pop_stacking_context();
     }
 
     fn flatten_iframe<'a>(&mut self,
                           pipeline_id: PipelineId,
                           parent_id: ClipId,
                           bounds: &LayerRect,
-                          clip_region: &ClipRegion,
                           context: &mut FlattenContext,
                           reference_frame_relative_offset: LayerVector2D) {
         let pipeline = match context.scene.pipeline_map.get(&pipeline_id) {
             Some(pipeline) => pipeline,
             None => return,
         };
 
         let display_list = match context.scene.display_lists.get(&pipeline_id) {
@@ -489,37 +498,29 @@ impl Frame {
         self.pipeline_epoch_map.insert(pipeline_id, pipeline.epoch);
 
         let iframe_rect = LayerRect::new(LayerPoint::zero(), bounds.size);
         let transform = LayerToScrollTransform::create_translation(
             reference_frame_relative_offset.x + bounds.origin.x,
             reference_frame_relative_offset.y + bounds.origin.y,
             0.0);
 
-        let new_clip_id = self.clip_scroll_tree.generate_new_clip_id(pipeline_id);
-        context.builder.add_clip_scroll_node(new_clip_id,
-                                             parent_id,
-                                             parent_id.pipeline_id(),
-                                             bounds,
-                                             clip_region,
-                                             &mut self.clip_scroll_tree);
-
         let iframe_reference_frame_id =
-            context.builder.push_reference_frame(Some(new_clip_id),
+            context.builder.push_reference_frame(Some(parent_id),
                                                  pipeline_id,
                                                  &iframe_rect,
                                                  &transform,
                                                  &mut self.clip_scroll_tree);
 
         context.builder.add_scroll_frame(
             ClipId::root_scroll_node(pipeline_id),
             iframe_reference_frame_id,
             pipeline_id,
-            &LayerRect::new(LayerPoint::zero(), pipeline.content_size),
             &iframe_rect,
+            &pipeline.content_size,
             &mut self.clip_scroll_tree);
 
         self.flatten_root(&mut display_list.iter(), pipeline_id, context, &pipeline.content_size);
 
         context.builder.pop_reference_frame();
     }
 
     fn flatten_item<'a, 'b>(&mut self,
@@ -534,136 +535,121 @@ impl Frame {
         let unreplaced_scroll_id = clip_and_scroll.scroll_node_id;
         clip_and_scroll.scroll_node_id =
             context.apply_scroll_frame_id_replacement(clip_and_scroll.scroll_node_id);
 
         match *item.item() {
             SpecificDisplayItem::WebGL(ref info) => {
                 context.builder.add_webgl_rectangle(clip_and_scroll,
                                                     item.rect(),
-                                                    item.clip_region(),
+                                                    item.local_clip(),
                                                     info.context_id);
             }
             SpecificDisplayItem::Image(ref info) => {
                 let image = context.resource_cache.get_image_properties(info.image_key);
                 if let Some(tile_size) = image.tiling {
                     // The image resource is tiled. We have to generate an image primitive
                     // for each tile.
                     let image_size = DeviceUintSize::new(image.descriptor.width,
                                                          image.descriptor.height);
                     self.decompose_image(clip_and_scroll,
                                          context,
                                          &item.rect(),
-                                         item.clip_region(),
+                                         item.local_clip(),
                                          info,
                                          image_size,
                                          tile_size as u32);
                 } else {
                     context.builder.add_image(clip_and_scroll,
                                               item.rect(),
-                                              item.clip_region(),
+                                              item.local_clip(),
                                               &info.stretch_size,
                                               &info.tile_spacing,
                                               None,
                                               info.image_key,
                                               info.image_rendering,
                                               None);
                 }
             }
             SpecificDisplayItem::YuvImage(ref info) => {
                 context.builder.add_yuv_image(clip_and_scroll,
                                               item.rect(),
-                                              item.clip_region(),
+                                              item.local_clip(),
                                               info.yuv_data,
                                               info.color_space,
                                               info.image_rendering);
             }
             SpecificDisplayItem::Text(ref text_info) => {
                 context.builder.add_text(clip_and_scroll,
                                          item.rect(),
-                                         item.clip_region(),
+                                         item.local_clip(),
                                          text_info.font_key,
                                          text_info.size,
                                          text_info.blur_radius,
                                          &text_info.color,
                                          item.glyphs(),
                                          item.display_list().get(item.glyphs()).count(),
                                          text_info.glyph_options);
             }
             SpecificDisplayItem::Rectangle(ref info) => {
-                // Try to extract the opaque inner rectangle out of the clipped primitive.
-                if let Some(opaque_rect) = clip_intersection(&item.rect(),
-                                                             item.clip_region(),
-                                                             item.display_list()) {
-                    let mut results = Vec::new();
-                    subtract_rect(&item.rect(), &opaque_rect, &mut results);
-                    // The inner rectangle is considered opaque within this layer.
-                    // It may still inherit some masking from the clip stack.
+                if !self.try_to_add_rectangle_splitting_on_clip(context,
+                                                                &item.rect(),
+                                                                item.local_clip(),
+                                                                &info.color,
+                                                                &clip_and_scroll) {
                     context.builder.add_solid_rectangle(clip_and_scroll,
-                                                        &opaque_rect,
-                                                        &ClipRegion::simple(&item.clip_region().main),
+                                                        &item.rect(),
+                                                        item.local_clip(),
                                                         &info.color,
                                                         PrimitiveFlags::None);
-                    for transparent_rect in &results {
-                        context.builder.add_solid_rectangle(clip_and_scroll,
-                                                            transparent_rect,
-                                                            item.clip_region(),
-                                                            &info.color,
-                                                            PrimitiveFlags::None);
-                    }
-                } else {
-                    context.builder.add_solid_rectangle(clip_and_scroll,
-                                                        &item.rect(),
-                                                        item.clip_region(),
-                                                        &info.color,
-                                                        PrimitiveFlags::None);
+
                 }
             }
             SpecificDisplayItem::Gradient(ref info) => {
                 context.builder.add_gradient(clip_and_scroll,
                                              item.rect(),
-                                             item.clip_region(),
+                                             item.local_clip(),
                                              info.gradient.start_point,
                                              info.gradient.end_point,
                                              item.gradient_stops(),
                                              item.display_list()
                                                  .get(item.gradient_stops()).count(),
                                              info.gradient.extend_mode,
                                              info.tile_size,
                                              info.tile_spacing);
             }
             SpecificDisplayItem::RadialGradient(ref info) => {
                 context.builder.add_radial_gradient(clip_and_scroll,
                                                     item.rect(),
-                                                    item.clip_region(),
+                                                    item.local_clip(),
                                                     info.gradient.start_center,
                                                     info.gradient.start_radius,
                                                     info.gradient.end_center,
                                                     info.gradient.end_radius,
                                                     info.gradient.ratio_xy,
                                                     item.gradient_stops(),
                                                     info.gradient.extend_mode,
                                                     info.tile_size,
                                                     info.tile_spacing);
             }
             SpecificDisplayItem::BoxShadow(ref box_shadow_info) => {
                 context.builder.add_box_shadow(clip_and_scroll,
                                                &box_shadow_info.box_bounds,
-                                               item.clip_region(),
+                                               item.local_clip(),
                                                &box_shadow_info.offset,
                                                &box_shadow_info.color,
                                                box_shadow_info.blur_radius,
                                                box_shadow_info.spread_radius,
                                                box_shadow_info.border_radius,
                                                box_shadow_info.clip_mode);
             }
             SpecificDisplayItem::Border(ref info) => {
                 context.builder.add_border(clip_and_scroll,
                                            item.rect(),
-                                           item.clip_region(),
+                                           item.local_clip(),
                                            info,
                                            item.gradient_stops(),
                                            item.display_list()
                                                .get(item.gradient_stops()).count());
             }
             SpecificDisplayItem::PushStackingContext(ref info) => {
                 let mut subtraversal = item.sub_iter();
                 self.flatten_stacking_context(&mut subtraversal,
@@ -675,89 +661,162 @@ impl Frame {
                                               &info.stacking_context,
                                               item.filters());
                 return Some(subtraversal);
             }
             SpecificDisplayItem::Iframe(ref info) => {
                 self.flatten_iframe(info.pipeline_id,
                                     clip_and_scroll.scroll_node_id,
                                     &item.rect(),
-                                    &item.clip_region(),
                                     context,
                                     reference_frame_relative_offset);
             }
             SpecificDisplayItem::Clip(ref info) => {
-                let content_rect = &item.rect().translate(&reference_frame_relative_offset);
+                let complex_clips = context.get_complex_clips(pipeline_id, item.complex_clip().0);
+                let mut clip_region = ClipRegion::for_clip_node(*item.local_clip().clip_rect(),
+                                                                complex_clips,
+                                                                info.image_mask);
+                clip_region.origin += reference_frame_relative_offset;
+
                 self.flatten_clip(context,
                                   pipeline_id,
-                                  clip_and_scroll.scroll_node_id,
-                                  &info,
-                                  &content_rect,
-                                  item.clip_region());
+                                  &clip_and_scroll.scroll_node_id,
+                                  &info.id,
+                                  clip_region);
+            }
+            SpecificDisplayItem::ScrollFrame(ref info) => {
+                let complex_clips = context.get_complex_clips(pipeline_id, item.complex_clip().0);
+                let mut clip_region = ClipRegion::for_clip_node(*item.local_clip().clip_rect(),
+                                                                complex_clips,
+                                                                info.image_mask);
+                clip_region.origin += reference_frame_relative_offset;
+
+                // Just use clip rectangle as the frame rect for this scroll frame.
+                // This is only interesting when calculating scroll extents for the
+                // ClipScrollNode::scroll(..) API
+                let frame_rect = item.local_clip()
+                                     .clip_rect()
+                                     .translate(&reference_frame_relative_offset);
+                let content_rect = item.rect().translate(&reference_frame_relative_offset);
+                self.flatten_scroll_frame(context,
+                                          pipeline_id,
+                                          &clip_and_scroll.scroll_node_id,
+                                          &info.id,
+                                          &frame_rect,
+                                          &content_rect,
+                                          clip_region);
             }
             SpecificDisplayItem::PushNestedDisplayList => {
                 // Using the clip and scroll already processed for nesting here
                 // means that in the case of multiple nested display lists, we
                 // will enter the outermost ids into the table and avoid having
                 // to do a replacement for every level of nesting.
                 context.push_nested_display_list_ids(clip_and_scroll);
             }
             SpecificDisplayItem::PopNestedDisplayList => context.pop_nested_display_list_ids(),
 
             // Do nothing; these are dummy items for the display list parser
-            SpecificDisplayItem::SetGradientStops | SpecificDisplayItem::SetClipRegion(_) => { }
+            SpecificDisplayItem::SetGradientStops => { }
 
             SpecificDisplayItem::PopStackingContext =>
                 unreachable!("Should have returned in parent method."),
         }
         None
     }
 
+    /// Try to optimize the rendering of a solid rectangle that is clipped by a single
+    /// rounded rectangle, by only masking the parts of the rectangle that intersect
+    /// the rounded parts of the clip. This is pretty simple now, so has a lot of
+    /// potential for further optimizations.
+    fn try_to_add_rectangle_splitting_on_clip(&mut self,
+                                              context: &mut FlattenContext,
+                                              rect: &LayerRect,
+                                              local_clip: &LocalClip,
+                                              color: &ColorF,
+                                              clip_and_scroll: &ClipAndScrollInfo)
+                                              -> bool {
+        // If this rectangle is not opaque, splitting the rectangle up
+        // into an inner opaque region just ends up hurting batching and
+        // doing more work than necessary.
+        if color.a != 1.0 {
+            return false;
+        }
+
+        let inner_unclipped_rect = match local_clip {
+            &LocalClip::Rect(_) => return false,
+            &LocalClip::RoundedRect(_, ref region) => region.get_inner_rect_full(),
+        };
+        let inner_unclipped_rect = match inner_unclipped_rect {
+            Some(rect) => rect,
+            None => return false,
+        };
+
+        // The inner rectangle is not clipped by its assigned clipping node, so we can
+        // let it be clipped by the parent of the clipping node, which may result in
+        // less masking some cases.
+        let mut clipped_rects = Vec::new();
+        subtract_rect(rect, &inner_unclipped_rect, &mut clipped_rects);
+
+        context.builder.add_solid_rectangle(*clip_and_scroll,
+                                            &inner_unclipped_rect,
+                                            &LocalClip::from(*local_clip.clip_rect()),
+                                            color,
+                                            PrimitiveFlags::None);
+
+        for clipped_rect in &clipped_rects {
+            context.builder.add_solid_rectangle(*clip_and_scroll,
+                                                clipped_rect,
+                                                local_clip,
+                                                color,
+                                                PrimitiveFlags::None);
+        }
+        true
+    }
+
     fn flatten_root<'a>(&mut self,
                         traversal: &mut BuiltDisplayListIter<'a>,
                         pipeline_id: PipelineId,
                         context: &mut FlattenContext,
                         content_size: &LayoutSize) {
-        let root_bounds = LayerRect::new(LayerPoint::zero(), *content_size);
         context.builder.push_stacking_context(&LayerVector2D::zero(),
                                               pipeline_id,
                                               CompositeOps::default(),
-                                              root_bounds,
                                               TransformStyle::Flat);
 
         // We do this here, rather than above because we want any of the top-level
         // stacking contexts in the display list to be treated like root stacking contexts.
         // FIXME(mrobinson): Currently only the first one will, which for the moment is
         // sufficient for all our use cases.
         context.builder.notify_waiting_for_root_stacking_context();
 
         // For the root pipeline, there's no need to add a full screen rectangle
         // here, as it's handled by the framebuffer clear.
         let clip_id = ClipId::root_scroll_node(pipeline_id);
         if context.scene.root_pipeline_id != Some(pipeline_id) {
             if let Some(pipeline) = context.scene.pipeline_map.get(&pipeline_id) {
                 if let Some(bg_color) = pipeline.background_color {
+                    let root_bounds = LayerRect::new(LayerPoint::zero(), *content_size);
                     context.builder.add_solid_rectangle(ClipAndScrollInfo::simple(clip_id),
                                                         &root_bounds,
-                                                        &ClipRegion::simple(&root_bounds),
+                                                        &LocalClip::from(root_bounds),
                                                         &bg_color,
                                                         PrimitiveFlags::None);
                 }
             }
         }
 
 
         self.flatten_items(traversal, pipeline_id, context, LayerVector2D::zero());
 
         if self.frame_builder_config.enable_scrollbars {
             let scrollbar_rect = LayerRect::new(LayerPoint::zero(), LayerSize::new(10.0, 70.0));
             context.builder.add_solid_rectangle(
                 ClipAndScrollInfo::simple(clip_id),
                 &scrollbar_rect,
-                &ClipRegion::simple(&scrollbar_rect),
+                &LocalClip::from(scrollbar_rect),
                 &DEFAULT_SCROLLBAR_COLOR,
                 PrimitiveFlags::Scrollbar(self.clip_scroll_tree.topmost_scrolling_node_id(), 4.0));
         }
 
         context.builder.pop_stacking_context();
     }
 
     fn flatten_items<'a>(&mut self,
@@ -797,27 +856,27 @@ impl Frame {
     /// an image where the width is too bug but the height is not).
     ///
     /// decompose_image and decompose_image_row handle image repetitions while decompose_tiled_image
     /// takes care of the decomposition required by the internal tiling of the image.
     fn decompose_image(&mut self,
                        clip_and_scroll: ClipAndScrollInfo,
                        context: &mut FlattenContext,
                        item_rect: &LayerRect,
-                       item_clip: &ClipRegion,
+                       item_local_clip: &LocalClip,
                        info: &ImageDisplayItem,
                        image_size: DeviceUintSize,
                        tile_size: u32) {
         let no_vertical_tiling = image_size.height <= tile_size;
         let no_vertical_spacing = info.tile_spacing.height == 0.0;
         if no_vertical_tiling && no_vertical_spacing {
             self.decompose_image_row(clip_and_scroll,
                                      context,
                                      item_rect,
-                                     item_clip,
+                                     item_local_clip,
                                      info,
                                      image_size,
                                      tile_size);
             return;
         }
 
         // Decompose each vertical repetition into rows.
         let layout_stride = info.stretch_size.height + info.tile_spacing.height;
@@ -827,39 +886,39 @@ impl Frame {
                 item_rect.origin.x,
                 item_rect.origin.y + (i as f32) * layout_stride,
                 item_rect.size.width,
                 info.stretch_size.height
             ).intersection(item_rect) {
                 self.decompose_image_row(clip_and_scroll,
                                          context,
                                          &row_rect,
-                                         item_clip,
+                                         item_local_clip,
                                          info,
                                          image_size,
                                          tile_size);
             }
         }
     }
 
     fn decompose_image_row(&mut self,
                            clip_and_scroll: ClipAndScrollInfo,
                            context: &mut FlattenContext,
                            item_rect: &LayerRect,
-                           item_clip: &ClipRegion,
+                           item_local_clip: &LocalClip,
                            info: &ImageDisplayItem,
                            image_size: DeviceUintSize,
                            tile_size: u32) {
         let no_horizontal_tiling = image_size.width <= tile_size;
         let no_horizontal_spacing = info.tile_spacing.width == 0.0;
         if no_horizontal_tiling && no_horizontal_spacing {
             self.decompose_tiled_image(clip_and_scroll,
                                        context,
                                        item_rect,
-                                       item_clip,
+                                       item_local_clip,
                                        info,
                                        image_size,
                                        tile_size);
             return;
         }
 
         // Decompose each horizontal repetition.
         let layout_stride = info.stretch_size.width + info.tile_spacing.width;
@@ -869,29 +928,29 @@ impl Frame {
                 item_rect.origin.x + (i as f32) * layout_stride,
                 item_rect.origin.y,
                 info.stretch_size.width,
                 item_rect.size.height,
             ).intersection(item_rect) {
                 self.decompose_tiled_image(clip_and_scroll,
                                            context,
                                            &decomposed_rect,
-                                           item_clip,
+                                           item_local_clip,
                                            info,
                                            image_size,
                                            tile_size);
             }
         }
     }
 
     fn decompose_tiled_image(&mut self,
                              clip_and_scroll: ClipAndScrollInfo,
                              context: &mut FlattenContext,
                              item_rect: &LayerRect,
-                             item_clip: &ClipRegion,
+                             item_local_clip: &LocalClip,
                              info: &ImageDisplayItem,
                              image_size: DeviceUintSize,
                              tile_size: u32) {
         // The image resource is tiled. We have to generate an image primitive
         // for each tile.
         // We need to do this because the image is broken up into smaller tiles in the texture
         // cache and the image shader is not able to work with this type of sparse representation.
 
@@ -960,76 +1019,76 @@ impl Frame {
         // Zero means the image size is a multiple of the tile size.
         let leftover = DeviceUintSize::new(image_size.width % tile_size, image_size.height % tile_size);
 
         for ty in 0..num_tiles_y {
             for tx in 0..num_tiles_x {
                 self.add_tile_primitive(clip_and_scroll,
                                         context,
                                         item_rect,
-                                        item_clip,
+                                        item_local_clip,
                                         info,
                                         TileOffset::new(tx, ty),
                                         stretched_tile_size,
                                         1.0, 1.0,
                                         repeat_x, repeat_y);
             }
             if leftover.width != 0 {
                 // Tiles on the right edge that are smaller than the tile size.
                 self.add_tile_primitive(clip_and_scroll,
                                         context,
                                         item_rect,
-                                        item_clip,
+                                        item_local_clip,
                                         info,
                                         TileOffset::new(num_tiles_x, ty),
                                         stretched_tile_size,
                                         (leftover.width as f32) / tile_size_f32,
                                         1.0,
                                         repeat_x, repeat_y);
             }
         }
 
         if leftover.height != 0 {
             for tx in 0..num_tiles_x {
                 // Tiles on the bottom edge that are smaller than the tile size.
                 self.add_tile_primitive(clip_and_scroll,
                                         context,
                                         item_rect,
-                                        item_clip,
+                                        item_local_clip,
                                         info,
                                         TileOffset::new(tx, num_tiles_y),
                                         stretched_tile_size,
                                         1.0,
                                         (leftover.height as f32) / tile_size_f32,
                                         repeat_x,
                                         repeat_y);
             }
 
             if leftover.width != 0 {
                 // Finally, the bottom-right tile with a "leftover" size.
                 self.add_tile_primitive(clip_and_scroll,
                                         context,
                                         item_rect,
-                                        item_clip,
+                                        item_local_clip,
                                         info,
                                         TileOffset::new(num_tiles_x, num_tiles_y),
                                         stretched_tile_size,
                                         (leftover.width as f32) / tile_size_f32,
                                         (leftover.height as f32) / tile_size_f32,
                                         repeat_x,
                                         repeat_y);
             }
         }
     }
 
     fn add_tile_primitive(&mut self,
                           clip_and_scroll: ClipAndScrollInfo,
                           context: &mut FlattenContext,
                           item_rect: &LayerRect,
-                          item_clip: &ClipRegion,
+                          item_local_clip: &LocalClip,
                           info: &ImageDisplayItem,
                           tile_offset: TileOffset,
                           stretched_tile_size: LayerSize,
                           tile_ratio_width: f32,
                           tile_ratio_height: f32,
                           repeat_x: bool,
                           repeat_y: bool) {
         // If the the image is tiled along a given axis, we can't have the shader compute
@@ -1063,17 +1122,17 @@ impl Frame {
             assert_eq!(tile_offset.y, 0);
             prim_rect.size.height = item_rect.size.height;
         }
 
         // Fix up the primitive's rect if it overflows the original item rect.
         if let Some(prim_rect) = prim_rect.intersection(item_rect) {
             context.builder.add_image(clip_and_scroll,
                                       prim_rect,
-                                      item_clip,
+                                      item_local_clip,
                                       &stretched_size,
                                       &info.tile_spacing,
                                       None,
                                       info.image_key,
                                       info.image_rendering,
                                       Some(tile_offset));
         }
     }
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -1,46 +1,44 @@
 /* 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 api::{BorderDetails, BorderDisplayItem, BoxShadowClipMode, ClipAndScrollInfo, ClipId, ColorF};
+use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize};
+use api::{ExtendMode, FontKey, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop};
+use api::{ImageKey, ImageRendering, ItemRange, LayerPoint, LayerRect, LayerSize};
+use api::{LayerToScrollTransform, LayerVector2D, LocalClip, PipelineId, RepeatMode, TileOffset};
+use api::{TransformStyle, WebGLContextId, WorldPixel, YuvColorSpace, YuvData};
 use app_units::Au;
 use frame::FrameId;
 use gpu_cache::GpuCache;
-use gpu_store::GpuStoreAddress;
 use internal_types::HardwareCompositeOp;
-use mask_cache::{ClipMode, ClipSource, MaskCacheInfo, RegionMode};
+use mask_cache::{ClipMode, ClipRegion, ClipSource, MaskCacheInfo};
 use plane_split::{BspSplitter, Polygon, Splitter};
 use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu};
 use prim_store::{ImagePrimitiveKind, PrimitiveContainer, PrimitiveIndex};
 use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
 use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu};
 use prim_store::{BoxShadowPrimitiveCpu, TexelRect, YuvImagePrimitiveCpu};
 use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
-use render_task::{AlphaRenderItem, MaskCacheKey, MaskResult, RenderTask, RenderTaskIndex};
+use render_task::{AlphaRenderItem, ClipWorkItem, MaskCacheKey, RenderTask, RenderTaskIndex};
 use render_task::{RenderTaskId, RenderTaskLocation};
 use resource_cache::ResourceCache;
 use clip_scroll_node::{ClipInfo, ClipScrollNode, NodeType};
 use clip_scroll_tree::ClipScrollTree;
 use std::{cmp, f32, i32, mem, usize};
 use std::collections::HashMap;
 use euclid::{SideOffsets2D, vec2, vec3};
 use tiling::{ContextIsolation, StackingContextIndex};
 use tiling::{ClipScrollGroup, ClipScrollGroupIndex, CompositeOps, DisplayListMap, Frame};
 use tiling::{PackedLayer, PackedLayerIndex, PrimitiveFlags, PrimitiveRunCmd, RenderPass};
 use tiling::{RenderTargetContext, RenderTaskCollection, ScrollbarPrimitive, StackingContext};
 use util::{self, pack_as_float, subtract_rect, recycle_vec};
-use util::RectHelpers;
-use webrender_traits::{BorderDetails, BorderDisplayItem, BoxShadowClipMode, ClipAndScrollInfo};
-use webrender_traits::{ClipId, ClipRegion, ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
-use webrender_traits::{DeviceUintRect, DeviceUintSize, ExtendMode, FontKey, FontRenderMode};
-use webrender_traits::{GlyphInstance, GlyphOptions, GradientStop, ImageKey, ImageRendering};
-use webrender_traits::{ItemRange, LayerPoint, LayerRect, LayerSize, LayerToScrollTransform};
-use webrender_traits::{PipelineId, RepeatMode, TileOffset, TransformStyle, WebGLContextId};
-use webrender_traits::{WorldPixel, YuvColorSpace, YuvData, LayerVector2D};
+use util::{MatrixHelpers, RectHelpers};
 
 #[derive(Debug, Clone)]
 struct ImageBorderSegment {
     geom_rect: LayerRect,
     sub_rect: TexelRect,
     stretch_size: LayerSize,
     tile_spacing: LayerSize,
 }
@@ -57,51 +55,51 @@ impl ImageBorderSegment {
 
         let image_size = LayerSize::new(sub_rect.uv1.x - sub_rect.uv0.x,
                                         sub_rect.uv1.y - sub_rect.uv0.y);
 
         let stretch_size_x = match repeat_horizontal {
             RepeatMode::Stretch => rect.size.width,
             RepeatMode::Repeat => image_size.width,
             RepeatMode::Round | RepeatMode::Space => {
-                println!("Round/Space not supported yet!");
+                error!("Round/Space not supported yet!");
                 rect.size.width
             }
         };
 
         let stretch_size_y = match repeat_vertical {
             RepeatMode::Stretch => rect.size.height,
             RepeatMode::Repeat => image_size.height,
             RepeatMode::Round | RepeatMode::Space => {
-                println!("Round/Space not supported yet!");
+                error!("Round/Space not supported yet!");
                 rect.size.height
             }
         };
 
         ImageBorderSegment {
             geom_rect: rect,
-            sub_rect: sub_rect,
+            sub_rect,
             stretch_size: LayerSize::new(stretch_size_x, stretch_size_y),
-            tile_spacing: tile_spacing,
+            tile_spacing,
         }
     }
 }
 
 /// Construct a polygon from stacking context boundaries.
 /// `anchor` here is an index that's going to be preserved in all the
 /// splits of the polygon.
-fn make_polygon(sc: &StackingContext, node: &ClipScrollNode, anchor: usize)
-                -> Polygon<f32, WorldPixel> {
-    //TODO: only work with `sc.local_bounds` worth of space
-    // This can be achieved by moving the `sc.local_bounds.origin` shift
+fn make_polygon(stacking_context: &StackingContext, node: &ClipScrollNode,
+                anchor: usize) -> Polygon<f32, WorldPixel> {
+    //TODO: only work with `isolated_items_bounds.size` worth of space
+    // This can be achieved by moving the `origin` shift
     // from the primitive local coordinates into the layer transformation.
     // Which in turn needs it to be a render task property obeyed by all primitives
     // upon rendering, possibly not limited to `write_*_vertex` implementations.
-    let size = sc.local_bounds.bottom_right();
-    let bounds = LayerRect::new(sc.reference_frame_offset.to_point(), LayerSize::new(size.x, size.y));
+    let size = stacking_context.isolated_items_bounds.bottom_right();
+    let bounds = LayerRect::new(LayerPoint::zero(), LayerSize::new(size.x, size.y));
     Polygon::from_transformed_rect(bounds, node.world_content_transform, anchor)
 }
 
 #[derive(Clone, Copy)]
 pub struct FrameBuilderConfig {
     pub enable_scrollbars: bool,
     pub default_font_render_mode: FontRenderMode,
     pub debug: bool,
@@ -145,35 +143,35 @@ impl FrameBuilder {
                     stacking_context_store: recycle_vec(prev.stacking_context_store),
                     clip_scroll_group_store: recycle_vec(prev.clip_scroll_group_store),
                     cmds: recycle_vec(prev.cmds),
                     packed_layers: recycle_vec(prev.packed_layers),
                     scrollbar_prims: recycle_vec(prev.scrollbar_prims),
                     reference_frame_stack: recycle_vec(prev.reference_frame_stack),
                     stacking_context_stack: recycle_vec(prev.stacking_context_stack),
                     prim_store: prev.prim_store.recycle(),
-                    screen_size: screen_size,
-                    background_color: background_color,
-                    config: config,
+                    screen_size,
+                    background_color,
+                    config,
                     has_root_stacking_context: false,
                 }
             }
             None => {
                 FrameBuilder {
                     stacking_context_store: Vec::new(),
                     clip_scroll_group_store: Vec::new(),
                     cmds: Vec::new(),
                     packed_layers: Vec::new(),
                     scrollbar_prims: Vec::new(),
                     reference_frame_stack: Vec::new(),
                     stacking_context_stack: Vec::new(),
                     prim_store: PrimitiveStore::new(),
-                    screen_size: screen_size,
-                    background_color: background_color,
-                    config: config,
+                    screen_size,
+                    background_color,
+                    config,
                     has_root_stacking_context: false,
                 }
             }
         }
     }
 
     pub fn create_clip_scroll_group_if_necessary(&mut self,
                                                  stacking_context_index: StackingContextIndex,
@@ -185,36 +183,37 @@ impl FrameBuilder {
         let group_index = self.create_clip_scroll_group(stacking_context_index, info);
         let stacking_context = &mut self.stacking_context_store[stacking_context_index.0];
         stacking_context.clip_scroll_groups.push(group_index);
     }
 
     pub fn add_primitive(&mut self,
                          clip_and_scroll: ClipAndScrollInfo,
                          rect: &LayerRect,
-                         clip_region: &ClipRegion,
+                         local_clip: &LocalClip,
                          extra_clips: &[ClipSource],
                          container: PrimitiveContainer)
                          -> PrimitiveIndex {
         let stacking_context_index = *self.stacking_context_stack.last().unwrap();
 
         self.create_clip_scroll_group_if_necessary(stacking_context_index, clip_and_scroll);
 
-        let mut clip_sources = Vec::new();
-        if clip_region.is_complex() {
-            clip_sources.push(ClipSource::Region(clip_region.clone(), RegionMode::ExcludeRect));
+        let mut clip_sources = extra_clips.to_vec();
+        if let &LocalClip::RoundedRect(_, _) = local_clip {
+            clip_sources.push(ClipSource::Region(ClipRegion::for_local_clip(local_clip)))
         }
 
-        clip_sources.extend(extra_clips.iter().cloned());
-
-        let clip_info = MaskCacheInfo::new(&clip_sources,
-                                           &mut self.prim_store.gpu_data32);
+        let clip_info = if !clip_sources.is_empty() {
+            Some(MaskCacheInfo::new(&clip_sources))
+        } else {
+            None
+        };
 
         let prim_index = self.prim_store.add_primitive(rect,
-                                                       &clip_region.main,
+                                                       &local_clip.clip_rect(),
                                                        clip_sources,
                                                        clip_info,
                                                        container);
 
         match self.cmds.last_mut().unwrap() {
             &mut PrimitiveRunCmd::PrimitiveRun(_run_prim_index, ref mut count, run_clip_and_scroll)
                 if run_clip_and_scroll == clip_and_scroll => {
                     debug_assert!(_run_prim_index.0 + *count == prim_index.0);
@@ -233,35 +232,34 @@ impl FrameBuilder {
     pub fn create_clip_scroll_group(&mut self,
                                     stacking_context_index: StackingContextIndex,
                                     info: ClipAndScrollInfo)
                                     -> ClipScrollGroupIndex {
         let packed_layer_index = PackedLayerIndex(self.packed_layers.len());
         self.packed_layers.push(PackedLayer::empty());
 
         self.clip_scroll_group_store.push(ClipScrollGroup {
-            stacking_context_index: stacking_context_index,
+            stacking_context_index,
             scroll_node_id: info.scroll_node_id,
             clip_node_id: info.clip_node_id(),
-            packed_layer_index: packed_layer_index,
+            packed_layer_index,
             screen_bounding_rect: None,
          });
 
         ClipScrollGroupIndex(self.clip_scroll_group_store.len() - 1, info)
     }
 
     pub fn notify_waiting_for_root_stacking_context(&mut self) {
         self.has_root_stacking_context = false;
     }
 
     pub fn push_stacking_context(&mut self,
                                  reference_frame_offset: &LayerVector2D,
                                  pipeline_id: PipelineId,
                                  composite_ops: CompositeOps,
-                                 local_bounds: LayerRect,
                                  transform_style: TransformStyle) {
         if let Some(parent_index) = self.stacking_context_stack.last() {
             let parent_is_root = self.stacking_context_store[parent_index.0].is_page_root;
 
             if composite_ops.mix_blend_mode.is_some() && !parent_is_root {
                 // the parent stacking context of a stacking context with mix-blend-mode
                 // must be drawn with a transparent background, unless the parent stacking context
                 // is the root of the page
@@ -274,17 +272,16 @@ impl FrameBuilder {
         }
 
         let stacking_context_index = StackingContextIndex(self.stacking_context_store.len());
         let reference_frame_id = self.current_reference_frame_id();
         self.stacking_context_store.push(StackingContext::new(pipeline_id,
                                                               *reference_frame_offset,
                                                               !self.has_root_stacking_context,
                                                               reference_frame_id,
-                                                              local_bounds,
                                                               transform_style,
                                                               composite_ops));
         self.has_root_stacking_context = true;
         self.cmds.push(PrimitiveRunCmd::PushStackingContext(stacking_context_index));
         self.stacking_context_stack.push(stacking_context_index);
     }
 
     pub fn pop_stacking_context(&mut self) {
@@ -352,98 +349,90 @@ impl FrameBuilder {
         self.push_reference_frame(None, pipeline_id, &viewport_rect, identity, clip_scroll_tree);
 
         let topmost_scrolling_node_id = ClipId::root_scroll_node(pipeline_id);
         clip_scroll_tree.topmost_scrolling_node_id = topmost_scrolling_node_id;
 
         self.add_scroll_frame(topmost_scrolling_node_id,
                               clip_scroll_tree.root_reference_frame_id,
                               pipeline_id,
-                              &LayerRect::new(LayerPoint::zero(), *content_size),
                               &viewport_rect,
+                              content_size,
                               clip_scroll_tree);
 
         topmost_scrolling_node_id
     }
 
-    pub fn add_clip_scroll_node(&mut self,
-                                new_node_id: ClipId,
-                                parent_id: ClipId,
-                                pipeline_id: PipelineId,
-                                content_rect: &LayerRect,
-                                clip_region: &ClipRegion,
-                                clip_scroll_tree: &mut ClipScrollTree) {
-        let clip_info = ClipInfo::new(clip_region,
-                                      &mut self.prim_store.gpu_data32,
-                                      PackedLayerIndex(self.packed_layers.len()));
-        let node = ClipScrollNode::new(pipeline_id,
-                                       parent_id,
-                                       content_rect,
-                                       &clip_region.main,
-                                       clip_info);
-
+    pub fn add_clip_node(&mut self,
+                         new_node_id: ClipId,
+                         parent_id: ClipId,
+                         pipeline_id: PipelineId,
+                         clip_region: ClipRegion,
+                         clip_scroll_tree: &mut ClipScrollTree) {
+        let clip_info = ClipInfo::new(clip_region, PackedLayerIndex(self.packed_layers.len()));
+        let node = ClipScrollNode::new(pipeline_id, parent_id, clip_info);
         clip_scroll_tree.add_node(node, new_node_id);
         self.packed_layers.push(PackedLayer::empty());
     }
 
     pub fn add_scroll_frame(&mut self,
                             new_node_id: ClipId,
                             parent_id: ClipId,
                             pipeline_id: PipelineId,
-                            content_rect: &LayerRect,
                             frame_rect: &LayerRect,
+                            content_size: &LayerSize,
                             clip_scroll_tree: &mut ClipScrollTree) {
         let node = ClipScrollNode::new_scroll_frame(pipeline_id,
                                                     parent_id,
-                                                    content_rect,
-                                                    frame_rect);
+                                                    frame_rect,
+                                                    content_size);
 
         clip_scroll_tree.add_node(node, new_node_id);
     }
 
     pub fn pop_reference_frame(&mut self) {
         self.reference_frame_stack.pop();
     }
 
     pub fn add_solid_rectangle(&mut self,
                                clip_and_scroll: ClipAndScrollInfo,
                                rect: &LayerRect,
-                               clip_region: &ClipRegion,
+                               local_clip: &LocalClip,
                                color: &ColorF,
                                flags: PrimitiveFlags) {
         if color.a == 0.0 {
             return;
         }
 
         let prim = RectanglePrimitive {
             color: *color,
         };
 
         let prim_index = self.add_primitive(clip_and_scroll,
                                             rect,
-                                            clip_region,
+                                            local_clip,
                                             &[],
                                             PrimitiveContainer::Rectangle(prim));
 
         match flags {
             PrimitiveFlags::None => {}
             PrimitiveFlags::Scrollbar(clip_id, border_radius) => {
                 self.scrollbar_prims.push(ScrollbarPrimitive {
-                    prim_index: prim_index,
-                    clip_id: clip_id,
-                    border_radius: border_radius,
+                    prim_index,
+                    clip_id,
+                    border_radius,
                 });
             }
         }
     }
 
     pub fn add_border(&mut self,
                       clip_and_scroll: ClipAndScrollInfo,
                       rect: LayerRect,
-                      clip_region: &ClipRegion,
+                      local_clip: &LocalClip,
                       border_item: &BorderDisplayItem,
                       gradient_stops: ItemRange<GradientStop>,
                       gradient_stops_count: usize) {
         let create_segments = |outset: SideOffsets2D<f32>| {
             // Calculate the modified rect as specific by border-image-outset
             let origin = LayerPoint::new(rect.origin.x - outset.left,
                                          rect.origin.y - outset.top);
             let size = LayerSize::new(rect.size.width + outset.left + outset.right,
@@ -539,16 +528,25 @@ impl FrameBuilder {
 
                     // Bottom left
                     ImageBorderSegment::new(LayerRect::from_floats(bl_outer.x, bl_inner.y, bl_inner.x, bl_outer.y),
                                             TexelRect::new(px0, py2, px1, py3),
                                             RepeatMode::Stretch,
                                             RepeatMode::Stretch),
                 ];
 
+                // Center
+                if border.fill {
+                    segments.push(ImageBorderSegment::new(
+                        LayerRect::from_floats(tl_inner.x, tl_inner.y, tr_inner.x, bl_inner.y),
+                        TexelRect::new(px1, py1, px2, py2),
+                        border.repeat_horizontal,
+                        border.repeat_vertical))
+                }
+
                 // Add edge segments if valid size.
                 if px1 < px2 && py1 < py2 {
                     segments.extend_from_slice(&[
                         // Top
                         ImageBorderSegment::new(LayerRect::from_floats(tl_inner.x, tl_outer.y, tr_inner.x, tl_inner.y),
                                                 TexelRect::new(px1, py0, px2, py1),
                                                 border.repeat_horizontal,
                                                 RepeatMode::Stretch),
@@ -571,55 +569,55 @@ impl FrameBuilder {
                                                 RepeatMode::Stretch,
                                                 border.repeat_vertical),
                     ]);
                 }
 
                 for segment in segments {
                     self.add_image(clip_and_scroll,
                                    segment.geom_rect,
-                                   clip_region,
+                                   local_clip,
                                    &segment.stretch_size,
                                    &segment.tile_spacing,
                                    Some(segment.sub_rect),
                                    border.image_key,
                                    ImageRendering::Auto,
                                    None);
                 }
             }
             BorderDetails::Normal(ref border) => {
                 self.add_normal_border(&rect,
                                        border,
                                        &border_item.widths,
                                        clip_and_scroll,
-                                       clip_region);
+                                       local_clip);
             }
             BorderDetails::Gradient(ref border) => {
                 for segment in create_segments(border.outset) {
                     let segment_rel = segment.origin - rect.origin;
 
                     self.add_gradient(clip_and_scroll,
                                       segment,
-                                      clip_region,
+                                      local_clip,
                                       border.gradient.start_point - segment_rel,
                                       border.gradient.end_point - segment_rel,
                                       gradient_stops,
                                       gradient_stops_count,
                                       border.gradient.extend_mode,
                                       segment.size,
                                       LayerSize::zero());
                 }
             }
             BorderDetails::RadialGradient(ref border) => {
                 for segment in create_segments(border.outset) {
                     let segment_rel = segment.origin - rect.origin;
 
                     self.add_radial_gradient(clip_and_scroll,
                                              segment,
-                                             clip_region,
+                                             local_clip,
                                              border.gradient.start_center - segment_rel,
                                              border.gradient.start_radius,
                                              border.gradient.end_center - segment_rel,
                                              border.gradient.end_radius,
                                              border.gradient.ratio_xy,
                                              gradient_stops,
                                              border.gradient.extend_mode,
                                              segment.size,
@@ -627,17 +625,17 @@ impl FrameBuilder {
                 }
             }
         }
     }
 
     pub fn add_gradient(&mut self,
                         clip_and_scroll: ClipAndScrollInfo,
                         rect: LayerRect,
-                        clip_region: &ClipRegion,
+                        local_clip: &LocalClip,
                         start_point: LayerPoint,
                         end_point: LayerPoint,
                         stops: ItemRange<GradientStop>,
                         stops_count: usize,
                         extend_mode: ExtendMode,
                         tile_size: LayerSize,
                         tile_spacing: LayerSize) {
         let tile_repeat = tile_size + tile_spacing;
@@ -671,73 +669,72 @@ impl FrameBuilder {
         let (sp, ep) = if reverse_stops {
             (end_point, start_point)
         } else {
             (start_point, end_point)
         };
 
         let gradient_cpu = GradientPrimitiveCpu {
             stops_range: stops,
-            stops_count: stops_count,
-            extend_mode: extend_mode,
-            reverse_stops: reverse_stops,
+            stops_count,
+            extend_mode,
+            reverse_stops,
             gpu_blocks: [
                 [sp.x, sp.y, ep.x, ep.y].into(),
                 [tile_size.width, tile_size.height, tile_repeat.width, tile_repeat.height].into(),
                 [pack_as_float(extend_mode as u32), 0.0, 0.0, 0.0].into(),
             ],
         };
 
         let prim = if aligned {
             PrimitiveContainer::AlignedGradient(gradient_cpu)
         } else {
             PrimitiveContainer::AngleGradient(gradient_cpu)
         };
 
-        self.add_primitive(clip_and_scroll, &rect, clip_region, &[], prim);
+        self.add_primitive(clip_and_scroll, &rect, local_clip, &[], prim);
     }
 
     pub fn add_radial_gradient(&mut self,
                                clip_and_scroll: ClipAndScrollInfo,
                                rect: LayerRect,
-                               clip_region: &ClipRegion,
+                               local_clip: &LocalClip,
                                start_center: LayerPoint,
                                start_radius: f32,
                                end_center: LayerPoint,
                                end_radius: f32,
                                ratio_xy: f32,
                                stops: ItemRange<GradientStop>,
                                extend_mode: ExtendMode,
                                tile_size: LayerSize,
                                tile_spacing: LayerSize) {
         let tile_repeat = tile_size + tile_spacing;
 
         let radial_gradient_cpu = RadialGradientPrimitiveCpu {
             stops_range: stops,
-            extend_mode: extend_mode,
-            gpu_data_address: GpuStoreAddress(0),
+            extend_mode,
             gpu_data_count: 0,
             gpu_blocks: [
                 [start_center.x, start_center.y, end_center.x, end_center.y].into(),
                 [start_radius, end_radius, ratio_xy, pack_as_float(extend_mode as u32)].into(),
                 [tile_size.width, tile_size.height, tile_repeat.width, tile_repeat.height].into(),
             ],
         };
 
         self.add_primitive(clip_and_scroll,
                            &rect,
-                           clip_region,
+                           local_clip,
                            &[],
                            PrimitiveContainer::RadialGradient(radial_gradient_cpu));
     }
 
     pub fn add_text(&mut self,
                     clip_and_scroll: ClipAndScrollInfo,
                     rect: LayerRect,
-                    clip_region: &ClipRegion,
+                    local_clip: &LocalClip,
                     font_key: FontKey,
                     size: Au,
                     blur_radius: f32,
                     color: &ColorF,
                     glyph_range: ItemRange<GlyphInstance>,
                     glyph_count: usize,
                     glyph_options: Option<GlyphOptions>) {
         if color.a == 0.0 {
@@ -759,89 +756,88 @@ impl FrameBuilder {
         // There are some conditions under which we can't use
         // subpixel text rendering, even if enabled.
         if render_mode == FontRenderMode::Subpixel {
             // text-blur shadow needs to force alpha AA.
             if blur_radius != 0.0 {
                 render_mode = FontRenderMode::Alpha;
             }
 
+            if color.a != 1.0 {
+                render_mode = FontRenderMode::Alpha;
+            }
+
             // text on a stacking context that has filters
             // (e.g. opacity) can't use sub-pixel.
             // TODO(gw): It's possible we can relax this in
             //           the future, if we modify the way
             //           we handle subpixel blending.
             if let Some(sc_index) = self.stacking_context_stack.last() {
                 let stacking_context = &self.stacking_context_store[sc_index.0];
                 if stacking_context.composite_ops.count() > 0 {
                     render_mode = FontRenderMode::Alpha;
                 }
             }
         }
 
         let prim_cpu = TextRunPrimitiveCpu {
-            font_key: font_key,
+            font_key,
             logical_font_size: size,
-            blur_radius: blur_radius,
-            glyph_range: glyph_range,
-            glyph_count: glyph_count,
+            blur_radius,
+            glyph_range,
+            glyph_count,
             glyph_instances: Vec::new(),
             color: *color,
-            render_mode: render_mode,
-            glyph_options: glyph_options,
+            render_mode,
+            glyph_options,
         };
 
         self.add_primitive(clip_and_scroll,
                            &rect,
-                           clip_region,
+                           local_clip,
                            &[],
                            PrimitiveContainer::TextRun(prim_cpu));
     }
 
     pub fn fill_box_shadow_rect(&mut self,
                                 clip_and_scroll: ClipAndScrollInfo,
                                 box_bounds: &LayerRect,
                                 bs_rect: LayerRect,
-                                clip_region: &ClipRegion,
+                                local_clip: &LocalClip,
                                 color: &ColorF,
                                 border_radius: f32,
                                 clip_mode: BoxShadowClipMode) {
         // We can draw a rectangle instead with the proper border radius clipping.
         let (bs_clip_mode, rect_to_draw) = match clip_mode {
             BoxShadowClipMode::Outset |
             BoxShadowClipMode::None => (ClipMode::Clip, bs_rect),
             BoxShadowClipMode::Inset => (ClipMode::ClipOut, *box_bounds),
         };
 
         let box_clip_mode = !bs_clip_mode;
 
-        // Clip the inside
-        let extra_clips = [ClipSource::Complex(bs_rect,
-                                               border_radius,
-                                               bs_clip_mode),
-                           // Clip the outside of the box
-                           ClipSource::Complex(*box_bounds,
-                                             border_radius,
-                                             box_clip_mode)];
+        // Clip the inside and then the outside of the box.
+        let extra_clips = [ClipSource::Complex(bs_rect, border_radius, bs_clip_mode),
+                           ClipSource::Complex(*box_bounds, border_radius, box_clip_mode)];
 
         let prim = RectanglePrimitive {
             color: *color,
         };
 
         self.add_primitive(clip_and_scroll,
                            &rect_to_draw,
-                           clip_region,
+                           local_clip,
                            &extra_clips,
                            PrimitiveContainer::Rectangle(prim));
     }
 
     pub fn add_box_shadow(&mut self,
                           clip_and_scroll: ClipAndScrollInfo,
                           box_bounds: &LayerRect,
-                          clip_region: &ClipRegion,
+                          local_clip: &LocalClip,
                           box_offset: &LayerVector2D,
                           color: &ColorF,
                           blur_radius: f32,
                           spread_radius: f32,
                           border_radius: f32,
                           clip_mode: BoxShadowClipMode) {
         if color.a == 0.0 {
             return
@@ -861,27 +857,27 @@ impl FrameBuilder {
         // Have to explicitly check this since euclid::TypedRect relies on negative rects
         let bs_rect_empty = bs_rect.size.width <= 0.0 || bs_rect.size.height <= 0.0;
 
         // Just draw a rectangle
         if (blur_radius == 0.0 && spread_radius == 0.0 && clip_mode == BoxShadowClipMode::None)
            || bs_rect_empty {
             self.add_solid_rectangle(clip_and_scroll,
                                      box_bounds,
-                                     clip_region,
+                                     local_clip,
                                      color,
                                      PrimitiveFlags::None);
             return;
         }
 
         if blur_radius == 0.0 && border_radius != 0.0 {
             self.fill_box_shadow_rect(clip_and_scroll,
                                       box_bounds,
                                       bs_rect,
-                                      clip_region,
+                                      local_clip,
                                       color,
                                       border_radius,
                                       clip_mode);
             return;
         }
 
         // Get the outer rectangle, based on the blur radius.
         let outside_edge_size = 2.0 * blur_radius;
@@ -944,28 +940,28 @@ impl FrameBuilder {
             }
         };
 
         match shadow_kind {
             BoxShadowKind::Simple(rects) => {
                 for rect in &rects {
                     self.add_solid_rectangle(clip_and_scroll,
                                              rect,
-                                             clip_region,
+                                             local_clip,
                                              color,
                                              PrimitiveFlags::None)
                 }
             }
             BoxShadowKind::Shadow(rects) => {
                 assert!(blur_radius > 0.0);
                 if clip_mode == BoxShadowClipMode::Inset {
                     self.fill_box_shadow_rect(clip_and_scroll,
                                               box_bounds,
                                               bs_rect,
-                                              clip_region,
+                                              local_clip,
                                               color,
                                               border_radius,
                                               clip_mode);
                 }
 
                 let inverted = match clip_mode {
                     BoxShadowClipMode::Outset | BoxShadowClipMode::None => 0.0,
                     BoxShadowClipMode::Inset => 1.0,
@@ -982,56 +978,56 @@ impl FrameBuilder {
                 if border_radius >= 0.0 {
                     extra_clips.push(ClipSource::Complex(*box_bounds,
                                                 border_radius,
                                                 extra_clip_mode));
                 }
 
                 let prim_cpu = BoxShadowPrimitiveCpu {
                     src_rect: *box_bounds,
-                    bs_rect: bs_rect,
+                    bs_rect,
                     color: *color,
-                    blur_radius: blur_radius,
-                    border_radius: border_radius,
-                    edge_size: edge_size,
-                    inverted: inverted,
-                    rects: rects,
+                    blur_radius,
+                    border_radius,
+                    edge_size,
+                    inverted,
+                    rects,
                 };
 
                 self.add_primitive(clip_and_scroll,
                                    &outer_rect,
-                                   clip_region,
+                                   local_clip,
                                    extra_clips.as_slice(),
                                    PrimitiveContainer::BoxShadow(prim_cpu));
             }
         }
     }
 
     pub fn add_webgl_rectangle(&mut self,
                                clip_and_scroll: ClipAndScrollInfo,
                                rect: LayerRect,
-                               clip_region: &ClipRegion,
+                               local_clip: &LocalClip,
                                context_id: WebGLContextId) {
         let prim_cpu = ImagePrimitiveCpu {
             kind: ImagePrimitiveKind::WebGL(context_id),
             gpu_blocks: [ [rect.size.width, rect.size.height, 0.0, 0.0].into(),
                           TexelRect::invalid().into() ],
         };
 
         self.add_primitive(clip_and_scroll,
                            &rect,
-                           clip_region,
+                           local_clip,
                            &[],
                            PrimitiveContainer::Image(prim_cpu));
     }
 
     pub fn add_image(&mut self,
                      clip_and_scroll: ClipAndScrollInfo,
                      rect: LayerRect,
-                     clip_region: &ClipRegion,
+                     local_clip: &LocalClip,
                      stretch_size: &LayerSize,
                      tile_spacing: &LayerSize,
                      sub_rect: Option<TexelRect>,
                      image_key: ImageKey,
                      image_rendering: ImageRendering,
                      tile: Option<TileOffset>) {
         let sub_rect_block = sub_rect.unwrap_or(TexelRect::invalid()).into();
 
@@ -1045,48 +1041,48 @@ impl FrameBuilder {
                             tile_spacing.width,
                             tile_spacing.height ].into(),
                             sub_rect_block,
                         ],
         };
 
         self.add_primitive(clip_and_scroll,
                            &rect,
-                           clip_region,
+                           local_clip,
                            &[],
                            PrimitiveContainer::Image(prim_cpu));
     }
 
     pub fn add_yuv_image(&mut self,
                          clip_and_scroll: ClipAndScrollInfo,
                          rect: LayerRect,
-                         clip_region: &ClipRegion,
+                         clip_rect: &LocalClip,
                          yuv_data: YuvData,
                          color_space: YuvColorSpace,
                          image_rendering: ImageRendering) {
         let format = yuv_data.get_format();
         let yuv_key = match yuv_data {
             YuvData::NV12(plane_0, plane_1) => [plane_0, plane_1, ImageKey::new(0, 0)],
             YuvData::PlanarYCbCr(plane_0, plane_1, plane_2) =>
                 [plane_0, plane_1, plane_2],
             YuvData::InterleavedYCbCr(plane_0) =>
                 [plane_0, ImageKey::new(0, 0), ImageKey::new(0, 0)],
         };
 
         let prim_cpu = YuvImagePrimitiveCpu {
-            yuv_key: yuv_key,
-            format: format,
-            color_space: color_space,
-            image_rendering: image_rendering,
+            yuv_key,
+            format,
+            color_space,
+            image_rendering,
             gpu_block: [rect.size.width, rect.size.height, 0.0, 0.0].into(),
         };
 
         self.add_primitive(clip_and_scroll,
                            &rect,
-                           clip_region,
+                           clip_rect,
                            &[],
                            PrimitiveContainer::YuvImage(prim_cpu));
     }
 
     /// 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,
@@ -1172,18 +1168,18 @@ impl FrameBuilder {
         // and then actually populate with items and dependencies on the way up.
         let mut alpha_task_stack = Vec::new();
         // A map of "preserve-3d" contexts. We are baking these into render targets
         // and only compositing once we are out of "preserve-3d" hierarchy.
         // The stacking contexts that fall into this category are
         //  - ones with `ContextIsolation::Items`, for their actual items to be backed
         //  - immediate children of `ContextIsolation::Items`
         let mut preserve_3d_map: HashMap<StackingContextIndex, RenderTask> = HashMap::new();
-        // The plane splitter, using a simple BSP tree.
-        let mut splitter = BspSplitter::new();
+        // The plane splitter stack, using a simple BSP tree.
+        let mut splitter_stack = Vec::new();
 
         debug!("build_render_task()");
 
         for cmd in &self.cmds {
             match *cmd {
                 PrimitiveRunCmd::PushStackingContext(stacking_context_index) => {
                     let parent_isolation = sc_stack.last()
                                                    .map(|index| self.stacking_context_store[index.0].isolation);
@@ -1202,28 +1198,32 @@ impl FrameBuilder {
                     if stacking_context.isolation == ContextIsolation::Full && composite_count == 0 {
                         alpha_task_stack.push(current_task);
                         current_task = RenderTask::new_dynamic_alpha_batch(next_task_index, stacking_context_rect);
                         next_task_index.0 += 1;
                     }
 
                     if parent_isolation == Some(ContextIsolation::Items) ||
                        stacking_context.isolation == ContextIsolation::Items {
+                        if parent_isolation != Some(ContextIsolation::Items) {
+                            splitter_stack.push(BspSplitter::new());
+                        }
                         alpha_task_stack.push(current_task);
                         current_task = RenderTask::new_dynamic_alpha_batch(next_task_index, stacking_context_rect);
                         next_task_index.0 += 1;
                         //Note: technically, we shouldn't make a new alpha task for "preserve-3d" contexts
                         // that have no child items (only other stacking contexts). However, we don't know if
                         // there are any items at this time (in `PushStackingContext`).
                         //Note: the reason we add the polygon for splitting during `Push*` as opposed to `Pop*`
                         // is because we need to preserve the order of drawing for planes that match together.
-                        let scroll_node = clip_scroll_tree.nodes.get(&stacking_context.reference_frame_id).unwrap();
-                        let sc_polygon = make_polygon(stacking_context, scroll_node, stacking_context_index.0);
-                        debug!("\tadd {:?} -> {:?}", stacking_context_index, sc_polygon);
-                        splitter.add(sc_polygon);
+                        let frame_node = clip_scroll_tree.nodes.get(&stacking_context.reference_frame_id).unwrap();
+                        let sc_polygon = make_polygon(stacking_context, frame_node, stacking_context_index.0);
+                        debug!("\tsplitter[{}]: add {:?} -> {:?} with bounds {:?}", splitter_stack.len(),
+                            stacking_context_index, sc_polygon, stacking_context.isolated_items_bounds);
+                        splitter_stack.last_mut().unwrap().add(sc_polygon);
                     }
 
                     for _ in 0..composite_count {
                         alpha_task_stack.push(current_task);
                         current_task = RenderTask::new_dynamic_alpha_batch(next_task_index, stacking_context_rect);
                         next_task_index.0 += 1;
                     }
                 }
@@ -1286,53 +1286,55 @@ impl FrameBuilder {
                        stacking_context.isolation == ContextIsolation::Items {
                         //Note: we don't register the dependent tasks here. It's only done
                         // when we are out of the `preserve-3d` branch (see the code below),
                         // since this is only where the parent task is known.
                         preserve_3d_map.insert(stacking_context_index, current_task);
                         current_task = alpha_task_stack.pop().unwrap();
                     }
 
-                    if !preserve_3d_map.is_empty() && parent_isolation != Some(ContextIsolation::Items) {
+                    if parent_isolation != Some(ContextIsolation::Items) &&
+                       stacking_context.isolation == ContextIsolation::Items {
+                        debug!("\tsplitter[{}]: flush {:?}", splitter_stack.len(), current_task.id);
+                        let mut splitter = splitter_stack.pop().unwrap();
                         // Flush the accumulated plane splits onto the task tree.
                         // Notice how this is done before splitting in order to avoid duplicate tasks.
                         current_task.children.extend(preserve_3d_map.values().cloned());
-                        debug!("\tplane splitting in {:?}", current_task.id);
                         // Z axis is directed at the screen, `sort` is ascending, and we need back-to-front order.
                         for poly in splitter.sort(vec3(0.0, 0.0, 1.0)) {
                             let sc_index = StackingContextIndex(poly.anchor);
                             let task_id = preserve_3d_map[&sc_index].id;
                             debug!("\t\tproduce {:?} -> {:?} for {:?}", sc_index, poly, task_id);
                             let pp = &poly.points;
                             let gpu_blocks = [
                                 [pp[0].x, pp[0].y, pp[0].z, pp[1].x].into(),
                                 [pp[1].y, pp[1].z, pp[2].x, pp[2].y].into(),
                                 [pp[2].z, pp[3].x, pp[3].y, pp[3].z].into(),
                             ];
                             let handle = gpu_cache.push_per_frame_blocks(&gpu_blocks);
                             let item = AlphaRenderItem::SplitComposite(sc_index, task_id, handle, next_z);
                             current_task.as_alpha_batch().items.push(item);
                         }
-                        splitter.reset();
                         preserve_3d_map.clear();
                         next_z += 1;
                     }
                 }
                 PrimitiveRunCmd::PrimitiveRun(first_prim_index, prim_count, clip_and_scroll) => {
                     let stacking_context_index = *sc_stack.last().unwrap();
                     let stacking_context = &self.stacking_context_store[stacking_context_index.0];
 
                     if !stacking_context.is_visible {
                         continue;
                     }
 
                     let stacking_context_index = *sc_stack.last().unwrap();
                     let group_index = self.stacking_context_store[stacking_context_index.0]
                                           .clip_scroll_group(clip_and_scroll);
                     if self.clip_scroll_group_store[group_index.0].screen_bounding_rect.is_none() {
+                        debug!("\tcs-group {:?} screen rect is None", group_index);
                         continue
                     }
 
                     debug!("\trun of {} items into {:?}", prim_count, current_task.id);
 
                     for i in 0..prim_count {
                         let prim_index = PrimitiveIndex(first_prim_index.0 + i);
 
@@ -1421,66 +1423,71 @@ impl FrameBuilder {
                                         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 {
-                device_pixel_ratio: device_pixel_ratio,
+                device_pixel_ratio,
                 stacking_context_store: &self.stacking_context_store,
                 clip_scroll_group_store: &self.clip_scroll_group_store,
                 prim_store: &self.prim_store,
-                resource_cache: resource_cache,
+                resource_cache,
             };
 
             pass.build(&ctx, gpu_cache, &mut render_tasks, &mut deferred_resolves);
 
             profile_counters.passes.inc();
             profile_counters.color_targets.add(pass.color_targets.target_count());
             profile_counters.alpha_targets.add(pass.alpha_targets.target_count());
         }
 
         let gpu_cache_updates = gpu_cache.end_frame(gpu_cache_profile);
 
         resource_cache.end_frame();
 
         Frame {
-            device_pixel_ratio: device_pixel_ratio,
+            device_pixel_ratio,
             background_color: self.background_color,
             window_size: self.screen_size,
-            profile_counters: profile_counters,
-            passes: passes,
-            cache_size: cache_size,
+            profile_counters,
+            passes,
+            cache_size,
             layer_texture_data: self.packed_layers.clone(),
             render_task_data: render_tasks.render_task_data,
-            gpu_data32: self.prim_store.gpu_data32.build(),
-            deferred_resolves: deferred_resolves,
+            deferred_resolves,
             gpu_cache_updates: Some(gpu_cache_updates),
         }
     }
 
 }
 
+#[derive(Debug, Clone, Copy)]
+struct LayerClipBounds {
+    outer: DeviceIntRect,
+    inner: DeviceIntRect,
+}
+
 struct LayerRectCalculationAndCullingPass<'a> {
     frame_builder: &'a mut FrameBuilder,
     screen_rect: &'a DeviceIntRect,
     clip_scroll_tree: &'a mut ClipScrollTree,
     display_lists: &'a DisplayListMap,
     resource_cache: &'a mut ResourceCache,
     gpu_cache: &'a mut GpuCache,
     profile_counters: &'a mut FrameProfileCounters,
     device_pixel_ratio: f32,
     stacking_context_stack: Vec<StackingContextIndex>,
 
     /// 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)>,
+    current_clip_stack: Vec<ClipWorkItem>,
 
     /// Information about the cached clip stack, which is used to avoid having
     /// to recalculate it for every primitive.
     current_clip_info: Option<(ClipId, Option<DeviceIntRect>)>
 }
 
 impl<'a> LayerRectCalculationAndCullingPass<'a> {
     fn create_and_run(frame_builder: &'a mut FrameBuilder,
@@ -1488,36 +1495,37 @@ impl<'a> LayerRectCalculationAndCullingP
                       clip_scroll_tree: &'a mut ClipScrollTree,
                       display_lists: &'a DisplayListMap,
                       resource_cache: &'a mut ResourceCache,
                       gpu_cache: &'a mut GpuCache,
                       profile_counters: &'a mut FrameProfileCounters,
                       device_pixel_ratio: f32) {
 
         let mut pass = LayerRectCalculationAndCullingPass {
-            frame_builder: frame_builder,
-            screen_rect: screen_rect,
-            clip_scroll_tree: clip_scroll_tree,
-            display_lists: display_lists,
-            resource_cache: resource_cache,
-            gpu_cache: gpu_cache,
-            profile_counters: profile_counters,
-            device_pixel_ratio: device_pixel_ratio,
+            frame_builder,
+            screen_rect,
+            clip_scroll_tree,
+            display_lists,
+            resource_cache,
+            gpu_cache,
+            profile_counters,
+            device_pixel_ratio,
             stacking_context_stack: Vec::new(),
             current_clip_stack: Vec::new(),
             current_clip_info: None,
         };
         pass.run();
     }
 
     fn run(&mut self) {
+        self.recalculate_clip_scroll_nodes();
         self.recalculate_clip_scroll_groups();
-        self.recalculate_clip_scroll_nodes();
         self.compute_stacking_context_visibility();
 
+        debug!("processing commands...");
         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::PrimitiveRun(prim_index, prim_count, clip_and_scroll) =>
                     self.handle_primitive_run(prim_index, prim_count, clip_and_scroll),
                 PrimitiveRunCmd::PopStackingContext => self.handle_pop_stacking_context(),
@@ -1537,82 +1545,91 @@ impl<'a> LayerRectCalculationAndCullingP
             let packed_layer_index = node_clip_info.packed_layer_index;
             let packed_layer = &mut self.frame_builder.packed_layers[packed_layer_index.0];
 
             // The coordinates of the mask are relative to the origin of the node itself,
             // so we need to account for that origin in the transformation we assign to
             // the packed layer.
             let transform = node.world_viewport_transform
                 .pre_translate(node.local_viewport_rect.origin.to_vector().to_3d());
-            packed_layer.set_transform(transform);
-
-            // Meanwhile, the combined viewport rect is relative to the reference frame, so
-            // we move it into the local coordinate system of the node.
-            let local_viewport_rect =
-                node.combined_local_viewport_rect.translate(&-node.local_viewport_rect.origin.to_vector());
 
-            node_clip_info.screen_bounding_rect = packed_layer.set_rect(&local_viewport_rect,
-                                                                        self.screen_rect,
-                                                                        self.device_pixel_ratio);
+            node_clip_info.screen_bounding_rect = if packed_layer.set_transform(transform) {
+                // Meanwhile, the combined viewport rect is relative to the reference frame, so
+                // we move it into the local coordinate system of the node.
+                let local_viewport_rect = node.combined_local_viewport_rect
+                    .translate(&-node.local_viewport_rect.origin.to_vector());
 
-            let mask_info = match node_clip_info.mask_cache_info {
-                Some(ref mut mask_info) => mask_info,
-                _ => continue,
+                packed_layer.set_rect(&local_viewport_rect,
+                                      self.screen_rect,
+                                      self.device_pixel_ratio)
+            } else {
+                None
             };
 
-            let display_list = self.display_lists.get(&node.pipeline_id)
-                                                 .expect("No display list?");
+            let inner_rect = match node_clip_info.screen_bounding_rect {
+                Some((_, rect)) => rect,
+                None => DeviceIntRect::zero(),
+            };
+            node_clip_info.screen_inner_rect = inner_rect;
 
-            mask_info.update(&node_clip_info.clip_sources,
-                             &packed_layer.transform,
-                             &mut self.frame_builder.prim_store.gpu_data32,
-                             self.device_pixel_ratio,
-                             display_list);
+            let bounds = node_clip_info.mask_cache_info.update(&node_clip_info.clip_sources,
+                                                               &transform,
+                                                               self.gpu_cache,
+                                                               self.device_pixel_ratio);
+
+            node_clip_info.screen_inner_rect = bounds.inner.as_ref()
+               .and_then(|inner| inner.device_rect.intersection(&inner_rect))
+               .unwrap_or(DeviceIntRect::zero());
 
             for clip_source in &node_clip_info.clip_sources {
                 if let Some(mask) = clip_source.image_mask() {
                     // We don't add the image mask for resolution, because
                     // layer masks are resolved later.
                     self.resource_cache.request_image(mask.image, ImageRendering::Auto, None);
                 }
             }
         }
     }
 
     fn recalculate_clip_scroll_groups(&mut self) {
+        debug!("recalculate_clip_scroll_groups");
         for ref mut group in &mut self.frame_builder.clip_scroll_group_store {
             let stacking_context_index = group.stacking_context_index;
             let stacking_context = &mut self.frame_builder
                                             .stacking_context_store[stacking_context_index.0];
 
             let scroll_node = &self.clip_scroll_tree.nodes[&group.scroll_node_id];
             let clip_node = &self.clip_scroll_tree.nodes[&group.clip_node_id];
             let packed_layer = &mut self.frame_builder.packed_layers[group.packed_layer_index.0];
 
             // The world content transform is relative to the containing reference frame,
             // so we translate into the origin of the stacking context itself.
             let transform = scroll_node.world_content_transform
                 .pre_translate(stacking_context.reference_frame_offset.to_3d());
-            packed_layer.set_transform(transform);
 
-            if !stacking_context.can_contribute_to_scene() {
+            if !packed_layer.set_transform(transform) || !stacking_context.can_contribute_to_scene() {
+                debug!("\t{:?} unable to set transform or contribute with {:?}",
+                    stacking_context_index, transform);
                 return;
             }
 
             // Here we move the viewport rectangle into the coordinate system
             // of the stacking context content.
-            let viewport_rect =
-                &clip_node.combined_local_viewport_rect
-                     .translate(&clip_node.reference_frame_relative_scroll_offset)
-                     .translate(&-scroll_node.reference_frame_relative_scroll_offset)
-                     .translate(&-stacking_context.reference_frame_offset)
-                     .translate(&-scroll_node.scroll_offset());
-            group.screen_bounding_rect = packed_layer.set_rect(viewport_rect,
+            let local_viewport_rect = clip_node.combined_local_viewport_rect
+                .translate(&clip_node.reference_frame_relative_scroll_offset)
+                .translate(&-scroll_node.reference_frame_relative_scroll_offset)
+                .translate(&-stacking_context.reference_frame_offset)
+                .translate(&-scroll_node.scroll_offset());
+
+            group.screen_bounding_rect = packed_layer.set_rect(&local_viewport_rect,
                                                                self.screen_rect,
                                                                self.device_pixel_ratio);
+
+            debug!("\t{:?} local viewport {:?} screen bound {:?}",
+                stacking_context_index, local_viewport_rect, group.screen_bounding_rect);
         }
     }
 
     fn compute_stacking_context_visibility(&mut self) {
         for context_index in 0..self.frame_builder.stacking_context_store.len() {
             let is_visible = {
                 // We don't take into account visibility of children here, so we must
                 // do that later.
@@ -1623,29 +1640,38 @@ impl<'a> LayerRectCalculationAndCullingP
             };
             self.frame_builder.stacking_context_store[context_index].is_visible = is_visible;
         }
     }
 
     fn handle_pop_stacking_context(&mut self) {
         let stacking_context_index = self.stacking_context_stack.pop().unwrap();
 
-        let (bounding_rect, is_visible) = {
+        let (bounding_rect, is_visible, is_preserve_3d, reference_frame_id, reference_frame_bounds) = {
             let stacking_context =
                 &mut self.frame_builder.stacking_context_store[stacking_context_index.0];
             stacking_context.screen_bounds = stacking_context.screen_bounds
                                                              .intersection(self.screen_rect)
                                                              .unwrap_or(DeviceIntRect::zero());
-            (stacking_context.screen_bounds.clone(), stacking_context.is_visible)
+            (stacking_context.screen_bounds.clone(),
+             stacking_context.is_visible,
+             stacking_context.isolation == ContextIsolation::Items,
+             stacking_context.reference_frame_id,
+             stacking_context.isolated_items_bounds.translate(&stacking_context.reference_frame_offset),
+            )
         };
 
         if let Some(ref mut parent_index) = self.stacking_context_stack.last_mut() {
             let parent = &mut self.frame_builder.stacking_context_store[parent_index.0];
             parent.screen_bounds = parent.screen_bounds.union(&bounding_rect);
-
+            // add children local bounds only for non-item-isolated contexts
+            if !is_preserve_3d && parent.reference_frame_id == reference_frame_id {
+                let child_bounds = reference_frame_bounds.translate(&-parent.reference_frame_offset);
+                parent.isolated_items_bounds = parent.isolated_items_bounds.union(&child_bounds);
+            }
             // The previous compute_stacking_context_visibility pass did not take into
             // account visibility of children, so we do that now.
             parent.is_visible = parent.is_visible || is_visible;
         }
     }
 
     fn handle_push_stacking_context(&mut self, stacking_context_index: StackingContextIndex) {
         self.stacking_context_stack.push(stacking_context_index);
@@ -1653,144 +1679,174 @@ impl<'a> LayerRectCalculationAndCullingP
         // Reset bounding rect to zero. We will calculate it as we collect primitives
         // from various scroll layers. In handle_pop_stacking_context , we use this to
         // calculate the device bounding rect. In the future, we could cache this during
         // the initial adding of items for the common case (where there is only a single
         // scroll layer for items in a stacking context).
         let stacking_context = &mut self.frame_builder
                                         .stacking_context_store[stacking_context_index.0];
         stacking_context.screen_bounds = DeviceIntRect::zero();
+        stacking_context.isolated_items_bounds = LayerRect::zero();
     }
 
-    fn rebuild_clip_info_stack_if_necessary(&mut self, id: ClipId) -> Option<DeviceIntRect> {
-        if let Some((current_scroll_id, bounding_rect)) = self.current_clip_info {
-            if current_scroll_id == id {
+    fn rebuild_clip_info_stack_if_necessary(&mut self, clip_id: ClipId) -> Option<DeviceIntRect> {
+        if let Some((current_id, bounding_rect)) = self.current_clip_info {
+            if current_id == clip_id {
                 return bounding_rect;
             }
         }
 
         // TODO(mrobinson): If we notice that this process is expensive, we can special-case
         // more common situations, such as moving from a child or a parent.
         self.current_clip_stack.clear();
-        let mut bounding_rect = None;
+        self.current_clip_info = Some((clip_id, None));
 
-        let mut current_id = Some(id);
+        let mut bounding_rect = *self.screen_rect;
+        let mut current_id = Some(clip_id);
+        // Indicates if the next non-reference-frame that we encounter needs to have its
+        // local combined clip rectangle backed into the clip mask.
+        let mut next_node_needs_region_mask = false;
         while let Some(id) = current_id {
             let node = &self.clip_scroll_tree.nodes.get(&id).unwrap();
             current_id = node.parent;
 
-            let clip_info = match node.node_type {
-                NodeType::Clip(ref clip) if clip.is_masking() => clip,
+            let clip = match node.node_type {
+                NodeType::ReferenceFrame(transform) => {
+                    // if the transform is non-aligned, bake the next LCCR into the clip mask
+                    next_node_needs_region_mask |= !transform.preserves_2d_axis_alignment();
+                    continue
+                },
+                NodeType::Clip(ref clip) if clip.mask_cache_info.is_masking() => clip,
                 _ => continue,
             };
 
-            if bounding_rect.is_none() {
-                bounding_rect = Some(match clip_info.screen_bounding_rect {
-                    Some((_kind, rect)) => rect,
-                    None => DeviceIntRect::zero(),
-                });
+            // apply the screen bounds of the clip node
+            //Note: these are based on the local combined viewport, so can be tighter
+            if let Some((_kind, ref screen_rect)) = clip.screen_bounding_rect {
+                bounding_rect = match bounding_rect.intersection(screen_rect) {
+                    Some(rect) => rect,
+                    None => return None,
+                }
             }
-            self.current_clip_stack.push((clip_info.packed_layer_index,
-                                          clip_info.mask_cache_info.clone().unwrap()))
+
+            let clip_info = if next_node_needs_region_mask {
+                clip.mask_cache_info.clone()
+            } else {
+                clip.mask_cache_info.strip_aligned()
+            };
+
+            // apply the outer device bounds of the clip stack
+            if let Some(ref outer) = clip_info.bounds.outer {
+                bounding_rect = match bounding_rect.intersection(&outer.device_rect) {
+                    Some(rect) => rect,
+                    None => return None,
+                }
+            }
+
+            //TODO-LCCR: bake a single LCCR instead of all aligned rects?
+            self.current_clip_stack.push((clip.packed_layer_index, clip_info));
+            next_node_needs_region_mask = false;
         }
+
         self.current_clip_stack.reverse();
-
-        self.current_clip_info = Some((id, bounding_rect));
-        bounding_rect
+        self.current_clip_info = Some((clip_id, Some(bounding_rect)));
+        Some(bounding_rect)
     }
 
     fn handle_primitive_run(&mut self,
-                            prim_index: PrimitiveIndex,
+                            base_prim_index: PrimitiveIndex,
                             prim_count: usize,
                             clip_and_scroll: ClipAndScrollInfo) {
         let stacking_context_index = *self.stacking_context_stack.last().unwrap();
         let (packed_layer_index, pipeline_id) = {
             let stacking_context =
                 &self.frame_builder.stacking_context_store[stacking_context_index.0];
 
             if !stacking_context.is_visible {
+                debug!("{:?} of invisible {:?}", base_prim_index, stacking_context_index);
                 return;
             }
 
             let group_index = stacking_context.clip_scroll_group(clip_and_scroll);
             let clip_scroll_group = &self.frame_builder.clip_scroll_group_store[group_index.0];
-            (clip_scroll_group.packed_layer_index, stacking_context.pipeline_id)
+            (clip_scroll_group.packed_layer_index,
+             stacking_context.pipeline_id)
         };
 
-        let clip_bounds =
-            self.rebuild_clip_info_stack_if_necessary(clip_and_scroll.clip_node_id());
-        if clip_bounds.map_or(false, |bounds| bounds.is_empty()) {
-            return;
-        }
+        debug!("\t{:?} of {:?} at {:?}", base_prim_index, stacking_context_index, packed_layer_index);
+        let clip_bounds = match self.rebuild_clip_info_stack_if_necessary(clip_and_scroll.clip_node_id()) {
+            Some(rect) => rect,
+            None => return,
+        };
 
         let stacking_context =
             &mut self.frame_builder.stacking_context_store[stacking_context_index.0];
         let packed_layer = &self.frame_builder.packed_layers[packed_layer_index.0];
         let display_list = self.display_lists.get(&pipeline_id)
                                              .expect("No display list?");
-        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) {
-                self.frame_builder.prim_store.prepare_prim_for_render(prim_index,
-                                                                      self.resource_cache,
-                                                                      self.gpu_cache,
-                                                                      &packed_layer.transform,
-                                                                      self.device_pixel_ratio,
-                                                                      display_list);
+        debug!("\tclip_bounds {:?}, layer_local_clip {:?}", clip_bounds, packed_layer.local_clip_rect);
 
-                // 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,
-                };
+        for i in 0..prim_count {
+            let prim_index = PrimitiveIndex(base_prim_index.0 + i);
+            let prim_store = &mut self.frame_builder.prim_store;
+            let (prim_local_rect, prim_screen_rect) = match prim_store
+                .build_bounding_rect(prim_index,
+                                     &clip_bounds,
+                                     &packed_layer.transform,
+                                     &packed_layer.local_clip_rect,
+                                     self.device_pixel_ratio) {
+                Some(rects) => rects,
+                None => 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;
+            debug!("\t\t{:?} bound is {:?}", prim_index, prim_screen_rect);
 
-                stacking_context.screen_bounds =
-                    stacking_context.screen_bounds.union(&prim_bounding_rect);
+            let prim_metadata = prim_store.prepare_prim_for_render(prim_index,
+                                                                   self.resource_cache,
+                                                                   self.gpu_cache,
+                                                                   &packed_layer.transform,
+                                                                   self.device_pixel_ratio,
+                                                                   display_list);
 
-                if let Some(info) = prim_clip_info {
-                    self.current_clip_stack.push((packed_layer_index, info.clone()));
-                }
+            stacking_context.screen_bounds = stacking_context.screen_bounds.union(&prim_screen_rect);
+            stacking_context.isolated_items_bounds = stacking_context.isolated_items_bounds.union(&prim_local_rect);
 
-                // 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 clip_bounds = clip_bounds.unwrap_or_else(DeviceIntRect::zero);
-                    let (mask_key, mask_rect) = match prim_clip_info {
-                        Some(..) => (MaskCacheKey::Primitive(prim_index), prim_bounding_rect),
-                        None => (MaskCacheKey::ClipNode(clip_and_scroll.clip_node_id()),
-                                                        clip_bounds)
-                    };
-                    let mask_opt =
-                        RenderTask::new_mask(mask_rect, mask_key, &self.current_clip_stack);
-                    match mask_opt {
-                        MaskResult::Outside => { // Primitive is completely clipped out.
-                            prim_metadata.clip_task = None;
-                            self.frame_builder.prim_store.cpu_bounding_rects[prim_index.0] = None;
-                            visible = false;
-                        }
-                        MaskResult::Inside(task) => prim_metadata.clip_task = Some(task),
+            // Try to create a mask if we may need to.
+            if !self.current_clip_stack.is_empty() || prim_metadata.clip_cache_info.is_some() {
+                // 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, extra) = match prim_metadata.clip_cache_info {
+                    Some(ref info) => {
+                        // Take into account the actual clip info of the primitive, and
+                        // mutate the current bounds accordingly.
+                        let mask_rect = match info.bounds.outer {
+                            Some(ref outer) => {
+                                match prim_screen_rect.intersection(&outer.device_rect) {
+                                    Some(rect) => rect,
+                                    None => continue,
+                                }
+                            }
+                            _ => prim_screen_rect,
+                        };
+                        (MaskCacheKey::Primitive(prim_index),
+                         mask_rect,
+                         Some((packed_layer_index, info.strip_aligned())))
                     }
-                }
+                    None => {
+                        //Note: can't use `prim_bounding_rect` since
+                        // the primitive ID is not a part of the task key
+                        (MaskCacheKey::ClipNode(clip_and_scroll.clip_node_id()),
+                         clip_bounds,
+                         None)
+                    }
+                };
+                prim_metadata.clip_task = RenderTask::new_mask(mask_rect,
+                                                               mask_key,
+                                                               &self.current_clip_stack,
+                                                               extra)
+            }
 
-                if prim_clip_info.is_some() {
-                    self.current_clip_stack.pop();
-                }
-
-                if visible {
-                    self.profile_counters.visible_primitives.inc();
-                }
-            }
+            self.profile_counters.visible_primitives.inc();
         }
     }
 }
--- a/gfx/webrender/src/glyph_rasterizer.rs
+++ b/gfx/webrender/src/glyph_rasterizer.rs
@@ -13,20 +13,20 @@ use rayon::prelude::*;
 use resource_cache::ResourceClassCache;
 use std::hash::BuildHasherDefault;
 use std::sync::{Arc, Mutex, MutexGuard};
 use std::sync::mpsc::{channel, Receiver, Sender};
 use std::collections::hash_map::Entry;
 use std::collections::HashSet;
 use std::mem;
 use texture_cache::{TextureCacheItemId, TextureCache};
-use webrender_traits::FontTemplate;
-use webrender_traits::{FontKey, FontRenderMode, ImageData, ImageFormat};
-use webrender_traits::{ImageDescriptor, ColorF, LayoutPoint};
-use webrender_traits::{GlyphKey, GlyphOptions, GlyphInstance, GlyphDimensions};
+use api::FontTemplate;
+use api::{FontKey, FontRenderMode, ImageData, ImageFormat};
+use api::{ImageDescriptor, ColorF, LayoutPoint};
+use api::{GlyphKey, GlyphOptions, GlyphInstance, GlyphDimensions};
 
 pub type GlyphCache = ResourceClassCache<GlyphRequest, Option<TextureCacheItemId>>;
 
 pub struct FontContexts {
     // These worker are mostly accessed from their corresponding worker threads.
     // The goal is that there should be no noticeable contention on the muteces.
     worker_contexts: Vec<Mutex<FontContext>>,
 
@@ -109,20 +109,20 @@ impl GlyphRasterizer {
         GlyphRasterizer {
             font_contexts: Arc::new(
                 FontContexts {
                     worker_contexts: contexts,
                     shared_context: Mutex::new(FontContext::new()),
                     workers: Arc::clone(&workers),
                 }
             ),
-            glyph_rx: glyph_rx,
-            glyph_tx: glyph_tx,
+            glyph_rx,
+            glyph_tx,
             pending_glyphs: HashSet::new(),
-            workers: workers,
+            workers,
             fonts_to_remove: Vec::new(),
         }
     }
 
     pub fn add_font(&mut self, font_key: FontKey, template: FontTemplate) {
         let font_contexts = Arc::clone(&self.font_contexts);
         // It's important to synchronously add the font for the shared context because
         // we use it to check that fonts have been properly added when requesting glyphs.
@@ -332,18 +332,18 @@ impl GlyphRequest {
         color: ColorF,
         index: u32,
         point: LayoutPoint,
         render_mode: FontRenderMode,
         glyph_options: Option<GlyphOptions>,
     ) -> GlyphRequest {
         GlyphRequest {
             key: GlyphKey::new(font_key, size, color, index, point, render_mode),
-            render_mode: render_mode,
-            glyph_options: glyph_options,
+            render_mode,
+            glyph_options,
         }
     }
 }
 
 struct GlyphRasterJob {
     request: GlyphRequest,
     result: Option<RasterizedGlyph>,
 }
--- a/gfx/webrender/src/gpu_cache.rs
+++ b/gfx/webrender/src/gpu_cache.rs
@@ -24,17 +24,17 @@
 //! address in the GPU cache of a given resource slot
 //! for this frame.
 
 use device::FrameId;
 use internal_types::UvRect;
 use profiler::GpuCacheProfileCounters;
 use renderer::MAX_VERTEX_TEXTURE_WIDTH;
 use std::{mem, u32};
-use webrender_traits::{ColorF, LayerRect};
+use api::{ColorF, LayerRect};
 
 pub const GPU_CACHE_INITIAL_HEIGHT: u32 = 512;
 const FRAMES_BEFORE_EVICTION: usize = 10;
 const NEW_ROWS_PER_RESIZE: u32 = 512;
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
 struct Epoch(u32);
 
@@ -51,16 +51,24 @@ struct CacheLocation {
 }
 
 /// A single texel in RGBAF32 texture - 16 bytes.
 #[derive(Copy, Clone, Debug)]
 pub struct GpuBlockData {
     pub data: [f32; 4],
 }
 
+impl GpuBlockData {
+    pub fn empty() -> GpuBlockData {
+        GpuBlockData {
+            data: [0.0; 4],
+        }
+    }
+}
+
 /// Conversion helpers for GpuBlockData
 impl Into<GpuBlockData> for ColorF {
     fn into(self) -> GpuBlockData {
         GpuBlockData {
             data: [self.r, self.g, self.b, self.a],
         }
     }
 }
@@ -149,18 +157,18 @@ struct Block {
     last_access_time: FrameId,
 }
 
 impl Block {
     fn new(address: GpuCacheAddress,
            next: Option<BlockIndex>,
            frame_id: FrameId) -> Block {
         Block {
-            address: address,
-            next: next,
+            address,
+            next,
             last_access_time: frame_id,
             epoch: Epoch(0),
         }
     }
 }
 
 #[derive(Debug, Copy, Clone)]
 struct BlockIndex(usize);
@@ -172,17 +180,17 @@ struct Row {
     // This means no dealing with fragmentation within a cache
     // row as items are allocated and freed.
     block_count_per_item: usize,
 }
 
 impl Row {
     fn new(block_count_per_item: usize) -> Row {
         Row {
-            block_count_per_item: block_count_per_item,
+            block_count_per_item,
         }
     }
 }
 
 // A list of update operations that can be applied on the cache
 // this frame. The list of updates is created by the render backend
 // during frame construction. It's passed to the render thread
 // where GL commands can be applied.
@@ -343,17 +351,17 @@ impl Texture {
         self.occupied_list_head = Some(free_block_index);
         self.allocated_block_count += alloc_size;
 
         if let Some(pending_block_index) = pending_block_index {
             // Add this update to the pending list of blocks that need
             // to be updated on the GPU.
             self.updates.push(GpuCacheUpdate::Copy {
                 block_index: pending_block_index,
-                block_count: block_count,
+                block_count,
                 address: block.address,
             });
         }
 
         CacheLocation {
             block_index: free_block_index,
             epoch: block.epoch,
         }
@@ -426,23 +434,30 @@ impl Texture {
 pub struct GpuDataRequest<'a> {
     handle: &'a mut GpuCacheHandle,
     frame_id: FrameId,
     start_index: usize,
     texture: &'a mut Texture,
 }
 
 impl<'a> GpuDataRequest<'a> {
-    pub fn push(&mut self, block: GpuBlockData) {
-        self.texture.pending_blocks.push(block);
+    pub fn push<B>(&mut self, block: B)
+    where B: Into<GpuBlockData>
+    {
+        self.texture.pending_blocks.push(block.into());
     }
 
     pub fn extend_from_slice(&mut self, blocks: &[GpuBlockData]) {
         self.texture.pending_blocks.extend_from_slice(blocks);
     }
+
+    /// Consume the request and return the number of blocks written
+    pub fn close(self) -> usize {
+        self.texture.pending_blocks.len() - self.start_index
+    }
 }
 
 impl<'a> Drop for GpuDataRequest<'a> {
     fn drop(&mut self) {
         // Push the data to the texture pending updates list.
         let block_count = self.texture.pending_blocks.len() - self.start_index;
         let location = self.texture.push_data(Some(self.start_index),
                                               block_count,
@@ -494,17 +509,17 @@ impl GpuCache {
             if block.epoch == location.epoch {
                 // Mark last access time to avoid evicting this block.
                 block.last_access_time = self.frame_id;
                 return None
             }
         }
 
         Some(GpuDataRequest {
-            handle: handle,
+            handle,
             frame_id: self.frame_id,
             start_index: self.texture.pending_blocks.len(),
             texture: &mut self.texture,
         })
     }
 
     // Push an array of data blocks to be uploaded to the GPU
     // unconditionally for this frame. The cache handle will
deleted file mode 100644
--- a/gfx/webrender/src/gpu_store.rs
+++ /dev/null
@@ -1,158 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use device::TextureFilter;
-use std::marker::PhantomData;
-use std::mem;
-use std::ops::Add;
-use util::recycle_vec;
-use webrender_traits::ImageFormat;
-
-#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
-pub struct GpuStoreAddress(pub i32);
-
-
-impl Add<i32> for GpuStoreAddress {
-    type Output = GpuStoreAddress;
-
-    fn add(self, other: i32) -> GpuStoreAddress {
-        GpuStoreAddress(self.0 + other)
-    }
-}
-
-impl Add<usize> for GpuStoreAddress {
-    type Output = GpuStoreAddress;
-
-    fn add(self, other: usize) -> GpuStoreAddress {
-        GpuStoreAddress(self.0 + other as 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::BGRA8 => 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>()
-    }
-
-    fn rows_per_item<T>() -> usize {
-        Self::texels_per_item::<T>() / Self::texture_width::<T>()
-    }
-}
-
-/// A CPU-side buffer storing content to be uploaded to the GPU.
-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, L: GpuStoreLayout> GpuStore<T, L> {
-    pub fn new() -> GpuStore<T, L> {
-        GpuStore {
-            data: Vec::new(),
-            layout: PhantomData,
-            //free_list: Vec::new(),
-        }
-    }
-
-    pub fn recycle(self) -> Self {
-        GpuStore {
-            data: recycle_vec(self.data),
-            layout: PhantomData,
-        }
-    }
-
-    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 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.
-        if items_per_row != 0 {
-            while items_per_row != 0 && items.len() % items_per_row != 0 {
-                items.push(T::default());
-            }
-        }
-
-        items
-    }
-
-    pub fn alloc(&mut self, count: usize) -> GpuStoreAddress {
-        let address = self.get_next_address();
-
-        for _ in 0..count {
-            self.data.push(T::default());
-        }
-
-        address
-    }
-
-    pub fn get_next_address(&self) -> GpuStoreAddress {
-        GpuStoreAddress(self.data.len() as i32)
-    }
-
-    pub fn get(&mut self, address: GpuStoreAddress) -> &T {
-        &self.data[address.0 as usize]
-    }
-
-    pub fn get_mut(&mut self, address: GpuStoreAddress) -> &mut T {
-        &mut self.data[address.0 as usize]
-    }
-
-    pub fn get_slice_mut(&mut self,
-                         address: GpuStoreAddress,
-                         count: usize) -> &mut [T] {
-        let offset = address.0 as usize;
-        &mut self.data[offset..offset + count]
-    }
-
-    pub fn clear(&mut self) {
-        self.data.clear()
-    }
-
-    // TODO(gw): Implement incremental updates of
-    // GPU backed data, and support freelist for removing
-    // dynamic items.
-
-    /*
-    pub fn free(&mut self, address: GpuStoreAddress) {
-
-    }
-
-    pub fn update(&mut self, address: GpuStoreAddress, data: T) {
-
-    }*/
-}
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -9,18 +9,18 @@ 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::{ClipId, ColorF, DeviceUintRect, Epoch, ExternalImageData, ExternalImageId};
-use webrender_traits::{DevicePoint, ImageData, ImageFormat, PipelineId};
+use api::{ClipId, ColorF, DeviceUintRect, Epoch, ExternalImageData, ExternalImageId};
+use api::{DevicePoint, ImageData, ImageFormat, PipelineId};
 
 // 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
@@ -54,17 +54,16 @@ pub const ORTHO_FAR_PLANE: f32 = 1000000
 
 #[derive(Debug, PartialEq, Eq)]
 pub enum TextureSampler {
     Color0,
     Color1,
     Color2,
     CacheA8,
     CacheRGBA8,
-    Data32,
     ResourceCache,
     Layers,
     RenderTasks,
     Dither,
 }
 
 impl TextureSampler {
     pub fn color(n: usize) -> TextureSampler {
@@ -166,38 +165,38 @@ pub struct DebugFontVertex {
     pub color: PackedColor,
     pub u: f32,
     pub v: f32,
 }
 
 impl DebugFontVertex {
     pub fn new(x: f32, y: f32, u: f32, v: f32, color: PackedColor) -> DebugFontVertex {
         DebugFontVertex {
-            x: x,
-            y: y,
-            color: color,
-            u: u,
-            v: v,
+            x,
+            y,
+            color,
+            u,
+            v,
         }
     }
 }
 
 #[repr(C)]
 pub struct DebugColorVertex {
     pub x: f32,
     pub y: f32,
     pub color: PackedColor,
 }
 
 impl DebugColorVertex {
     pub fn new(x: f32, y: f32, color: PackedColor) -> DebugColorVertex {
         DebugColorVertex {
-            x: x,
-            y: y,
-            color: color,
+            x,
+            y,
+            color,
         }
     }
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum RenderTargetMode {
     None,
     SimpleRenderTarget,
@@ -274,19 +273,19 @@ pub struct RendererFrame {
 }
 
 impl RendererFrame {
     pub fn new(pipeline_epoch_map: HashMap<PipelineId, Epoch, BuildHasherDefault<FnvHasher>>,
                layers_bouncing_back: HashSet<ClipId, BuildHasherDefault<FnvHasher>>,
                frame: Option<tiling::Frame>)
                -> RendererFrame {
         RendererFrame {
-            pipeline_epoch_map: pipeline_epoch_map,
-            layers_bouncing_back: layers_bouncing_back,
-            frame: frame,
+            pipeline_epoch_map,
+            layers_bouncing_back,
+            frame,
         }
     }
 }
 
 pub enum ResultMsg {
     RefreshShader(PathBuf),
     NewFrame(RendererFrame, TextureUpdateList, BackendProfileCounters),
 }
--- a/gfx/webrender/src/lib.rs
+++ b/gfx/webrender/src/lib.rs
@@ -28,17 +28,17 @@
 //! More information about [stacking contexts][stacking_contexts].
 //!
 //! `set_display_list()` also needs to be supplied with `BuiltDisplayList`s.
 //! These are obtained by finalizing a `DisplayListBuilder`. These are used to draw your geometry.
 //! But it doesn't only contain trivial geometry, it can also store another StackingContext, as
 //! they're nestable.
 //!
 //! [stacking_contexts]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
-//! [newframe]: ../webrender_traits/struct.RenderApi.html#method.set_display_list
+//! [newframe]: ../webrender_api/struct.RenderApi.html#method.set_display_list
 //! [notifier]: renderer/struct.Renderer.html#method.set_render_notifier
 
 #[macro_use]
 extern crate lazy_static;
 #[macro_use]
 extern crate log;
 #[macro_use]
 extern crate bitflags;
@@ -54,17 +54,16 @@ mod debug_render;
 mod device;
 mod ellipse;
 mod frame;
 mod frame_builder;
 mod freelist;
 mod geometry;
 mod glyph_rasterizer;
 mod gpu_cache;
-mod gpu_store;
 mod internal_types;
 mod mask_cache;
 mod prim_store;
 mod print_tree;
 mod profiler;
 mod record;
 mod render_backend;
 mod render_task;
@@ -129,20 +128,22 @@ extern crate dwrote;
 extern crate app_units;
 extern crate bincode;
 extern crate euclid;
 extern crate fnv;
 extern crate gleam;
 extern crate num_traits;
 //extern crate notify;
 extern crate time;
-extern crate webrender_traits;
+pub extern crate webrender_api;
 #[cfg(feature = "webgl")]
 extern crate offscreen_gl_context;
 extern crate byteorder;
 extern crate rayon;
 extern crate plane_split;
 
 #[cfg(any(target_os="macos", target_os="windows"))]
 extern crate gamma_lut;
 
 pub use renderer::{ExternalImage, ExternalImageSource, ExternalImageHandler};
 pub use renderer::{GraphicsApi, GraphicsApiInfo, ReadPixelsFormat, Renderer, RendererOptions};
+
+pub use webrender_api as api;
--- a/gfx/webrender/src/mask_cache.rs
+++ b/gfx/webrender/src/mask_cache.rs
@@ -1,25 +1,69 @@
 /* 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 api::{BorderRadius, ComplexClipRegion, DeviceIntRect, ImageMask, LayerPoint, LayerRect};
+use api::{LayerSize, LayerToWorldTransform, LocalClip};
 use border::BorderCornerClipSource;
-use gpu_store::GpuStoreAddress;
-use prim_store::{ClipData, GpuBlock32, ImageMaskData, PrimitiveStore};
-use prim_store::{CLIP_DATA_GPU_SIZE, MASK_DATA_GPU_SIZE};
-use renderer::VertexDataStore;
-use util::{ComplexClipRegionHelpers, MatrixHelpers, TransformedRect};
-use webrender_traits::{BorderRadius, BuiltDisplayList, ClipRegion, ComplexClipRegion, ImageMask};
-use webrender_traits::{DeviceIntRect, LayerToWorldTransform};
-use webrender_traits::{DeviceRect, LayerRect, LayerPoint, LayerSize};
+use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
+use prim_store::{CLIP_DATA_GPU_BLOCKS, ClipData, ImageMaskData};
+use util::{ComplexClipRegionHelpers, TransformedRect};
 use std::ops::Not;
 
 const MAX_CLIP: f32 = 1000000.0;
 
+#[derive(Clone, Debug)]
+pub struct ClipRegion {
+    pub origin: LayerPoint,
+    pub main: LayerRect,
+    pub image_mask: Option<ImageMask>,
+    pub complex_clips: Vec<ComplexClipRegion>,
+}
+
+impl ClipRegion {
+    pub fn for_clip_node(rect: LayerRect,
+                         mut complex_clips: Vec<ComplexClipRegion>,
+                         mut image_mask: Option<ImageMask>)
+                         -> ClipRegion {
+        // All the coordinates we receive are relative to the stacking context, but we want
+        // to convert them to something relative to the origin of the clip.
+        let negative_origin = -rect.origin.to_vector();
+        if let Some(ref mut image_mask) = image_mask {
+            image_mask.rect = image_mask.rect.translate(&negative_origin);
+        }
+
+        for complex_clip in complex_clips.iter_mut() {
+            complex_clip.rect = complex_clip.rect.translate(&negative_origin);
+        }
+
+        ClipRegion {
+            origin: rect.origin,
+            main: LayerRect::new(LayerPoint::zero(), rect.size),
+            image_mask,
+            complex_clips,
+        }
+    }
+
+    pub fn for_local_clip(local_clip: &LocalClip) -> ClipRegion {
+        let complex_clips = match local_clip {
+            &LocalClip::Rect(_) => Vec::new(),
+            &LocalClip::RoundedRect(_, ref region) => vec![region.clone()],
+        };
+
+        ClipRegion {
+            origin: LayerPoint::zero(),
+            main: *local_clip.clip_rect(),
+            image_mask: None,
+            complex_clips,
+        }
+    }
+}
+
 #[repr(C)]
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum ClipMode {
     Clip,           // Pixels inside the region are visible.
     ClipOut,        // Pixels outside the region are visible.
 }
 
 impl Not for ClipMode {
@@ -28,305 +72,301 @@ impl Not for ClipMode {
     fn not(self) -> ClipMode {
         match self {
             ClipMode::Clip => ClipMode::ClipOut,
             ClipMode::ClipOut => ClipMode::Clip
         }
     }
 }
 
-#[repr(C)]
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum RegionMode {
-    IncludeRect,
-    ExcludeRect,
-}
-
 #[derive(Clone, Debug)]
 pub enum ClipSource {
     Complex(LayerRect, f32, ClipMode),
-    // The RegionMode here specifies whether to consider the rect
-    // from the clip region as part of the mask. This is true
-    // for clip/scroll nodes, but false for primitives, where
-    // the clip rect is handled in local space.
-    Region(ClipRegion, RegionMode),
-
-    // TODO(gw): This currently only handles dashed style
-    // clips, where the border style is dashed for both
-    // adjacent border edges. Expand to handle dotted style
-    // and different styles per edge.
+    Region(ClipRegion),
+    /// TODO(gw): This currently only handles dashed style
+    /// clips, where the border style is dashed for both
+    /// adjacent border edges. Expand to handle dotted style
+    /// and different styles per edge.
     BorderCorner(BorderCornerClipSource),
 }
 
 impl ClipSource {
     pub fn image_mask(&self) -> Option<ImageMask> {
         match *self {
             ClipSource::Complex(..) |
-            ClipSource::BorderCorner{..} => None,
-            ClipSource::Region(ref region, _) => region.image_mask,
+            ClipSource::BorderCorner(..) => None,
+            ClipSource::Region(ref region) => region.image_mask,
         }
     }
 }
 
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+#[derive(Debug, Copy, Clone)]
 pub struct ClipAddressRange {
-    pub start: GpuStoreAddress,
+    pub location: GpuCacheHandle,
     item_count: usize,
 }
 
+impl ClipAddressRange {
+    fn new(count: usize) -> Self {
+        ClipAddressRange {
+            location: GpuCacheHandle::new(),
+            item_count: count,
+        }
+    }
+
+    pub fn get_count(&self) -> usize {
+        self.item_count
+    }
+
+    fn get_block_count(&self) -> Option<usize> {
+        if self.item_count != 0 {
+            Some(self.item_count * CLIP_DATA_GPU_BLOCKS)
+        } else {
+            None
+        }
+    }
+}
+
 /// Represents a local rect and a device space
-/// bounding rect that can be updated when the
-/// transform changes.
+/// rectangles that are either outside or inside bounds.
 #[derive(Clone, Debug, PartialEq)]
 pub struct Geometry {
     pub local_rect: LayerRect,
-    pub bounding_rect: DeviceIntRect,
+    pub device_rect: DeviceIntRect,
 }
 
-impl Geometry {
-    fn new(local_rect: LayerRect) -> Geometry {
+impl From<LayerRect> for Geometry {
+    fn from(local_rect: LayerRect) -> Self {
         Geometry {
-            local_rect: local_rect,
-            bounding_rect: DeviceIntRect::zero(),
+            local_rect,
+            device_rect: DeviceIntRect::zero(),
         }
     }
-
-    fn update(&mut self,
-              transform: &LayerToWorldTransform,
-              device_pixel_ratio: f32) {
-        let transformed = TransformedRect::new(&self.local_rect,
-                                               transform,
-                                               device_pixel_ratio);
-        self.bounding_rect = transformed.bounding_rect;
-    }
 }
 
 /// Depending on the complexity of the clip, we may either
 /// know the outer and/or inner rect, or neither or these.
 /// In the case of a clip-out, we currently set the mask
 /// bounds to be unknown. This is conservative, but ensures
 /// correctness. In the future we can make this a lot
 /// more clever with some proper region handling.
 #[derive(Clone, Debug, PartialEq)]
-pub enum MaskBounds {
-    /// We know both the outer and inner rect. This is the
-    /// fast path for, e.g. a simple rounded rect.
-    OuterInner(Geometry, Geometry),
-    /// We know the outer rect only.
-    Outer(Geometry),
-    /// We can't determine the bounds - draw mask over entire rect.
-    /// This is currently used for clip-out operations on
-    /// box shadows.
-    None,
+pub struct MaskBounds {
+    pub outer: Option<Geometry>,
+    pub inner: Option<Geometry>,
+}
+
+impl MaskBounds {
+    pub fn update(&mut self, transform: &LayerToWorldTransform, device_pixel_ratio: f32) {
+        if let Some(ref mut outer) = self.outer {
+            let transformed = TransformedRect::new(&outer.local_rect,
+                                                   transform,
+                                                   device_pixel_ratio);
+            outer.device_rect = transformed.bounding_rect;
+        }
+        if let Some(ref mut inner) = self.inner {
+            let transformed = TransformedRect::new(&inner.local_rect,
+                                                   transform,
+                                                   device_pixel_ratio);
+            inner.device_rect = transformed.inner_rect;
+        }
+    }
 }
 
 #[derive(Clone, Debug)]
 pub struct MaskCacheInfo {
+    /// Clip items that are always applied
     pub complex_clip_range: ClipAddressRange,
-    pub effective_complex_clip_count: usize,
-    pub image: Option<(ImageMask, GpuStoreAddress)>,
-    pub border_corners: Vec<(BorderCornerClipSource, GpuStoreAddress)>,
-    pub bounds: Option<MaskBounds>,
-    pub is_aligned: bool,
+    /// Clip items that are only applied if the clip space is transformed from
+    /// the local space of target primitive/layer.
+    pub layer_clip_range: ClipAddressRange,
+    pub image: Option<(ImageMask, GpuCacheHandle)>,
+    pub border_corners: Vec<(BorderCornerClipSource, GpuCacheHandle)>,
+    pub bounds: MaskBounds,
 }
 
 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(clips: &[ClipSource],
-               clip_store: &mut VertexDataStore<GpuBlock32>)
-               -> Option<MaskCacheInfo> {
-        if clips.is_empty() {
-            return None;
-        }
-
+    /// it uninitialized for the following `update()` call to deal with.
+    pub fn new(clips: &[ClipSource]) -> MaskCacheInfo {
         let mut image = None;
         let mut border_corners = Vec::new();
         let mut complex_clip_count = 0;
+        let mut layer_clip_count = 0;
 
         // Work out how much clip data space we need to allocate
         // and if we have an image mask.
         for clip in clips {
             match *clip {
                 ClipSource::Complex(..) => {
                     complex_clip_count += 1;
                 }
-                ClipSource::Region(ref region, region_mode) => {
+                ClipSource::Region(ref region) => {
                     if let Some(info) = region.image_mask {
                         debug_assert!(image.is_none());     // TODO(gw): Support >1 image mask!
-                        image = Some((info, clip_store.alloc(MASK_DATA_GPU_SIZE)));
+                        image = Some((info, GpuCacheHandle::new()));
                     }
-                    complex_clip_count += region.complex_clip_count;
-                    if region_mode == RegionMode::IncludeRect {
-                        complex_clip_count += 1;
-                    }
+                    complex_clip_count += region.complex_clips.len();
+                    layer_clip_count += 1;
                 }
                 ClipSource::BorderCorner(ref source) => {
-                    // One block for the corner header, plus one
-                    // block per dash to clip out.
-                    let gpu_address = clip_store.alloc(1 + source.max_clip_count);
-                    border_corners.push((source.clone(), gpu_address));
+                    border_corners.push((source.clone(), GpuCacheHandle::new()));
                 }
             }
         }
 
-        let complex_clip_range = ClipAddressRange {
-            start: if complex_clip_count > 0 {
-                clip_store.alloc(CLIP_DATA_GPU_SIZE * complex_clip_count)
-            } else {
-                GpuStoreAddress(0)
+        MaskCacheInfo {
+            complex_clip_range: ClipAddressRange::new(complex_clip_count),
+            layer_clip_range: ClipAddressRange::new(layer_clip_count),
+            image,
+            border_corners,
+            bounds: MaskBounds {
+                inner: None,
+                outer: None,
             },
-            item_count: complex_clip_count,
-        };
-
-        Some(MaskCacheInfo {
-            complex_clip_range: complex_clip_range,
-            effective_complex_clip_count: complex_clip_range.item_count,
-            image: image,
-            border_corners: border_corners,
-            bounds: None,
-            is_aligned: true,
-        })
+        }
     }
 
     pub fn update(&mut self,
                   sources: &[ClipSource],
                   transform: &LayerToWorldTransform,
-                  clip_store: &mut VertexDataStore<GpuBlock32>,
-                  device_pixel_ratio: f32,
-                  display_list: &BuiltDisplayList) {
-        let is_aligned = transform.preserves_2d_axis_alignment();
+                  gpu_cache: &mut GpuCache,
+                  device_pixel_ratio: f32)
+                  -> &MaskBounds {
 
-        // If we haven't cached this info, or if the transform type has changed
-        // we need to re-calculate the number of clips.
-        if self.bounds.is_none() || self.is_aligned != is_aligned {
+        // Step[1] - compute the local bounds
+        //TODO: move to initialization stage?
+        if self.bounds.inner.is_none() {
             let mut local_rect = Some(LayerRect::new(LayerPoint::new(-MAX_CLIP, -MAX_CLIP),
                                                      LayerSize::new(2.0 * MAX_CLIP, 2.0 * MAX_CLIP)));
             let mut local_inner: Option<LayerRect> = None;
             let mut has_clip_out = false;
-            let mut has_border_clip = false;
-
-            self.effective_complex_clip_count = 0;
-            self.is_aligned = is_aligned;
+            let has_border_clip = !self.border_corners.is_empty();
 
             for source in sources {
                 match *source {
                     ClipSource::Complex(rect, radius, mode) => {
                         // Once we encounter a clip-out, we just assume the worst
                         // case clip mask size, for now.
                         if mode == ClipMode::ClipOut {
                             has_clip_out = true;
+                            break;
                         }
-                        debug_assert!(self.effective_complex_clip_count < self.complex_clip_range.item_count);
-                        let address = self.complex_clip_range.start + self.effective_complex_clip_count * CLIP_DATA_GPU_SIZE;
-                        self.effective_complex_clip_count += 1;
-
-                        let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE);
-                        let data = ClipData::uniform(rect, radius, mode);
-                        PrimitiveStore::populate_clip_data(slice, data);
                         local_rect = local_rect.and_then(|r| r.intersection(&rect));
                         local_inner = ComplexClipRegion::new(rect, BorderRadius::uniform(radius))
                                                         .get_inner_rect_safe();
                     }
-                    ClipSource::Region(ref region, region_mode) => {
+                    ClipSource::Region(ref region) => {
                         local_rect = local_rect.and_then(|r| r.intersection(&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));
+                            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 = display_list.get(region.complex_clips);
-                        if !self.is_aligned && region_mode == RegionMode::IncludeRect {
-                            // we have an extra clip rect coming from the transformed layer
-                            debug_assert!(self.effective_complex_clip_count < self.complex_clip_range.item_count);
-                            let address = self.complex_clip_range.start + self.effective_complex_clip_count * CLIP_DATA_GPU_SIZE;
-                            self.effective_complex_clip_count += 1;
-
-                            let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE);
-                            PrimitiveStore::populate_clip_data(slice, ClipData::uniform(region.main, 0.0, ClipMode::Clip));
-                        }
-
-                        debug_assert!(self.effective_complex_clip_count + clips.len() <= self.complex_clip_range.item_count);
-                        let address = self.complex_clip_range.start + self.effective_complex_clip_count * CLIP_DATA_GPU_SIZE;
-                        self.effective_complex_clip_count += clips.len();
-
-                        let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE * clips.len());
-                        for (clip, chunk) in clips.zip(slice.chunks_mut(CLIP_DATA_GPU_SIZE)) {
-                            let data = ClipData::from_clip_region(&clip);
-                            PrimitiveStore::populate_clip_data(chunk, data);
+                        for clip in &region.complex_clips {
                             local_rect = local_rect.and_then(|r| r.intersection(&clip.rect));
                             local_inner = local_inner.and_then(|r| clip.get_inner_rect_safe()
                                                                        .and_then(|ref inner| r.intersection(inner)));
                         }
                     }
                     ClipSource::BorderCorner{..} => {}
                 }
             }
 
-            for &mut (ref mut source, gpu_address) in &mut self.border_corners {
-                has_border_clip = true;
-                let slice = clip_store.get_slice_mut(gpu_address,
-                                                     1 + source.max_clip_count);
-                source.populate_gpu_data(slice);
-            }
-
-            if let Some((ref mask, gpu_address)) = self.image {
-                let mask_data = clip_store.get_slice_mut(gpu_address, MASK_DATA_GPU_SIZE);
-                mask_data[0] = GpuBlock32::from(ImageMaskData {
-                    padding: DeviceRect::zero(),
-                    local_rect: mask.rect,
-                });
-            }
-
             // Work out the type of mask geometry we have, based on the
             // list of clip sources above.
-            if has_clip_out || has_border_clip {
+            self.bounds = if has_clip_out || has_border_clip {
                 // For clip-out, the mask rect is not known.
-                self.bounds = Some(MaskBounds::None);
+                MaskBounds {
+                    outer: None,
+                    inner: Some(LayerRect::zero().into()),
+                }
             } else {
                 // TODO(gw): local inner is only valid if there's a single clip (for now).
                 // This can be improved in the future, with some proper
                 // rectangle region handling.
                 if sources.len() > 1 {
                     local_inner = None;
                 }
 
-                let local_rect = local_rect.unwrap_or(LayerRect::zero());
+                MaskBounds {
+                    outer: Some(local_rect.unwrap_or(LayerRect::zero()).into()),
+                    inner: Some(local_inner.unwrap_or(LayerRect::zero()).into()),
+                }
+            };
+        }
+
+        // Step[2] - update GPU cache data
 
-                self.bounds = match local_inner {
-                    Some(local_inner) => {
-                        Some(MaskBounds::OuterInner(Geometry::new(local_rect),
-                                                    Geometry::new(local_inner)))
+        if let Some(block_count) = self.complex_clip_range.get_block_count() {
+            if let Some(mut request) = gpu_cache.request(&mut self.complex_clip_range.location) {
+                for source in sources {
+                    match *source {
+                        ClipSource::Complex(rect, radius, mode) => {
+                            let data = ClipData::uniform(rect, radius, mode);
+                            data.write(&mut request);
+                        }
+                        ClipSource::Region(ref region) => {
+                            for clip in &region.complex_clips {
+                                let data = ClipData::from_clip_region(&clip);
+                                data.write(&mut request);
+                            }
+                        }
+                        ClipSource::BorderCorner{..} => {}
                     }
-                    None => {
-                        Some(MaskBounds::Outer(Geometry::new(local_rect)))
-                    }
-                };
+                }
+                assert_eq!(request.close(), block_count);
             }
         }
 
-        // Update the device space bounding rects of the mask
-        // geometry.
-        match self.bounds.as_mut().unwrap() {
-            &mut MaskBounds::None => {}
-            &mut MaskBounds::Outer(ref mut outer) => {
-                outer.update(transform, device_pixel_ratio);
+        if let Some(block_count) = self.layer_clip_range.get_block_count() {
+            if let Some(mut request) = gpu_cache.request(&mut self.layer_clip_range.location) {
+                for source in sources {
+                    if let ClipSource::Region(ref region) = *source {
+                        let data = ClipData::uniform(region.main, 0.0, ClipMode::Clip);
+                        data.write(&mut request);
+                    }
+                }
+                assert_eq!(request.close(), block_count);
             }
-            &mut MaskBounds::OuterInner(ref mut outer, ref mut inner) => {
-                outer.update(transform, device_pixel_ratio);
-                inner.update(transform, device_pixel_ratio);
+        }
+
+        for &mut (ref mut border_source, ref mut gpu_location) in &mut self.border_corners {
+            if let Some(request) = gpu_cache.request(gpu_location) {
+                border_source.write(request);
             }
         }
+
+        if let Some((ref mask, ref mut gpu_location)) = self.image {
+            if let Some(request) = gpu_cache.request(gpu_location) {
+                let data = ImageMaskData {
+                    local_rect: mask.rect,
+                };
+                data.write_gpu_blocks(request);
+            }
+        }
+
+        // Step[3] - update the screen bounds
+        self.bounds.update(transform, device_pixel_ratio);
+        &self.bounds
     }
 
-    /// Check if this `MaskCacheInfo` actually carries any masks. `effective_complex_clip_count`
-    /// can change during the `update` call depending on the transformation, so the mask may
-    /// appear to be empty.
+    /// Check if this `MaskCacheInfo` actually carries any masks.
     pub fn is_masking(&self) -> bool {
         self.image.is_some() ||
-        self.effective_complex_clip_count != 0 ||
+        self.complex_clip_range.item_count != 0 ||
+        self.layer_clip_range.item_count != 0 ||
         !self.border_corners.is_empty()
     }
+
+    /// Return a clone of this object without any layer-aligned clip items
+    pub fn strip_aligned(&self) -> Self {
+        MaskCacheInfo {
+            layer_clip_range: ClipAddressRange::new(0),
+            .. self.clone()
+        }
+    }
 }
--- a/gfx/webrender/src/platform/macos/font.rs
+++ b/gfx/webrender/src/platform/macos/font.rs
@@ -10,19 +10,19 @@ use core_graphics::context::{CGContext, 
 use core_graphics::data_provider::CGDataProvider;
 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};
-use webrender_traits::{GlyphKey, GlyphOptions, SubpixelPoint};
-use webrender_traits::NativeFontHandle;
+use api::{ColorU, FontKey, FontRenderMode, GlyphDimensions};
+use api::{GlyphKey, GlyphOptions, SubpixelPoint};
+use api::NativeFontHandle;
 use gamma_lut::{GammaLut, Color as ColorLut};
 
 pub struct FontContext {
     cg_fonts: HashMap<FontKey, CGFont>,
     ct_fonts: HashMap<(FontKey, Au), CTFont>,
     gamma_lut: GammaLut,
 }
 
--- a/gfx/webrender/src/platform/unix/font.rs
+++ b/gfx/webrender/src/platform/unix/font.rs
@@ -1,16 +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, FontRenderMode, GlyphDimensions};
-use webrender_traits::{NativeFontHandle, GlyphOptions};
-use webrender_traits::{GlyphKey};
+use api::{FontKey, FontRenderMode, GlyphDimensions};
+use api::{NativeFontHandle, GlyphOptions};
+use api::{GlyphKey};
 
 use freetype::freetype::{FT_Render_Mode, FT_Pixel_Mode};
 use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter};
 use freetype::freetype::{FT_Library, FT_Set_Char_Size};
 use freetype::freetype::{FT_Face, FT_Long, FT_UInt, FT_F26Dot6};
 use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph};
 use freetype::freetype::{FT_New_Memory_Face, FT_GlyphSlot, FT_LcdFilter};
 use freetype::freetype::{FT_Done_Face, FT_Error};
@@ -60,17 +60,17 @@ impl FontContext {
             // TODO(gw): Check result of this to determine if freetype build supports subpixel.
             let result = FT_Library_SetLcdFilter(lib, FT_LcdFilter::FT_LCD_FILTER_DEFAULT);
             if !result.succeeded() {
                 println!("WARN: Initializing a FreeType library build without subpixel AA enabled!");
             }
         }
 
         FontContext {
-            lib: lib,
+            lib,
             faces: HashMap::new(),
         }
     }
 
     pub fn has_font(&self, font_key: &FontKey) -> bool {
         self.faces.contains_key(font_key)
     }
 
@@ -81,17 +81,17 @@ impl FontContext {
                 FT_New_Memory_Face(self.lib,
                                    bytes.as_ptr(),
                                    bytes.len() as FT_Long,
                                    index as FT_Long,
                                    &mut face)
             };
             if result.succeeded() && !face.is_null() {
                 self.faces.insert(*font_key, Face {
-                    face: face,
+                    face,
                     //_bytes: bytes
                 });
             } else {
                 println!("WARN: webrender failed to load font {:?}", font_key);
             }
         }
     }
 
--- a/gfx/webrender/src/platform/windows/font.rs
+++ b/gfx/webrender/src/platform/windows/font.rs
@@ -1,15 +1,15 @@
 /* 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 std::collections::HashMap;
-use webrender_traits::{FontKey, FontRenderMode, GlyphDimensions};
-use webrender_traits::{GlyphKey, GlyphOptions};
+use api::{FontKey, FontRenderMode, GlyphDimensions};
+use api::{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,
@@ -98,18 +98,18 @@ fn get_glyph_dimensions_with_analysis(an
     // Such as for spaces
     if width == 0 || height == 0 {
         return None
     }
 
     Some(GlyphDimensions {
         left: bounds.left,
         top: -bounds.top,
-        width: width,
-        height: height,
+        width,
+        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.
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1,36 +1,59 @@
 /* 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 api::{BuiltDisplayList, ColorF, ComplexClipRegion, DeviceIntRect, DeviceIntSize, DevicePoint};
+use api::{ExtendMode, FontKey, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop};
+use api::{ImageKey, ImageRendering, ItemRange, LayerPoint, LayerRect, LayerSize};
+use api::{LayerToWorldTransform, TileOffset, WebGLContextId, YuvColorSpace, YuvFormat};
+use api::device_length;
 use app_units::Au;
-use border::{BorderCornerClipData, BorderCornerDashClipData, BorderCornerDotClipData};
 use border::BorderCornerInstance;
 use euclid::{Size2D};
 use gpu_cache::{GpuCacheAddress, GpuBlockData, GpuCache, GpuCacheHandle, GpuDataRequest, ToGpuBlocks};
-use gpu_store::GpuStoreAddress;
-use mask_cache::{ClipMode, ClipSource, MaskCacheInfo};
-use renderer::{VertexDataStore, MAX_VERTEX_TEXTURE_WIDTH};
+use mask_cache::{ClipMode, ClipRegion, ClipSource, MaskCacheInfo};
+use renderer::MAX_VERTEX_TEXTURE_WIDTH;
 use render_task::{RenderTask, RenderTaskLocation};
 use resource_cache::{ImageProperties, ResourceCache};
-use std::mem;
-use std::usize;
+use std::{mem, usize};
 use util::{TransformedRect, recycle_vec};
-use webrender_traits::{BuiltDisplayList, ColorF, ImageKey, ImageRendering, YuvColorSpace};
-use webrender_traits::{YuvFormat, ClipRegion, ComplexClipRegion, ItemRange};
-use webrender_traits::{FontKey, FontRenderMode, WebGLContextId};
-use webrender_traits::{device_length, DeviceIntRect, DeviceIntSize};
-use webrender_traits::{DeviceRect, DevicePoint};
-use webrender_traits::{LayerRect, LayerSize, LayerPoint};
-use webrender_traits::{LayerToWorldTransform, GlyphInstance, GlyphOptions};
-use webrender_traits::{ExtendMode, GradientStop, TileOffset};
+
+
+pub const CLIP_DATA_GPU_BLOCKS: usize = 10;
+
+#[derive(Debug, Copy, Clone)]
+pub struct PrimitiveOpacity {
+    pub is_opaque: bool,
+}
+
+impl PrimitiveOpacity {
+    pub fn opaque() -> PrimitiveOpacity {
+        PrimitiveOpacity {
+            is_opaque: true,
+        }
+    }
 
-pub const CLIP_DATA_GPU_SIZE: usize = 5;
-pub const MASK_DATA_GPU_SIZE: usize = 1;
+    pub fn translucent() -> PrimitiveOpacity {
+        PrimitiveOpacity {
+            is_opaque: false,
+        }
+    }
+
+    pub fn from_alpha(alpha: f32) -> PrimitiveOpacity {
+        PrimitiveOpacity {
+            is_opaque: alpha == 1.0,
+        }
+    }
+
+    pub fn accumulate(&mut self, alpha: f32) {
+        self.is_opaque = self.is_opaque && alpha == 1.0;
+    }
+}
 
 /// 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
 /// updated on the CPU when the texture size changes.
 #[derive(Copy, Clone, Debug)]
 pub struct TexelRect {
@@ -109,17 +132,17 @@ impl GpuCacheHandle {
         //           to use 2x u16 for the vertex attribute instead of an i32.
         address.v as i32 * MAX_VERTEX_TEXTURE_WIDTH as i32 + address.u as i32
     }
 }
 
 // TODO(gw): Pack the fields here better!
 #[derive(Debug)]
 pub struct PrimitiveMetadata {
-    pub is_opaque: bool,
+    pub opacity: PrimitiveOpacity,
     pub clips: Vec<ClipSource>,
     pub clip_cache_info: Option<MaskCacheInfo>,
     pub prim_kind: PrimitiveKind,
     pub cpu_prim_index: SpecificPrimitiveIndex,
     pub gpu_location: GpuCacheHandle,
     // An optional render task that is a dependency of
     // drawing this primitive. For instance, box shadows
     // use this to draw a portion of the box shadow to
@@ -147,17 +170,17 @@ impl PrimitiveMetadata {
 #[derive(Debug, Clone)]
 #[repr(C)]
 pub struct RectanglePrimitive {
     pub color: ColorF,
 }
 
 impl ToGpuBlocks for RectanglePrimitive {
     fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
-        request.push(self.color.into());
+        request.push(self.color);
     }
 }
 
 #[derive(Debug)]
 pub enum ImagePrimitiveKind {
     Image(ImageKey, ImageRendering, Option<TileOffset>, LayerSize),
     WebGL(WebGLContextId),
 }
@@ -224,49 +247,53 @@ pub struct BoxShadowPrimitiveCpu {
     pub edge_size: f32,
     pub blur_radius: f32,
     pub inverted: f32,
     pub rects: Vec<LayerRect>,
 }
 
 impl ToGpuBlocks for BoxShadowPrimitiveCpu {
     fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
-        request.push(self.src_rect.into());
-        request.push(self.bs_rect.into());
-        request.push(self.color.into());
+        request.push(self.src_rect);
+        request.push(self.bs_rect);
+        request.push(self.color);
         request.push([self.border_radius,
                       self.edge_size,
                       self.blur_radius,
-                      self.inverted].into());
+                      self.inverted]);
         for &rect in &self.rects {
-            request.push(rect.into());
+            request.push(rect);
         }
     }
 }
 
 #[derive(Debug)]
 pub struct GradientPrimitiveCpu {
     pub stops_range: ItemRange<GradientStop>,
     pub stops_count: usize,
     pub extend_mode: ExtendMode,
     pub reverse_stops: bool,
     pub gpu_blocks: [GpuBlockData; 3],
 }
 
 impl GradientPrimitiveCpu {
     fn build_gpu_blocks_for_aligned(&self,
                                     display_list: &BuiltDisplayList,
-                                    mut request: GpuDataRequest) {
+                                    mut request: GpuDataRequest) -> PrimitiveOpacity {
+        let mut opacity = PrimitiveOpacity::opaque();
         request.extend_from_slice(&self.gpu_blocks);
         let src_stops = display_list.get(self.stops_range);
 
         for src in src_stops {
-            request.push(src.color.premultiplied().into());
-            request.push([src.offset, 0.0, 0.0, 0.0].into());
+            request.push(src.color.premultiplied());
+            request.push([src.offset, 0.0, 0.0, 0.0]);
+            opacity.accumulate(src.color.a);
         }
+
+        opacity
     }
 
     fn build_gpu_blocks_for_angle_radial(&self,
                                          display_list: &BuiltDisplayList,
                                          mut request: GpuDataRequest) {
         request.extend_from_slice(&self.gpu_blocks);
 
         let gradient_builder = GradientGpuBlockBuilder::new(self.stops_range,
@@ -302,18 +329,18 @@ struct GradientGpuBlockBuilder<'a> {
     stops_range: ItemRange<GradientStop>,
     display_list: &'a BuiltDisplayList,
 }
 
 impl<'a> GradientGpuBlockBuilder<'a> {
     fn new(stops_range: ItemRange<GradientStop>,
            display_list: &'a BuiltDisplayList) -> GradientGpuBlockBuilder<'a> {
         GradientGpuBlockBuilder {
-            stops_range: stops_range,
-            display_list: display_list,
+            stops_range,
+            display_list,
         }
     }
 
     /// Generate a color ramp filling the indices in [start_idx, end_idx) and interpolating
     /// from start_color to end_color.
     fn fill_colors(&self,
                    start_idx: usize,
                    end_idx: usize,
@@ -421,27 +448,26 @@ impl<'a> GradientGpuBlockBuilder<'a> {
             }
             debug_assert_eq!(cur_idx, GRADIENT_DATA_TABLE_END);
 
             // Fill in the last entry with the last color stop
             self.fill_colors(GRADIENT_DATA_LAST_STOP, GRADIENT_DATA_LAST_STOP + 1, &cur_color, &cur_color, &mut entries);
         }
 
         for entry in entries.iter() {
-            request.push(entry.start_color.into());
-            request.push(entry.end_color.into());
+            request.push(entry.start_color);
+            request.push(entry.end_color);
         }
     }
 }
 
 #[derive(Debug)]
 pub struct RadialGradientPrimitiveCpu {
     pub stops_range: ItemRange<GradientStop>,
     pub extend_mode: ExtendMode,
-    pub gpu_data_address: GpuStoreAddress,
     pub gpu_data_count: i32,
     pub gpu_blocks: [GpuBlockData; 3],
 }
 
 impl RadialGradientPrimitiveCpu {
     fn build_gpu_blocks_for_angle_radial(&self,
                                          display_list: &BuiltDisplayList,
                                          mut request: GpuDataRequest) {
@@ -464,92 +490,108 @@ pub struct TextRunPrimitiveCpu {
     pub glyph_instances: Vec<GlyphInstance>,
     pub color: ColorF,
     pub render_mode: FontRenderMode,
     pub glyph_options: Option<GlyphOptions>,
 }
 
 impl ToGpuBlocks for TextRunPrimitiveCpu {
     fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
-        request.push(self.color.into());
+        request.push(self.color);
 
         // Two glyphs are packed per GPU block.
         for glyph_chunk in self.glyph_instances.chunks(2) {
             // In the case of an odd number of glyphs, the
             // last glyph will get duplicated in the final
             // GPU block.
             let first_glyph = glyph_chunk.first().unwrap();
             let second_glyph = glyph_chunk.last().unwrap();
             request.push([first_glyph.point.x,
                           first_glyph.point.y,
                           second_glyph.point.x,
-                          second_glyph.point.y].into());
+                          second_glyph.point.y]);
         }
     }
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 struct GlyphPrimitive {
     offset: LayerPoint,
     padding: LayerPoint,
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 struct ClipRect {
     rect: LayerRect,
     mode: f32,
-    padding: [f32; 3],
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 struct ClipCorner {
     rect: LayerRect,
     outer_radius_x: f32,
     outer_radius_y: f32,
     inner_radius_x: f32,
     inner_radius_y: f32,
 }
 
+impl ToGpuBlocks for ClipCorner {
+    fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
+        self.write(&mut request)
+    }
+}
+
 impl ClipCorner {
+    fn write(&self, request: &mut GpuDataRequest) {
+        request.push(self.rect);
+        request.push([self.outer_radius_x, self.outer_radius_y,
+                      self.inner_radius_x, self.inner_radius_y,
+                     ]);
+    }
+
     fn uniform(rect: LayerRect, outer_radius: f32, inner_radius: f32) -> ClipCorner {
         ClipCorner {
-            rect: rect,
+            rect,
             outer_radius_x: outer_radius,
             outer_radius_y: outer_radius,
             inner_radius_x: inner_radius,
             inner_radius_y: inner_radius,
         }
     }
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 pub struct ImageMaskData {
     pub local_rect: LayerRect,
-    pub padding: DeviceRect,
+}
+
+impl ToGpuBlocks for ImageMaskData {
+    fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
+        request.push(self.local_rect);
+    }
 }
 
 #[derive(Debug, Clone)]
 pub struct ClipData {
     rect: ClipRect,
     top_left: ClipCorner,
     top_right: ClipCorner,
     bottom_left: ClipCorner,
     bottom_right: ClipCorner,
 }
 
 impl ClipData {
     pub fn from_clip_region(clip: &ComplexClipRegion) -> ClipData {
         ClipData {
             rect: ClipRect {
                 rect: clip.rect,
-                padding: [0.0; 3],
                 // TODO(gw): Support other clip modes for regions?
                 mode: ClipMode::Clip as u32 as f32,
             },
             top_left: ClipCorner {
                 rect: LayerRect::new(
                     LayerPoint::new(clip.rect.origin.x, clip.rect.origin.y),
                     LayerSize::new(clip.radii.top_left.width, clip.radii.top_left.height)),
                 outer_radius_x: clip.radii.top_left.width,
@@ -586,18 +628,17 @@ impl ClipData {
                 inner_radius_y: 0.0,
             },
         }
     }
 
     pub fn uniform(rect: LayerRect, radius: f32, mode: ClipMode) -> ClipData {
         ClipData {
             rect: ClipRect {
-                rect: rect,
-                padding: [0.0; 3],
+                rect,
                 mode: mode as u32 as f32,
             },
             top_left: ClipCorner::uniform(
                 LayerRect::new(
                     LayerPoint::new(rect.origin.x, rect.origin.y),
                     LayerSize::new(radius, radius)),
                 radius, 0.0),
             top_right: ClipCorner::uniform(
@@ -612,16 +653,24 @@ impl ClipData {
                 radius, 0.0),
             bottom_right: ClipCorner::uniform(
                 LayerRect::new(
                     LayerPoint::new(rect.origin.x + rect.size.width - radius, rect.origin.y + rect.size.height - radius),
                     LayerSize::new(radius, radius)),
                 radius, 0.0),
         }
     }
+
+    pub fn write(&self, request: &mut GpuDataRequest) {
+        request.push(self.rect.rect);
+        request.push([self.rect.mode, 0.0, 0.0, 0.0]);
+        for corner in &[&self.top_left, &self.top_right, &self.bottom_left, &self.bottom_right] {
+            corner.write(request);
+        }
+    }
 }
 
 #[derive(Debug)]
 pub enum PrimitiveContainer {
     Rectangle(RectanglePrimitive),
     TextRun(TextRunPrimitiveCpu),
     Image(ImagePrimitiveCpu),
     YuvImage(YuvImagePrimitiveCpu),
@@ -639,201 +688,185 @@ pub struct PrimitiveStore {
     pub cpu_text_runs: Vec<TextRunPrimitiveCpu>,
     pub cpu_images: Vec<ImagePrimitiveCpu>,
     pub cpu_yuv_images: Vec<YuvImagePrimitiveCpu>,
     pub cpu_gradients: Vec<GradientPrimitiveCpu>,
     pub cpu_radial_gradients: Vec<RadialGradientPrimitiveCpu>,
     pub cpu_metadata: Vec<PrimitiveMetadata>,
     pub cpu_borders: Vec<BorderPrimitiveCpu>,
     pub cpu_box_shadows: Vec<BoxShadowPrimitiveCpu>,
-
-    /// Gets uploaded directly to GPU via vertex texture.
-    pub gpu_data32: VertexDataStore<GpuBlock32>,
 }
 
 impl PrimitiveStore {
     pub fn new() -> PrimitiveStore {
         PrimitiveStore {
             cpu_metadata: Vec::new(),
             cpu_rectangles: Vec::new(),
             cpu_bounding_rects: Vec::new(),
             cpu_text_runs: Vec::new(),
             cpu_images: Vec::new(),
             cpu_yuv_images: Vec::new(),
             cpu_gradients: Vec::new(),
             cpu_radial_gradients: Vec::new(),
             cpu_borders: Vec::new(),
             cpu_box_shadows: Vec::new(),
-            gpu_data32: VertexDataStore::new(),
         }
     }
 
     pub fn recycle(self) -> Self {
         PrimitiveStore {
             cpu_metadata: recycle_vec(self.cpu_metadata),
             cpu_rectangles: recycle_vec(self.cpu_rectangles),
             cpu_bounding_rects: recycle_vec(self.cpu_bounding_rects),
             cpu_text_runs: recycle_vec(self.cpu_text_runs),
             cpu_images: recycle_vec(self.cpu_images),
             cpu_yuv_images: recycle_vec(self.cpu_yuv_images),
             cpu_gradients: recycle_vec(self.cpu_gradients),
             cpu_radial_gradients: recycle_vec(self.cpu_radial_gradients),
             cpu_borders: recycle_vec(self.cpu_borders),
             cpu_box_shadows: recycle_vec(self.cpu_box_shadows),
-            gpu_data32: self.gpu_data32.recycle(),
         }
     }
 
-    pub fn populate_clip_data(data: &mut [GpuBlock32], clip: ClipData) {
-        data[0] = GpuBlock32::from(clip.rect);
-        data[1] = GpuBlock32::from(clip.top_left);
-        data[2] = GpuBlock32::from(clip.top_right);
-        data[3] = GpuBlock32::from(clip.bottom_left);
-        data[4] = GpuBlock32::from(clip.bottom_right);
-    }
-
     pub fn add_primitive(&mut self,
                          local_rect: &LayerRect,
                          local_clip_rect: &LayerRect,
                          clips: Vec<ClipSource>,
                          clip_info: Option<MaskCacheInfo>,
                          container: PrimitiveContainer) -> PrimitiveIndex {
         let prim_index = self.cpu_metadata.len();
         self.cpu_bounding_rects.push(None);
 
         let metadata = match container {
             PrimitiveContainer::Rectangle(rect) => {
-                let is_opaque = rect.color.a == 1.0;
-
                 let metadata = PrimitiveMetadata {
-                    is_opaque: is_opaque,
-                    clips: clips,
+                    opacity: PrimitiveOpacity::from_alpha(rect.color.a),
+                    clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::Rectangle,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_rectangles.len()),
                     gpu_location: GpuCacheHandle::new(),
                     render_task: None,
                     clip_task: None,
                     local_rect: *local_rect,
                     local_clip_rect: *local_clip_rect,
                 };
 
                 self.cpu_rectangles.push(rect);
 
                 metadata
             }
             PrimitiveContainer::TextRun(text_cpu) => {
                 let metadata = PrimitiveMetadata {
-                    is_opaque: false,
-                    clips: clips,
+                    opacity: PrimitiveOpacity::translucent(),
+                    clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::TextRun,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_text_runs.len()),
                     gpu_location: GpuCacheHandle::new(),
                     render_task: None,
                     clip_task: None,
                     local_rect: *local_rect,
                     local_clip_rect: *local_clip_rect,
                 };
 
                 self.cpu_text_runs.push(text_cpu);
                 metadata
             }
             PrimitiveContainer::Image(image_cpu) => {
                 let metadata = PrimitiveMetadata {
-                    is_opaque: false,
-                    clips: clips,
+                    opacity: PrimitiveOpacity::translucent(),
+                    clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::Image,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_images.len()),
                     gpu_location: GpuCacheHandle::new(),
                     render_task: None,
                     clip_task: None,
                     local_rect: *local_rect,
                     local_clip_rect: *local_clip_rect,
                 };
 
                 self.cpu_images.push(image_cpu);
                 metadata
             }
             PrimitiveContainer::YuvImage(image_cpu) => {
                 let metadata = PrimitiveMetadata {
-                    is_opaque: true,
-                    clips: clips,
+                    opacity: PrimitiveOpacity::opaque(),
+                    clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::YuvImage,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_yuv_images.len()),
                     gpu_location: GpuCacheHandle::new(),
                     render_task: None,
                     clip_task: None,
                     local_rect: *local_rect,
                     local_clip_rect: *local_clip_rect,
                 };
 
                 self.cpu_yuv_images.push(image_cpu);
                 metadata
             }
             PrimitiveContainer::Border(border_cpu) => {
                 let metadata = PrimitiveMetadata {
-                    is_opaque: false,
-                    clips: clips,
+                    opacity: PrimitiveOpacity::translucent(),
+                    clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::Border,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_borders.len()),
                     gpu_location: GpuCacheHandle::new(),
                     render_task: None,
                     clip_task: None,
                     local_rect: *local_rect,
                     local_clip_rect: *local_clip_rect,
                 };
 
                 self.cpu_borders.push(border_cpu);
                 metadata
             }
             PrimitiveContainer::AlignedGradient(gradient_cpu) => {
                 let metadata = PrimitiveMetadata {
-                    // TODO: calculate if the gradient is actually opaque
-                    is_opaque: false,
-                    clips: clips,
+                    opacity: PrimitiveOpacity::translucent(),
+                    clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::AlignedGradient,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_gradients.len()),
                     gpu_location: GpuCacheHandle::new(),
                     render_task: None,
                     clip_task: None,
                     local_rect: *local_rect,
                     local_clip_rect: *local_clip_rect,
                 };
 
                 self.cpu_gradients.push(gradient_cpu);
                 metadata
             }
             PrimitiveContainer::AngleGradient(gradient_cpu) => {
                 let metadata = PrimitiveMetadata {
                     // TODO: calculate if the gradient is actually opaque
-                    is_opaque: false,
-                    clips: clips,
+                    opacity: PrimitiveOpacity::translucent(),
+                    clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::AngleGradient,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_gradients.len()),
                     gpu_location: GpuCacheHandle::new(),
                     render_task: None,
                     clip_task: None,
                     local_rect: *local_rect,
                     local_clip_rect: *local_clip_rect,
                 };
 
                 self.cpu_gradients.push(gradient_cpu);
                 metadata
             }
             PrimitiveContainer::RadialGradient(radial_gradient_cpu) => {
                 let metadata = PrimitiveMetadata {
                     // TODO: calculate if the gradient is actually opaque
-                    is_opaque: false,
-                    clips: clips,
+                    opacity: PrimitiveOpacity::translucent(),
+                    clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::RadialGradient,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_radial_gradients.len()),
                     gpu_location: GpuCacheHandle::new(),
                     render_task: None,
                     clip_task: None,
                     local_rect: *local_rect,
                     local_clip_rect: *local_clip_rect,
@@ -864,18 +897,18 @@ impl PrimitiveStore {
                 // shadow shader needs to run on.
                 // TODO(gw): In the future, we can probably merge the box shadow
                 // primitive (stretch) shader with the generic cached primitive shader.
                 let render_task = RenderTask::new_prim_cache(cache_key,
                                                              cache_size,
                                                              PrimitiveIndex(prim_index));
 
                 let metadata = PrimitiveMetadata {
-                    is_opaque: false,
-                    clips: clips,
+                    opacity: PrimitiveOpacity::translucent(),
+                    clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::BoxShadow,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_box_shadows.len()),
                     gpu_location: GpuCacheHandle::new(),
                     render_task: Some(render_task),
                     clip_task: None,
                     local_rect: *local_rect,
                     local_clip_rect: *local_clip_rect,
@@ -899,50 +932,51 @@ impl PrimitiveStore {
         self.cpu_metadata.len()
     }
 
     pub fn build_bounding_rect(&mut self,
                                prim_index: PrimitiveIndex,
                                screen_rect: &DeviceIntRect,
                                layer_transform: &LayerToWorldTransform,
                                layer_combined_local_clip_rect: &LayerRect,
-                               device_pixel_ratio: f32) -> bool {
+                               device_pixel_ratio: f32) -> Option<(LayerRect, DeviceIntRect)> {
         let metadata = &self.cpu_metadata[prim_index.0];
+        let local_rect = metadata.local_rect
+                                 .intersection(&metadata.local_clip_rect)
+                                 .and_then(|rect| rect.intersection(layer_combined_local_clip_rect));
 
-        let bounding_rect = metadata.local_rect
-                                    .intersection(&metadata.local_clip_rect)
-                                    .and_then(|rect| rect.intersection(layer_combined_local_clip_rect))
-                                    .and_then(|ref local_rect| {
-            let xf_rect = TransformedRect::new(local_rect,
+        let bounding_rect = local_rect.and_then(|local_rect| {
+            let xf_rect = TransformedRect::new(&local_rect,
                                                layer_transform,
                                                device_pixel_ratio);
             xf_rect.bounding_rect.intersection(screen_rect)
         });
 
         self.cpu_bounding_rects[prim_index.0] = bounding_rect;
-        bounding_rect.is_some()
+        bounding_rect.map(|screen_bound| (local_rect.unwrap(), screen_bound))
     }
 
     /// Returns true if the bounding box needs to be updated.
     pub fn prepare_prim_for_render(&mut self,
                                    prim_index: PrimitiveIndex,
                                    resource_cache: &mut ResourceCache,
                                    gpu_cache: &mut GpuCache,
                                    layer_transform: &LayerToWorldTransform,
                                    device_pixel_ratio: f32,
-                                   display_list: &BuiltDisplayList) {
+                                   display_list: &BuiltDisplayList)
+                                   -> &mut PrimitiveMetadata {
 
         let metadata = &mut self.cpu_metadata[prim_index.0];
 
         if let Some(ref mut clip_info) = metadata.clip_cache_info {
-            clip_info.update(&metadata.clips,
-                             layer_transform,
-                             &mut self.gpu_data32,
-                             device_pixel_ratio,
-                             display_list);
+            clip_info.update(&metadata.clips, layer_transform, gpu_cache, device_pixel_ratio);
+
+            //TODO-LCCR: we could tighten up the `local_clip_rect` here
+            // but that would require invalidating the whole GPU block
+
             for clip in &metadata.clips {
                 if let ClipSource::Region(ClipRegion{ image_mask: Some(ref mask), .. }, ..) = *clip {
                     resource_cache.request_image(mask.image, ImageRendering::Auto, None);
                 }
             }
         }
 
         match metadata.prim_kind {
@@ -1015,44 +1049,41 @@ impl PrimitiveStore {
                     ImagePrimitiveKind::Image(image_key, image_rendering, tile_offset, tile_spacing) => {
                         resource_cache.request_image(image_key, image_rendering, tile_offset);
 
                         // TODO(gw): This doesn't actually need to be calculated each frame.
                         // It's cheap enough that it's not worth introducing a cache for images
                         // right now, but if we introduce a cache for images for some other
                         // reason then we might as well cache this with it.
                         let image_properties = resource_cache.get_image_properties(image_key);
-                        metadata.is_opaque = image_properties.descriptor.is_opaque &&
-                                             tile_spacing.width == 0.0 &&
-                                             tile_spacing.height == 0.0;
+                        metadata.opacity.is_opaque = image_properties.descriptor.is_opaque &&
+                                                     tile_spacing.width == 0.0 &&
+                                                     tile_spacing.height == 0.0;
                     }
                     ImagePrimitiveKind::WebGL(..) => {}
                 }
             }
             PrimitiveKind::YuvImage => {
                 let image_cpu = &mut self.cpu_yuv_images[metadata.cpu_prim_index.0];
 
                 let channel_num = image_cpu.format.get_plane_num();
                 debug_assert!(channel_num <= 3);
                 for channel in 0..channel_num {
                     resource_cache.request_image(image_cpu.yuv_key[channel], image_cpu.image_rendering, None);
                 }
-
-                // TODO(nical): Currently assuming no tile_spacing for yuv images.
-                metadata.is_opaque = true;
             }
             PrimitiveKind::AlignedGradient |
             PrimitiveKind::AngleGradient |
             PrimitiveKind::RadialGradient => {}
         }
 
         // Mark this GPU resource as required for this frame.
         if let Some(mut request) = gpu_cache.request(&mut metadata.gpu_location) {
-            request.push(metadata.local_rect.into());
-            request.push(metadata.local_clip_rect.into());
+            request.push(metadata.local_rect);
+            request.push(metadata.local_clip_rect);
 
             match metadata.prim_kind {
                 PrimitiveKind::Rectangle => {
                     let rect = &self.cpu_rectangles[metadata.cpu_prim_index.0];
                     rect.write_gpu_blocks(request);
                 }
                 PrimitiveKind::Border => {
                     let border = &self.cpu_borders[metadata.cpu_prim_index.0];
@@ -1067,18 +1098,18 @@ impl PrimitiveStore {
                     image.write_gpu_blocks(request);
                 }
                 PrimitiveKind::YuvImage => {
                     let yuv_image = &self.cpu_yuv_images[metadata.cpu_prim_index.0];
                     yuv_image.write_gpu_blocks(request);
                 }
                 PrimitiveKind::AlignedGradient => {
                     let gradient = &self.cpu_gradients[metadata.cpu_prim_index.0];
-                    gradient.build_gpu_blocks_for_aligned(display_list,
-                                                          request);
+                    metadata.opacity = gradient.build_gpu_blocks_for_aligned(display_list,
+                                                                             request);
                 }
                 PrimitiveKind::AngleGradient => {
                     let gradient = &self.cpu_gradients[metadata.cpu_prim_index.0];
                     gradient.build_gpu_blocks_for_angle_radial(display_list,
                                                                request);
                 }
                 PrimitiveKind::RadialGradient => {
                     let gradient = &self.cpu_radial_gradients[metadata.cpu_prim_index.0];
@@ -1086,51 +1117,22 @@ impl PrimitiveStore {
                                                                request);
                 }
                 PrimitiveKind::TextRun => {
                     let text = &self.cpu_text_runs[metadata.cpu_prim_index.0];
                     text.write_gpu_blocks(request);
                 }
             }
         }
+
+        metadata
     }
 }
 
 
-macro_rules! define_gpu_block {
-    ($name:ident: $ty:ty = $($derive:ident),* ) => (
-        #[derive(Clone)]
-        #[repr(C)]
-        pub struct $name {
-            data: $ty,
-        }
-
-        impl Default for $name {
-            fn default() -> $name {
-                $name {
-                    data: unsafe { mem::uninitialized() }
-                }
-            }
-        }
-
-        $(
-            impl From<$derive> for $name {
-                fn from(data: $derive) -> $name {
-                    unsafe { mem::transmute(data) }
-                }
-            }
-        )*
-    )
-}
-
-define_gpu_block!(GpuBlock32: [f32; 8] =
-    ClipCorner, ClipRect, ImageMaskData,
-    BorderCornerClipData, BorderCornerDashClipData, BorderCornerDotClipData
-);
-
 //Test for one clip region contains another
 trait InsideTest<T> {
     fn might_contain(&self, clip: &T) -> bool;
 }
 
 impl InsideTest<ComplexClipRegion> for ComplexClipRegion {
     // Returns true if clip is inside self, can return false negative
     fn might_contain(&self, clip: &ComplexClipRegion) -> bool {
--- a/gfx/webrender/src/profiler.rs
+++ b/gfx/webrender/src/profiler.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use debug_render::DebugRenderer;
 use device::{Device, GpuMarker, GpuSample, NamedTag};
 use euclid::{Point2D, Size2D, Rect, vec2};
 use std::collections::vec_deque::VecDeque;
 use std::f32;
 use std::mem;
-use webrender_traits::ColorF;
+use api::ColorF;
 use time::precise_time_ns;
 
 const GRAPH_WIDTH: f32 = 1024.0;
 const GRAPH_HEIGHT: f32 = 320.0;
 const GRAPH_PADDING: f32 = 8.0;
 const GRAPH_FRAME_HEIGHT: f32 = 16.0;
 const PROFILE_PADDING: f32 = 10.0;
 
@@ -40,17 +40,17 @@ trait ProfileCounter {
 pub struct IntProfileCounter {
     description: &'static str,
     value: usize,
 }
 
 impl IntProfileCounter {
     fn new(description: &'static str) -> IntProfileCounter {
         IntProfileCounter {
-            description: description,
+            description,
             value: 0,
         }
     }
 
     fn reset(&mut self) {
         self.value = 0;
     }
 
@@ -89,17 +89,17 @@ pub struct ResourceProfileCounter {
     description: &'static str,
     value: usize,
     size: usize,
 }
 
 impl ResourceProfileCounter {
     fn new(description: &'static str) -> ResourceProfileCounter {
         ResourceProfileCounter {
-            description: description,
+            description,
             value: 0,
             size: 0,
         }
     }
 
     #[allow(dead_code)]
     fn reset(&mut self) {
         self.value = 0;
@@ -129,19 +129,19 @@ pub struct TimeProfileCounter {
     description: &'static str,
     nanoseconds: u64,
     invert: bool,
 }
 
 impl TimeProfileCounter {
     pub fn new(description: &'static str, invert: bool) -> TimeProfileCounter {
         TimeProfileCounter {
-            description: description,
+            description,
             nanoseconds: 0,
-            invert: invert,
+            invert,
         }
     }
 
     fn reset(&mut self) {
         self.nanoseconds = 0;
     }
 
     #[allow(dead_code)]
@@ -190,23 +190,23 @@ pub struct AverageTimeProfileCounter {
     num_samples: u64,
     nanoseconds: u64,
     invert: bool,
 }
 
 impl AverageTimeProfileCounter {
     pub fn new(description: &'static str, invert: bool, average_over_ns: u64) -> AverageTimeProfileCounter {
         AverageTimeProfileCounter {
-            description: description,
-            average_over_ns: average_over_ns,
+            description,
+            average_over_ns,
             start_ns: precise_time_ns(),
             sum_ns: 0,
             num_samples: 0,
             nanoseconds: 0,
-            invert: invert,
+            invert,
         }
     }
 
     #[allow(dead_code)]
     fn reset(&mut self) {
         self.start_ns = precise_time_ns();
         self.nanoseconds = 0;
         self.sum_ns = 0;
@@ -420,17 +420,17 @@ struct GraphStats {
 struct ProfileGraph {
     max_samples: usize,
     values: VecDeque<f32>,
 }
 
 impl ProfileGraph {
     fn new(max_samples: usize) -> ProfileGraph {
         ProfileGraph {
-            max_samples: max_samples,
+            max_samples,
             values: VecDeque::new(),
         }
     }
 
     fn push(&mut self, ns: u64) {
         let ms = ns as f64 / 1000000.0;
         if self.values.len() == self.max_samples {
             self.values.pop_back();
@@ -551,18 +551,18 @@ impl GpuFrameCollection {
         }
     }
 
     fn push(&mut self, total_time: u64, samples: Vec<GpuSample<GpuProfileTag>>) {
         if self.frames.len() == 20 {
             self.frames.pop_back();
         }
         self.frames.push_front(GpuFrame {
-            total_time: total_time,
-            samples: samples,
+            total_time,
+            samples,
         });
     }
 }
 
 impl GpuFrameCollection {
     fn draw(&self,
             x: f32,
             y: f32,
--- a/gfx/webrender/src/record.rs
+++ b/gfx/webrender/src/record.rs
@@ -4,17 +4,17 @@
 
 use bincode::{Infinite, serialize};
 use std::fmt::Debug;
 use std::mem;
 use std::any::TypeId;
 use std::fs::File;
 use std::io::Write;
 use std::path::PathBuf;
-use webrender_traits::ApiMsg;
+use api::ApiMsg;
 use byteorder::{LittleEndian, WriteBytesExt};
 
 pub static WEBRENDER_RECORDING_HEADER: u64 = 0xbeefbeefbeefbe01u64;
 
 pub trait ApiRecordingReceiver: Send + Debug {
     fn write_msg(&mut self, frame: u32, msg: &ApiMsg);
     fn write_payload(&mut self, frame: u32, data: &[u8]);
 }
@@ -32,17 +32,17 @@ impl BinaryRecorder {
         let apimsg_type_id = unsafe {
             assert!(mem::size_of::<TypeId>() == mem::size_of::<u64>());
             mem::transmute::<TypeId, u64>(TypeId::of::<ApiMsg>())
         };
         file.write_u64::<LittleEndian>(WEBRENDER_RECORDING_HEADER).ok();
         file.write_u64::<LittleEndian>(apimsg_type_id).ok();
 
         BinaryRecorder {
-            file: file,
+            file,
         }
     }
 
     fn write_length_and_data(&mut self, data: &[u8]) {
         self.file.write_u32::<LittleEndian>(data.len() as u32).ok();
         self.file.write(data).ok();
     }
 }
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -5,31 +5,31 @@
 use frame::Frame;
 use frame_builder::FrameBuilderConfig;
 use gpu_cache::GpuCache;
 use internal_types::{SourceTexture, ResultMsg, RendererFrame};
 use profiler::{BackendProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
 use record::ApiRecordingReceiver;
 use resource_cache::ResourceCache;
 use scene::Scene;
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
 use std::sync::{Arc, Mutex};
 use std::sync::mpsc::Sender;
 use texture_cache::TextureCache;
 use time::precise_time_ns;
 use thread_profiler::register_thread_with_profiler;
 use rayon::ThreadPool;
 use webgl_types::{GLContextHandleWrapper, GLContextWrapper};
-use webrender_traits::channel::{MsgReceiver, PayloadReceiver, PayloadReceiverHelperMethods};
-use webrender_traits::channel::{PayloadSender, PayloadSenderHelperMethods};
-use webrender_traits::{ApiMsg, BlobImageRenderer, BuiltDisplayList, DeviceIntPoint};
-use webrender_traits::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, IdNamespace, ImageData};
-use webrender_traits::{LayerPoint, PipelineId, RenderDispatcher, RenderNotifier};
-use webrender_traits::{VRCompositorCommand, VRCompositorHandler, WebGLCommand, WebGLContextId};
-use webrender_traits::{FontTemplate};
+use api::channel::{MsgReceiver, PayloadReceiver, PayloadReceiverHelperMethods};
+use api::channel::{PayloadSender, PayloadSenderHelperMethods};
+use api::{ApiMsg, BlobImageRenderer, BuiltDisplayList, DeviceIntPoint};
+use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, IdNamespace, ImageData};
+use api::{LayerPoint, PipelineId, RenderDispatcher, RenderNotifier};
+use api::{VRCompositorCommand, VRCompositorHandler, WebGLCommand, WebGLContextId};
+use api::{FontTemplate};
 
 #[cfg(feature = "webgl")]
 use offscreen_gl_context::GLContextDispatcher;
 
 #[cfg(not(feature = "webgl"))]
 use webgl_types::GLContextDispatcher;
 
 /// The render backend is responsible for transforming high level display lists into
@@ -55,23 +55,31 @@ pub struct RenderBackend {
     resource_cache: ResourceCache,
 
     scene: Scene,
     frame: Frame,
 
     notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
     webrender_context_handle: Option<GLContextHandleWrapper>,
     webgl_contexts: HashMap<WebGLContextId, GLContextWrapper>,
+    dirty_webgl_contexts: HashSet<WebGLContextId>,
     current_bound_webgl_context_id: Option<WebGLContextId>,
     recorder: Option<Box<ApiRecordingReceiver>>,
     main_thread_dispatcher: Arc<Mutex<Option<Box<RenderDispatcher>>>>,
 
     next_webgl_id: usize,
 
-    vr_compositor_handler: Arc<Mutex<Option<Box<VRCompositorHandler>>>>
+    vr_compositor_handler: Arc<Mutex<Option<Box<VRCompositorHandler>>>>,
+
+    // A helper switch to prevent any frames rendering triggered by scrolling
+    // messages between `SetDisplayList` and `GenerateFrame`.
+    // If we allow them, then a reftest that scrolls a few layers before generating
+    // the first frame would produce inconsistent rendering results, because
+    // scroll events are not necessarily received in deterministic order.
+    render_on_scroll: bool,
 }
 
 impl RenderBackend {
     pub fn new(api_rx: MsgReceiver<ApiMsg>,
                payload_rx: PayloadReceiver,
                payload_tx: PayloadSender,
                result_tx: Sender<ResultMsg>,
                hidpi_factor: f32,
@@ -86,39 +94,52 @@ impl RenderBackend {
                vr_compositor_handler: Arc<Mutex<Option<Box<VRCompositorHandler>>>>,
                initial_window_size: DeviceUintSize) -> RenderBackend {
 
         let resource_cache = ResourceCache::new(texture_cache, workers, blob_image_renderer);
 
         register_thread_with_profiler("Backend".to_string());
 
         RenderBackend {
-            api_rx: api_rx,
-            payload_rx: payload_rx,
-            payload_tx: payload_tx,
-            result_tx: result_tx,
-            hidpi_factor: hidpi_factor,
+            api_rx,
+            payload_rx,
+            payload_tx,
+            result_tx,
+            hidpi_factor,
             page_zoom_factor: 1.0,
             pinch_zoom_factor: 1.0,
             pan: DeviceIntPoint::zero(),
-            resource_cache: resource_cache,
+            resource_cache,
             gpu_cache: GpuCache::new(),
             scene: Scene::new(),
             frame: Frame::new(config),
             next_namespace_id: IdNamespace(1),
-            notifier: notifier,
-            webrender_context_handle: webrender_context_handle,
+            notifier,
+            webrender_context_handle,
             webgl_contexts: HashMap::new(),
+            dirty_webgl_contexts: HashSet::new(),
             current_bound_webgl_context_id: None,
-            recorder: recorder,
-            main_thread_dispatcher: main_thread_dispatcher,
+            recorder,
+            main_thread_dispatcher,
             next_webgl_id: 0,
-            vr_compositor_handler: vr_compositor_handler,
+            vr_compositor_handler,
             window_size: initial_window_size,
             inner_rect: DeviceUintRect::new(DeviceUintPoint::zero(), initial_window_size),
+            render_on_scroll: false,
+        }
+    }
+
+    fn scroll_frame(&mut self, frame_maybe: Option<RendererFrame>,
+                    profile_counters: &mut BackendProfileCounters) {
+        match frame_maybe {
+            Some(frame) => {
+                self.publish_frame(frame, profile_counters);
+                self.notify_compositor_of_new_scroll_frame(true)
+            }
+            None => self.notify_compositor_of_new_scroll_frame(false),
         }
     }
 
     pub fn run(&mut self, mut profile_counters: BackendProfileCounters) {
         let mut frame_counter: u32 = 0;
 
         loop {
             let msg = self.api_rx.recv();
@@ -227,16 +248,18 @@ impl RenderBackend {
                                                             epoch,
                                                             built_display_list,
                                                             background_color,
                                                             viewport_size,
                                                             content_size);
                                 self.build_scene();
                             });
 
+                            self.render_on_scroll = false; //wait for `GenerateFrame`
+
                             // Note: this isn't quite right as auxiliary values will be
                             // pulled out somewhere in the prim_store, but aux values are
                             // really simple and cheap to access, so it's not a big deal.
                             let display_list_consumed_time = precise_time_ns();
 
                             profile_counters.ipc.set(builder_start_time, builder_finish_time,
                                                      display_list_received_time, display_list_consumed_time,
                                                      display_list_len);
@@ -254,70 +277,61 @@ impl RenderBackend {
                             })
                         }
                         ApiMsg::Scroll(delta, cursor, move_phase) => {
                             profile_scope!("Scroll");
                             let frame = {
                                 let counters = &mut profile_counters.resources.texture_cache;
                                 let gpu_cache_counters = &mut profile_counters.resources.gpu_cache;
                                 profile_counters.total_time.profile(|| {
-                                    if self.frame.scroll(delta, cursor, move_phase) {
+                                    if self.frame.scroll(delta, cursor, move_phase) && self.render_on_scroll {
                                         Some(self.render(counters, gpu_cache_counters))
                                     } else {
                                         None
                                     }
                                 })
                             };
 
-                            match frame {
-                                Some(frame) => {
-                                    self.publish_frame(frame, &mut profile_counters);
-                                    self.notify_compositor_of_new_scroll_frame(true)
-                                }
-                                None => self.notify_compositor_of_new_scroll_frame(false),
-                            }
+                            self.scroll_frame(frame, &mut profile_counters);
                         }
                         ApiMsg::ScrollNodeWithId(origin, id, clamp) => {
                             profile_scope!("ScrollNodeWithScrollId");
                             let frame = {
                                 let counters = &mut profile_counters.resources.texture_cache;
                                 let gpu_cache_counters = &mut profile_counters.resources.gpu_cache;
                                 profile_counters.total_time.profile(|| {
-                                    if self.frame.scroll_node(origin, id, clamp) {
+                                    if self.frame.scroll_node(origin, id, clamp) && self.render_on_scroll {
                                         Some(self.render(counters, gpu_cache_counters))
                                     } else {
                                         None
                                     }
                                 })
                             };
 
-                            match frame {
-                                Some(frame) => {
-                                    self.publish_frame(frame, &mut profile_counters);
-                                    self.notify_compositor_of_new_scroll_frame(true)
-                                }
-                                None => self.notify_compositor_of_new_scroll_frame(false),
-                            }
-
+                            self.scroll_frame(frame, &mut profile_counters);
                         }
                         ApiMsg::TickScrollingBounce => {
                             profile_scope!("TickScrollingBounce");
                             let frame = {
                                 let counters = &mut profile_counters.resources.texture_cache;
                                 let gpu_cache_counters = &mut profile_counters.resources.gpu_cache;
                                 profile_counters.total_time.profile(|| {
                                     self.frame.tick_scrolling_bounce_animations();
-                                    self.render(counters, gpu_cache_counters)
+                                    if self.render_on_scroll {
+                                        Some(self.render(counters, gpu_cache_counters))
+                                    } else {
+                                        None
+                                    }
                                 })
                             };
 
-                            self.publish_frame_and_notify_compositor(frame, &mut profile_counters);
+                            self.scroll_frame(frame, &mut profile_counters);
                         }
                         ApiMsg::TranslatePointToLayerSpace(..) => {
-                            panic!("unused api - remove from webrender_traits");
+                            panic!("unused api - remove from webrender_api");
                         }
                         ApiMsg::GetScrollNodeState(tx) => {
                             profile_scope!("GetScrollNodeState");
                             tx.send(self.frame.get_scroll_node_state()).unwrap()
                         }
                         ApiMsg::RequestWebGLContext(size, attributes, tx) => {
                             if let Some(ref wrapper) = self.webrender_context_handle {
                                 let dispatcher: Option<Box<GLContextDispatcher>> = if cfg!(target_os = "windows") {
@@ -340,16 +354,17 @@ impl RenderBackend {
 
                                         let (real_size, texture_id, limits) = ctx.get_info();
 
                                         self.webgl_contexts.insert(id, ctx);
 
                                         self.resource_cache
                                             .add_webgl_texture(id, SourceTexture::WebGL(texture_id),
                                                                real_size);
+                                        self.dirty_webgl_contexts.insert(id);
 
                                         tx.send(Ok((id, limits))).unwrap();
                                     },
                                     Err(msg) => {
                                         tx.send(Err(msg.to_owned())).unwrap();
                                     }
                                 }
                             } else {
@@ -364,39 +379,42 @@ impl RenderBackend {
                             }
                             match ctx.resize(&size) {
                                 Ok(_) => {
                                     // Update webgl texture size. Texture id may change too.
                                     let (real_size, texture_id, _) = ctx.get_info();
                                     self.resource_cache
                                         .update_webgl_texture(context_id, SourceTexture::WebGL(texture_id),
                                                               real_size);
+                                    self.dirty_webgl_contexts.insert(context_id);
                                 },
                                 Err(msg) => {
                                     error!("Error resizing WebGLContext: {}", msg);
                                 }
                             }
                         }
                         ApiMsg::WebGLCommand(context_id, command) => {
                             // TODO: Buffer the commands and only apply them here if they need to
                             // be synchronous.
                             let ctx = &self.webgl_contexts[&context_id];
                             if Some(context_id) != self.current_bound_webgl_context_id {
                                 ctx.make_current();
                                 self.current_bound_webgl_context_id = Some(context_id);
                             }
                             ctx.apply_command(command);
+                            self.dirty_webgl_contexts.insert(context_id);
                         },
 
                         ApiMsg::VRCompositorCommand(context_id, command) => {
                             if Some(context_id) != self.current_bound_webgl_context_id {
                                 self.webgl_contexts[&context_id].make_current();
                                 self.current_bound_webgl_context_id = Some(context_id);
                             }
                             self.handle_vr_compositor_command(context_id, command);
+                            self.dirty_webgl_contexts.insert(context_id);
                         }
                         ApiMsg::GenerateFrame(property_bindings) => {
                             profile_scope!("GenerateFrame");
 
                             // Ideally, when there are property bindings present,
                             // we won't need to rebuild the entire frame here.
                             // However, to avoid conflicts with the ongoing work to
                             // refactor how scroll roots + transforms work, this
@@ -408,16 +426,18 @@ impl RenderBackend {
                             //           rebuild of the frame!
                             if let Some(property_bindings) = property_bindings {
                                 self.scene.properties.set_properties(property_bindings);
                                 profile_counters.total_time.profile(|| {
                                     self.build_scene();
                                 });
                             }
 
+                            self.render_on_scroll = true;
+
                             let frame = {
                                 let counters = &mut profile_counters.resources.texture_cache;
                                 let gpu_cache_counters = &mut profile_counters.resources.gpu_cache;
                                 profile_counters.total_time.profile(|| {
                                     self.render(counters, gpu_cache_counters)
                                 })
                             };
                             if self.scene.root_pipeline_id.is_some() {
@@ -471,20 +491,28 @@ impl RenderBackend {
 
         // When running in OSMesa mode with texture sharing,
         // a flush is required on any GL contexts to ensure
         // that read-back from the shared texture returns
         // valid data! This should be fine to have run on all
         // implementations - a single flush for each webgl
         // context at the start of a render frame should
         // incur minimal cost.
-        for (_, webgl_context) in &self.webgl_contexts {
-            webgl_context.make_current();
-            webgl_context.apply_command(WebGLCommand::Flush);
-            webgl_context.unbind();
+        // glFlush is not enough in some GPUs.
+        // glFlush doesn't guarantee the completion of the GL commands when the shared texture is sampled.
+        // This leads to some graphic glitches on some demos or even nothing being rendered at all (GPU Mali-T880).
+        // glFinish guarantees the completion of the commands but it may hurt performance a lot.
+        // Sync Objects are the recommended way to ensure that textures are ready in OpenGL 3.0+.
+        // They are more performant than glFinish and guarantee the completion of the GL commands.
+        for (id, webgl_context) in &self.webgl_contexts {
+            if self.dirty_webgl_contexts.remove(&id) {
+                webgl_context.make_current();
+                webgl_context.apply_command(WebGLCommand::FenceAndWaitSync);
+                webgl_context.unbind();
+            }
         }
 
         let accumulated_scale_factor = self.accumulated_scale_factor();
         self.frame.create(&self.scene,
                           &mut self.resource_cache,
                           self.window_size,
                           self.inner_rect,
                           accumulated_scale_factor);
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -1,21 +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/. */
 
 use gpu_cache::GpuCacheHandle;
 use internal_types::{HardwareCompositeOp, LowLevelFilterOp};
-use mask_cache::{MaskBounds, MaskCacheInfo};
+use mask_cache::MaskCacheInfo;
 use prim_store::{PrimitiveCacheKey, PrimitiveIndex};
 use std::{cmp, f32, i32, mem, usize};
 use tiling::{ClipScrollGroupIndex, PackedLayerIndex, RenderPass, RenderTargetIndex};
 use tiling::{RenderTargetKind, StackingContextIndex};
-use webrender_traits::{ClipId, DeviceIntLength, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
-use webrender_traits::{MixBlendMode};
+use api::{ClipId, DeviceIntLength, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
+use api::{MixBlendMode};
 
 const FLOATS_PER_RENDER_TASK_INFO: usize = 12;
 
 #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
 pub struct RenderTaskIndex(pub usize);
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 pub enum RenderTaskKey {
@@ -79,32 +79,26 @@ pub enum MaskSegment {
 #[derive(Debug, Copy, Clone)]
 #[repr(C)]
 pub enum MaskGeometryKind {
     Default,        // Draw the entire rect
     CornersOnly,    // Draw the corners (simple axis aligned mask)
     // TODO(gw): Add more types here (e.g. 4 rectangles outside the inner rect)
 }
 
+pub type ClipWorkItem = (PackedLayerIndex, MaskCacheInfo);
+
 #[derive(Debug, Clone)]
 pub struct CacheMaskTask {
     actual_rect: DeviceIntRect,
     inner_rect: DeviceIntRect,
-    pub clips: Vec<(PackedLayerIndex, MaskCacheInfo)>,
+    pub clips: Vec<ClipWorkItem>,
     pub geometry_kind: MaskGeometryKind,
 }
 
-#[derive(Debug)]
-pub enum MaskResult {
-    /// The mask is completely outside the region
-    Outside,
-    /// The mask is inside and needs to be processed
-    Inside(RenderTask),
-}
-
 #[derive(Debug, Clone)]
 pub struct RenderTaskData {
     pub data: [f32; FLOATS_PER_RENDER_TASK_INFO],
 }
 
 impl RenderTaskData {
     pub fn empty() -> RenderTaskData {
         RenderTaskData {
@@ -144,19 +138,19 @@ pub struct RenderTask {
 
 impl RenderTask {
     pub fn new_alpha_batch(task_index: RenderTaskIndex,
                            screen_origin: DeviceIntPoint,
                            location: RenderTaskLocation) -> RenderTask {
         RenderTask {
             id: RenderTaskId::Static(task_index),
             children: Vec::new(),
-            location: location,
+            location,
             kind: RenderTaskKind::Alpha(AlphaRenderTask {
-                screen_origin: screen_origin,
+                screen_origin,
                 items: Vec::new(),
             }),
         }
     }
 
     pub fn new_dynamic_alpha_batch(task_index: RenderTaskIndex,
                                    rect: &DeviceIntRect) -> RenderTask {
         let location = RenderTaskLocation::Dynamic(None, rect.size);
@@ -179,93 +173,68 @@ impl RenderTask {
         RenderTask {
             id: RenderTaskId::Dynamic(RenderTaskKey::CopyFramebuffer(key)),
             children: Vec::new(),
             location: RenderTaskLocation::Dynamic(None, screen_rect.size),
             kind: RenderTaskKind::Readback(screen_rect),
         }
     }
 
-    pub fn new_mask(actual_rect: DeviceIntRect,
+    pub fn new_mask(task_rect: DeviceIntRect,
                     mask_key: MaskCacheKey,
-                    clips: &[(PackedLayerIndex, MaskCacheInfo)])
-                    -> MaskResult {
-        if clips.is_empty() {
-            return MaskResult::Outside;
-        }
-
-        // We scan through the clip stack and detect if our actual rectangle
-        // is in the intersection of all of all the outer bounds,
-        // and if it's completely inside the intersection of all of the inner bounds.
-
-        // TODO(gw): If we encounter a clip with unknown bounds, we'll just use
-        // the original rect. This is overly conservative, but can
-        // be optimized later.
-        let mut result = Some(actual_rect);
-        for &(_, ref clip) in clips {
-            match *clip.bounds.as_ref().unwrap() {
-                MaskBounds::OuterInner(ref outer, _) |
-                MaskBounds::Outer(ref outer) => {
-                    result = result.and_then(|rect| {
-                        rect.intersection(&outer.bounding_rect)
-                    });
+                    raw_clips: &[ClipWorkItem],
+                    extra_clip: Option<ClipWorkItem>)
+                    -> Option<RenderTask> {
+        /// Filter out all the clip instances that don't contribute to the result
+        let mut inner_rect = Some(task_rect);
+        let clips: Vec<_> = raw_clips.iter()
+                                     .chain(extra_clip.iter())
+                                     .filter(|&&(_, ref clip_info)| {
+            match clip_info.bounds.inner {
+                Some(ref inner) if !inner.device_rect.is_empty() => {
+                    inner_rect = inner_rect.and_then(|r| r.intersection(&inner.device_rect));
+                    !inner.device_rect.contains_rect(&task_rect)
                 }
-                MaskBounds::None => {
-                    result = Some(actual_rect);
-                    break;
+                _ => {
+                    inner_rect = None;
+                    true
                 }
             }
-        }
-
-        let task_rect = match result {
-            None => return MaskResult::Outside,
-            Some(rect) => rect,
-        };
+        }).cloned().collect();
 
-        // Accumulate inner rects. As soon as we encounter
-        // a clip mask where we don't have or don't know
-        // the inner rect, this will become None.
-        let inner_rect = clips.iter()
-                              .fold(Some(task_rect), |current, clip| {
-            current.and_then(|rect| {
-                let inner_rect = match *clip.1.bounds.as_ref().unwrap() {
-                    MaskBounds::Outer(..) |
-                    MaskBounds::None => DeviceIntRect::zero(),
-                    MaskBounds::OuterInner(_, ref inner) => inner.bounding_rect
-                };
-                rect.intersection(&inner_rect)
-            })
-        });
+        // Nothing to do, all clips are irrelevant for this case
+        if clips.is_empty() {
+            return None
+        }
 
         // TODO(gw): This optimization is very conservative for now.
         //           For now, only draw optimized geometry if it is
         //           a single aligned rect mask with rounded corners.
         //           In the future, we'll expand this to handle the
         //           more complex types of clip mask geometry.
         let mut geometry_kind = MaskGeometryKind::Default;
         if inner_rect.is_some() && clips.len() == 1 {
-            let (_, ref clip_info) = clips[0];
-            if clip_info.image.is_none() &&
-               clip_info.effective_complex_clip_count == 1 &&
-               clip_info.is_aligned {
+            let (_, ref info) = clips[0];
+            if info.border_corners.is_empty() &&
+               info.image.is_none() &&
+               info.complex_clip_range.get_count() == 1 &&
+               info.layer_clip_range.get_count() == 0 {
                 geometry_kind = MaskGeometryKind::CornersOnly;
             }
         }
 
-        let inner_rect = inner_rect.unwrap_or(DeviceIntRect::zero());
-
-        MaskResult::Inside(RenderTask {
+        Some(RenderTask {
             id: RenderTaskId::Dynamic(RenderTaskKey::CacheMask(mask_key)),
             children: Vec::new(),
             location: RenderTaskLocation::Dynamic(None, task_rect.size),
             kind: RenderTaskKind::CacheMask(CacheMaskTask {
                 actual_rect: task_rect,
-                inner_rect: inner_rect,
-                clips: clips.to_vec(),
-                geometry_kind: geometry_kind,
+                inner_rect: inner_rect.unwrap_or(DeviceIntRect::zero()),
+                clips,
+                geometry_kind,
             }),
         })
     }
 
     // Construct a render task to apply a blur to a primitive. For now,
     // this is only used for text runs, but we can probably extend this
     // to handle general blurs to any render task in the future.
     // The render task chain that is constructed looks like:
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -6,25 +6,24 @@
 //!
 //! The `webrender::renderer` module provides the interface to webrender, which
 //! is accessible through [`Renderer`][renderer]
 //!
 //! [renderer]: struct.Renderer.html
 
 use debug_colors;
 use debug_render::DebugRenderer;
-use device::{DepthFunction, Device, FrameId, ProgramId, TextureId, VertexFormat, GpuMarker, GpuProfiler};
+use device::{DepthFunction, Device, FrameId, ProgramId, TextureId, VertexFormat, GpuMarker, GpuProfiler, PBOId};
 use device::{GpuSample, TextureFilter, VAOId, VertexUsageHint, FileWatcherHandler, TextureTarget, ShaderError};
 use device::get_gl_format_bgra;
 use euclid::Transform3D;
 use fnv::FnvHasher;
 use frame_builder::FrameBuilderConfig;
 use gleam::gl;
 use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
-use gpu_store::{GpuStore, GpuStoreLayout};
 use internal_types::{CacheTextureId, RendererFrame, ResultMsg, TextureUpdateOp};
 use internal_types::{TextureUpdateList, PackedVertex, RenderTargetMode};
 use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, SourceTexture};
 use internal_types::{BatchTextures, TextureSampler};
 use profiler::{Profiler, BackendProfileCounters};
 use profiler::{GpuProfileTag, RendererProfileTimers, RendererProfileCounters};
 use record::ApiRecordingReceiver;
 use render_backend::RenderBackend;
@@ -33,36 +32,35 @@ use std;
 use std::cmp;
 use std::collections::{HashMap, VecDeque};
 use std::f32;
 use std::hash::BuildHasherDefault;
 use std::marker::PhantomData;
 use std::mem;
 use std::path::PathBuf;
 use std::rc::Rc;
-use std::slice;
 use std::sync::{Arc, Mutex};
 use std::sync::mpsc::{channel, Receiver, Sender};
 use std::thread;
 use texture_cache::TextureCache;
 use rayon::ThreadPool;
 use rayon::Configuration as ThreadPoolConfig;
 use tiling::{AlphaBatchKind, BlurCommand, CompositePrimitiveInstance, Frame, PrimitiveBatch, RenderTarget};
 use tiling::{AlphaRenderTarget, CacheClipInstance, PrimitiveInstance, ColorRenderTarget, RenderTargetKind};
 use time::precise_time_ns;
 use thread_profiler::{register_thread_with_profiler, write_profile};
 use util::TransformedRectKind;
 use webgl_types::GLContextHandleWrapper;
-use webrender_traits::{ColorF, Epoch, PipelineId, RenderNotifier, RenderDispatcher};
-use webrender_traits::{ExternalImageId, ExternalImageType, ImageData, ImageFormat, RenderApiSender};
-use webrender_traits::{DeviceIntRect, DeviceUintRect, DeviceIntPoint, DeviceIntSize, DeviceUintSize};
-use webrender_traits::{BlobImageRenderer, channel, FontRenderMode};
-use webrender_traits::VRCompositorHandler;
-use webrender_traits::{YuvColorSpace, YuvFormat};
-use webrender_traits::{YUV_COLOR_SPACES, YUV_FORMATS};
+use api::{ColorF, Epoch, PipelineId, RenderNotifier, RenderDispatcher};
+use api::{ExternalImageId, ExternalImageType, ImageData, ImageFormat, RenderApiSender};
+use api::{DeviceIntRect, DeviceUintRect, DeviceIntPoint, DeviceIntSize, DeviceUintSize};
+use api::{BlobImageRenderer, channel, FontRenderMode};
+use api::VRCompositorHandler;
+use api::{YuvColorSpace, YuvFormat};
+use api::{YUV_COLOR_SPACES, YUV_FORMATS};
 
 pub const GPU_DATA_TEXTURE_POOL: usize = 5;
 pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024;
 
 const GPU_TAG_CACHE_BOX_SHADOW: GpuProfileTag = GpuProfileTag { label: "C_BoxShadow", color: debug_colors::BLACK };
 const GPU_TAG_CACHE_CLIP: GpuProfileTag = GpuProfileTag { label: "C_Clip", color: debug_colors::PURPLE };
 const GPU_TAG_CACHE_TEXT_RUN: GpuProfileTag = GpuProfileTag { label: "C_TextRun", color: debug_colors::MISTYROSE };
 const GPU_TAG_SETUP_TARGET: GpuProfileTag = GpuProfileTag { label: "target", color: debug_colors::SLATEGREY };
@@ -151,148 +149,223 @@ pub struct GpuProfile {
 
 impl GpuProfile {
     fn new<T>(frame_id: FrameId, samples: &[GpuSample<T>]) -> GpuProfile {
         let mut paint_time_ns = 0;
         for sample in samples {
             paint_time_ns += sample.time_ns;
         }
         GpuProfile {
-            frame_id: frame_id,
-            paint_time_ns: paint_time_ns,
+            frame_id,
+            paint_time_ns,
         }
     }
 }
 
 #[derive(Debug)]
 pub struct CpuProfile {
     pub frame_id: FrameId,
     pub composite_time_ns: u64,
     pub draw_calls: usize,
 }
 
 impl CpuProfile {
     fn new(frame_id: FrameId,
            composite_time_ns: u64,
            draw_calls: usize) -> CpuProfile {
         CpuProfile {
-            frame_id: frame_id,
-            composite_time_ns: composite_time_ns,
-            draw_calls: draw_calls,
+            frame_id,
+            composite_time_ns,
+            draw_calls,
         }
     }
 }
 
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub enum BlendMode {
     None,
     Alpha,
     PremultipliedAlpha,
 
     // Use the color of the text itself as a constant color blend factor.
     Subpixel(ColorF),
 }
 
+// Tracks the state of each row in the GPU cache texture.
+struct CacheRow {
+    is_dirty: bool,
+}
+
+impl CacheRow {
+    fn new() -> CacheRow {
+        CacheRow {
+            is_dirty: false,
+        }
+    }
+}
+
 /// The device-specific representation of the cache texture in gpu_cache.rs
 struct CacheTexture {
-    current_id: TextureId,
-    next_id: TextureId,
+    texture_id: TextureId,
+    pbo_id: PBOId,
+    rows: Vec<CacheRow>,
+    cpu_blocks: Vec<GpuBlockData>,
 }
 
 impl CacheTexture {
     fn new(device: &mut Device) -> CacheTexture {
-        let ids = device.create_texture_ids(2, TextureTarget::Default);
+        let texture_id = device.create_texture_ids(1, TextureTarget::Default)[0];
+        let pbo_id = device.create_pbo();
 
         CacheTexture {
-            current_id: ids[0],
-            next_id: ids[1],
+            texture_id,
+            pbo_id,
+            rows: Vec::new(),
+            cpu_blocks: Vec::new(),
         }
     }
 
     fn apply_patch(&mut self,
-                   device: &mut Device,
                    update: &GpuCacheUpdate,
                    blocks: &[GpuBlockData]) {
         match update {
             &GpuCacheUpdate::Copy { block_index, block_count, address } => {
-                // Apply an incremental update to the cache texture.
-                // TODO(gw): For the initial implementation, we will just
-                //           use update_texture() since it's simple. If / when
-                //           we profile this and find it to be slow on some / all
-                //           devices - we can look into other options, such as
-                //           using glMapBuffer() with the unsynchronized bit,
-                //           and managing the synchronization ourselves with fences.
-                let data: &[u8] = unsafe {
-                    let ptr = blocks.as_ptr()
-                                    .offset(block_index as isize);
-                    slice::from_raw_parts(ptr as *const _, block_count * 16)
-                };
-                device.update_texture(self.current_id,
-                                      address.u as u32,
-                                      address.v as u32,
-                                      block_count as u32,
-                                      1,
-                                      None,
-                                      data);
+                let row = address.v as usize;
+
+                // Ensure that the CPU-side shadow copy of the GPU cache data has enough
+                // rows to apply this patch.
+                while self.rows.len() <= row {
+                    // Add a new row.
+                    self.rows.push(CacheRow::new());
+                    // Add enough GPU blocks for this row.
+                    self.cpu_blocks.extend_from_slice(&[GpuBlockData::empty(); MAX_VERTEX_TEXTURE_WIDTH]);
+                }
+
+                // This row is dirty (needs to be updated in GPU texture).
+                self.rows[row].is_dirty = true;
+
+                // Copy the blocks from the patch array in the shadow CPU copy.
+                let block_offset = row * MAX_VERTEX_TEXTURE_WIDTH + address.u as usize;
+                let data = &mut self.cpu_blocks[block_offset..(block_offset + block_count)];
+                for i in 0..block_count {
+                    data[i] = blocks[block_index + i];
+                }
             }
         }
     }
 
     fn update(&mut self, device: &mut Device, updates: &GpuCacheUpdateList) {
         // See if we need to create or resize the texture.
-        let current_dimensions = device.get_texture_dimensions(self.current_id);
+        let current_dimensions = device.get_texture_dimensions(self.texture_id);
         if updates.height > current_dimensions.height {
             // Create a f32 texture that can be used for the vertex shader
             // to fetch data from.
-            device.init_texture(self.next_id,
+            device.init_texture(self.texture_id,
                                 MAX_VERTEX_TEXTURE_WIDTH as u32,
                                 updates.height as u32,
                                 ImageFormat::RGBAF32,
                                 TextureFilter::Nearest,
-                                RenderTargetMode::SimpleRenderTarget,
+                                RenderTargetMode::None,
                                 None);
 
             // Copy the current texture into the newly resized texture.
             if current_dimensions.height > 0 {
-                device.bind_draw_target(Some((self.next_id, 0)), None);
-
-                let blit_rect = DeviceIntRect::new(DeviceIntPoint::zero(),
-                                                   DeviceIntSize::new(MAX_VERTEX_TEXTURE_WIDTH as i32,
-                                                                      current_dimensions.height as i32));
-
-                // TODO(gw): Should probably switch this to glCopyTexSubImage2D, since we
-                // don't do any stretching here.
-                device.blit_render_target(Some((self.current_id, 0)),
-                                          Some(blit_rect),
-                                          blit_rect);
-
-                // Free the GPU memory for that texture until we need to resize again.
-                device.deinit_texture(self.current_id);
+                // If we had to resize the texture, just mark all rows
+                // as dirty so they will be uploaded to the texture
+                // during the next flush.
+                for row in &mut self.rows {
+                    row.is_dirty = true;
+                }
             }
-
-            mem::swap(&mut self.current_id, &mut self.next_id);
         }
 
         for update in &updates.updates {
-            self.apply_patch(device, update, &updates.blocks);
+            self.apply_patch(update, &updates.blocks);
+        }
+    }
+
+    fn flush(&mut self, device: &mut Device) {
+        // Bind a PBO to do the texture upload.
+        // Updating the texture via PBO avoids CPU-side driver stalls.
+        device.bind_pbo(Some(self.pbo_id));
+
+        for (row_index, row) in self.rows.iter_mut().enumerate() {
+            if row.is_dirty {
+                // Get the data for this row and push to the PBO.
+                let block_index = row_index * MAX_VERTEX_TEXTURE_WIDTH;
+                let cpu_blocks = &self.cpu_blocks[block_index..(block_index + MAX_VERTEX_TEXTURE_WIDTH)];
+                device.update_pbo_data(cpu_blocks);
+
+                // Insert a command to copy the PBO data to the right place in
+                // the GPU-side cache texture.
+                device.update_texture_from_pbo(self.texture_id,
+                                               0,
+                                               row_index as u32,
+                                               MAX_VERTEX_TEXTURE_WIDTH as u32,
+                                               1,
+                                               0);
+
+                // Orphan the PBO. This is the recommended way to hint to the
+                // driver to detach the underlying storage from this PBO id.
+                // Keeping the size the same gives the driver a hint for future
+                // use of this PBO.
+                device.orphan_pbo(mem::size_of::<GpuBlockData>() * MAX_VERTEX_TEXTURE_WIDTH);
+
+                row.is_dirty = false;
+            }
         }
+
+        // Ensure that other texture updates won't read from this PBO.
+        device.bind_pbo(None);
+    }
+}
+
+
+trait GpuStoreLayout {
+    fn image_format() -> ImageFormat;
+
+    fn texture_width<T>() -> usize;
+
+    fn texture_filter() -> TextureFilter;
+
+    fn texel_size() -> usize {
+        match Self::image_format() {
+            ImageFormat::BGRA8 => 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>()
+    }
+
+    fn rows_per_item<T>() -> usize {
+        Self::texels_per_item::<T>() / Self::texture_width::<T>()
     }
 }
 
 struct GpuDataTexture<L> {
     id: TextureId,
     layout: PhantomData<L>,
 }
 
 impl<L: GpuStoreLayout> GpuDataTexture<L> {
     fn new(device: &mut Device) -> GpuDataTexture<L> {
         let id = device.create_texture_ids(1, TextureTarget::Default)[0];
 
         GpuDataTexture {
-            id: id,
+            id,
             layout: PhantomData,
         }
     }
 
     fn init<T: Default>(&mut self,
                         device: &mut Device,
                         data: &mut Vec<T>) {
         if data.is_empty() {
@@ -339,21 +412,20 @@ impl GpuStoreLayout for VertexDataTextur
     }
 
     fn texture_filter() -> TextureFilter {
         TextureFilter::Nearest
     }
 }
 
 type VertexDataTexture = GpuDataTexture<VertexDataTextureLayout>;
-pub type VertexDataStore<T> = GpuStore<T, VertexDataTextureLayout>;
 
-const TRANSFORM_FEATURE: &'static str = "TRANSFORM";
-const SUBPIXEL_AA_FEATURE: &'static str = "SUBPIXEL_AA";
-const CLIP_FEATURE: &'static str = "CLIP";
+const TRANSFORM_FEATURE: &str = "TRANSFORM";
+const SUBPIXEL_AA_FEATURE: &str = "SUBPIXEL_AA";
+const CLIP_FEATURE: &str = "CLIP";
 
 enum ShaderKind {
     Primitive,
     Cache(VertexFormat),
     ClipCache,
 }
 
 struct LazilyCompiledShader {
@@ -366,18 +438,18 @@ struct LazilyCompiledShader {
 impl LazilyCompiledShader {
     fn new(kind: ShaderKind,
            name: &'static str,
            features: &[&'static str],
            device: &mut Device,
            precache: bool) -> Result<LazilyCompiledShader, ShaderError> {
         let mut shader = LazilyCompiledShader {
             id: None,
-            name: name,
-            kind: kind,
+            name,
+            kind,
             features: features.to_vec(),
         };
 
         if precache {
             try!{ shader.get(device) };
         }
 
         Ok(shader)
@@ -460,18 +532,18 @@ impl PrimitiveShader {
             LazilyCompiledShader::new(ShaderKind::Primitive,
                                       name,
                                       &transform_features,
                                       device,
                                       precache)
         };
 
         Ok(PrimitiveShader {
-            simple: simple,
-            transform: transform,
+            simple,
+            transform,
         })
     }
 
     fn get(&mut self,
            device: &mut Device,
            transform_kind: TransformedRectKind) -> Result<ProgramId, ShaderError> {
         match transform_kind {
             TransformedRectKind::AxisAligned => self.simple.get(device),
@@ -506,36 +578,32 @@ fn create_clip_shader(name: &'static str
 
     let includes = &["prim_shared", "clip_shared"];
     device.create_program_with_prefix(name, includes, Some(prefix), VertexFormat::Clip)
 }
 
 struct GpuDataTextures {
     layer_texture: VertexDataTexture,
     render_task_texture: VertexDataTexture,
-    data32_texture: VertexDataTexture,
 }
 
 impl GpuDataTextures {
     fn new(device: &mut Device) -> GpuDataTextures {
         GpuDataTextures {
             layer_texture: VertexDataTexture::new(device),
             render_task_texture: VertexDataTexture::new(device),
-            data32_texture: VertexDataTexture::new(device),
         }
     }
 
     fn init_frame(&mut self, device: &mut Device, frame: &mut Frame) {
-        self.data32_texture.init(device, &mut frame.gpu_data32);
         self.layer_texture.init(device, &mut frame.layer_texture_data);
         self.render_task_texture.init(device, &mut frame.render_task_data);
 
         device.bind_texture(TextureSampler::Layers, self.layer_texture.id);
         device.bind_texture(TextureSampler::RenderTasks, self.render_task_texture.id);
-        device.bind_texture(TextureSampler::Data32, self.data32_texture.id);
     }
 }
 
 #[derive(Clone, Debug, PartialEq)]
 pub enum ReadPixelsFormat {
     Rgba8,
     Bgra8,
 }
@@ -1049,17 +1117,17 @@ impl Renderer {
         let default_font_render_mode = match (options.enable_aa, options.enable_subpixel_aa) {
             (true, true) => FontRenderMode::Subpixel,
             (true, false) => FontRenderMode::Alpha,
             (false, _) => FontRenderMode::Mono,
         };
 
         let config = FrameBuilderConfig {
             enable_scrollbars: options.enable_scrollbars,
-            default_font_render_mode: default_font_render_mode,
+            default_font_render_mode,
             debug: options.debug,
             cache_expiry_frames: options.cache_expiry_frames,
         };
 
         let device_pixel_ratio = options.device_pixel_ratio;
         let render_target_debug = options.render_target_debug;
         let payload_tx_for_backend = payload_tx.clone();
         let recorder = options.recorder;
@@ -1090,77 +1158,77 @@ impl Renderer {
             backend.run(backend_profile_counters);
         })};
 
         let gpu_cache_texture = CacheTexture::new(&mut device);
 
         let gpu_profile = GpuProfiler::new(device.rc_gl());
 
         let renderer = Renderer {
-            result_rx: result_rx,
-            device: device,
+            result_rx,
+            device,
             current_frame: None,
             pending_texture_updates: Vec::new(),
             pending_gpu_cache_updates: Vec::new(),
             pending_shader_updates: Vec::new(),
-            cs_box_shadow: cs_box_shadow,
-            cs_text_run: cs_text_run,
-            cs_blur: cs_blur,
-            cs_clip_rectangle: cs_clip_rectangle,
-            cs_clip_border: cs_clip_border,
-            cs_clip_image: cs_clip_image,
-            ps_rectangle: ps_rectangle,
-            ps_rectangle_clip: ps_rectangle_clip,
-            ps_text_run: ps_text_run,
-            ps_text_run_subpixel: ps_text_run_subpixel,
-            ps_image: ps_image,
-            ps_yuv_image: ps_yuv_image,
-            ps_border_corner: ps_border_corner,
-            ps_border_edge: ps_border_edge,
-            ps_box_shadow: ps_box_shadow,
-            ps_gradient: ps_gradient,
-            ps_angle_gradient: ps_angle_gradient,
-            ps_radial_gradient: ps_radial_gradient,
-            ps_cache_image: ps_cache_image,
-            ps_blend: ps_blend,
-            ps_hw_composite: ps_hw_composite,
-            ps_split_composite: ps_split_composite,
-            ps_composite: ps_composite,
-            notifier: notifier,
+            cs_box_shadow,
+            cs_text_run,
+            cs_blur,
+            cs_clip_rectangle,
+            cs_clip_border,
+            cs_clip_image,
+            ps_rectangle,
+            ps_rectangle_clip,
+            ps_text_run,
+            ps_text_run_subpixel,
+            ps_image,
+            ps_yuv_image,
+            ps_border_corner,
+            ps_border_edge,
+            ps_box_shadow,
+            ps_gradient,
+            ps_angle_gradient,
+            ps_radial_gradient,
+            ps_cache_image,
+            ps_blend,
+            ps_hw_composite,
+            ps_split_composite,
+            ps_composite,
+            notifier,
             debug: debug_renderer,
-            render_target_debug: render_target_debug,
+            render_target_debug,
             enable_batcher: options.enable_batcher,
             backend_profile_counters: BackendProfileCounters::new(),
             profile_counters: RendererProfileCounters::new(),
             profiler: Profiler::new(),
             enable_profiler: options.enable_profiler,
             max_recorded_profiles: options.max_recorded_profiles,
             clear_framebuffer: options.clear_framebuffer,
             clear_color: options.clear_color,
             enable_clear_scissor: options.enable_clear_scissor,
             last_time: 0,
             color_render_targets: Vec::new(),
             alpha_render_targets: Vec::new(),
-            gpu_profile: gpu_profile,
-            prim_vao_id: prim_vao_id,
-            blur_vao_id: blur_vao_id,
-            clip_vao_id: clip_vao_id,
+            gpu_profile,
+            prim_vao_id,
+            blur_vao_id,
+            clip_vao_id,
             gdt_index: 0,
-            gpu_data_textures: gpu_data_textures,
+            gpu_data_textures,
             pipeline_epoch_map: HashMap::default(),
-            main_thread_dispatcher: main_thread_dispatcher,
+            main_thread_dispatcher,
             cache_texture_id_map: Vec::new(),
-            dummy_cache_texture_id: dummy_cache_texture_id,
-            dither_matrix_texture_id: dither_matrix_texture_id,
+            dummy_cache_texture_id,
+            dither_matrix_texture_id,
             external_image_handler: None,
             external_images: HashMap::default(),
             vr_compositor_handler: vr_compositor,
             cpu_profiles: VecDeque::new(),
             gpu_profiles: VecDeque::new(),
-            gpu_cache_texture: gpu_cache_texture,
+            gpu_cache_texture,
         };
 
         let sender = RenderApiSender::new(api_tx, payload_tx);
         Ok((renderer, sender))
     }
 
     pub fn get_graphics_api_info(&self) -> GraphicsApiInfo {
         GraphicsApiInfo {
@@ -1277,17 +1345,17 @@ impl Renderer {
         let cpu_profiles = self.cpu_profiles.drain(..).collect();
         let gpu_profiles = self.gpu_profiles.drain(..).collect();
         (cpu_profiles, gpu_profiles)
     }
 
     /// Renders the current frame.
     ///
     /// A Frame is supplied by calling [`set_display_list()`][newframe].
-    /// [newframe]: ../../webrender_traits/struct.RenderApi.html#method.set_display_list
+    /// [newframe]: ../../webrender_api/struct.RenderApi.html#method.set_display_list
     pub fn render(&mut self, framebuffer_size: DeviceUintSize) {
         profile_scope!("render");
 
         if let Some(mut frame) = self.current_frame.take() {
             if let Some(ref mut frame) = frame.frame {
                 let mut profile_timers = RendererProfileTimers::new();
 
                 {
@@ -1314,20 +1382,19 @@ impl Renderer {
 
                         self.device.disable_scissor();
                         self.device.disable_depth();
                         self.device.set_blend(false);
                         //self.update_shaders();
 
                         self.update_texture_cache();
 
-                        self.update_gpu_cache();
-                        self.update_deferred_resolves(frame);
+                        self.update_gpu_cache(frame);
 
-                        self.device.bind_texture(TextureSampler::ResourceCache, self.gpu_cache_texture.current_id);
+                        self.device.bind_texture(TextureSampler::ResourceCache, self.gpu_cache_texture.texture_id);
 
                         frame_id
                     };
 
                     self.draw_tile_frame(frame, &framebuffer_size);
 
                     self.gpu_profile.end_frame();
                     cpu_frame_id
@@ -1391,21 +1458,23 @@ impl Renderer {
         }
 
         if update_uniforms {
             self.update_uniform_locations();
         }
     }
 */
 
-    fn update_gpu_cache(&mut self) {
+    fn update_gpu_cache(&mut self, frame: &mut Frame) {
         let _gm = GpuMarker::new(self.device.rc_gl(), "gpu cache update");
         for update_list in self.pending_gpu_cache_updates.drain(..) {
             self.gpu_cache_texture.update(&mut self.device, &update_list);
         }
+        self.update_deferred_resolves(frame);
+        self.gpu_cache_texture.flush(&mut self.device);
     }
 
     fn update_texture_cache(&mut self) {
         let _gm = GpuMarker::new(self.device.rc_gl(), "texture cache update");
         let mut pending_texture_updates = mem::replace(&mut self.pending_texture_updates, vec![]);
         for update_list in pending_texture_updates.drain(..) {
             for update in update_list.updates {
                 match update.op {
@@ -1754,26 +1823,31 @@ impl Renderer {
         //           blur radii with fixed weights.
         if !target.vertical_blurs.is_empty() || !target.horizontal_blurs.is_empty() {
             let _gm = self.gpu_profile.add_marker(GPU_TAG_BLUR);
             let vao = self.blur_vao_id;
 
             self.device.set_blend(false);
             let shader = self.cs_blur.get(&mut self.device).unwrap();
 
-            self.draw_instanced_batch(&target.vertical_blurs,
-                                      vao,
-                                      shader,
-                                      &BatchTextures::no_texture(),
-                                      &projection);
-            self.draw_instanced_batch(&target.horizontal_blurs,
-                                      vao,
-                                      shader,
-                                      &BatchTextures::no_texture(),
-                                      &projection);
+            if !target.vertical_blurs.is_empty() {
+                self.draw_instanced_batch(&target.vertical_blurs,
+                                          vao,
+                                          shader,
+                                          &BatchTextures::no_texture(),
+                                          &projection);
+            }
+
+            if !target.horizontal_blurs.is_empty() {
+                self.draw_instanced_batch(&target.horizontal_blurs,
+                                          vao,
+                                          shader,
+                                          &BatchTextures::no_texture(),
+                                          &projection);
+            }
         }
 
         // Draw any box-shadow caches for this target.
         if !target.box_shadow_cache_prims.is_empty() {
             self.device.set_blend(false);
             let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_BOX_SHADOW);
             let vao = self.prim_vao_id;
             let shader = self.cs_box_shadow.get(&mut self.device).unwrap();
@@ -1800,68 +1874,76 @@ impl Renderer {
 
             self.draw_instanced_batch(&target.text_run_cache_prims,
                                       vao,
                                       shader,
                                       &target.text_run_textures,
                                       &projection);
         }
 
-        let _gm2 = GpuMarker::new(self.device.rc_gl(), "alpha batches");
-        self.device.set_blend(false);
-        let mut prev_blend_mode = BlendMode::None;
-
-        //Note: depth equality is needed for split planes
-        self.device.set_depth_func(DepthFunction::LessEqual);
-        self.device.enable_depth();
-        self.device.enable_depth_write();
+        if !target.alpha_batcher.is_empty() {
+            let _gm2 = GpuMarker::new(self.device.rc_gl(), "alpha batches");
+            self.device.set_blend(false);
+            let mut prev_blend_mode = BlendMode::None;
 
-        for batch in &target.alpha_batcher.batch_list.opaque_batches {
-            self.submit_batch(batch,
-                              &projection,
-                              render_task_data,
-                              color_cache_texture,
-                              render_target,
-                              target_size);
-        }
-
-        self.device.disable_depth_write();
+            //Note: depth equality is needed for split planes
+            self.device.set_depth_func(DepthFunction::LessEqual);
+            self.device.enable_depth();
+            self.device.enable_depth_write();
 
-        for batch in &target.alpha_batcher.batch_list.alpha_batches {
-            if batch.key.blend_mode != prev_blend_mode {
-                match batch.key.blend_mode {
-                    BlendMode::None => {
-                        self.device.set_blend(false);
-                    }
-                    BlendMode::Alpha => {
-                        self.device.set_blend(true);
-                        self.device.set_blend_mode_alpha();
-                    }
-                    BlendMode::PremultipliedAlpha => {
-                        self.device.set_blend(true);
-                        self.device.set_blend_mode_premultiplied_alpha();
-                    }
-                    BlendMode::Subpixel(color) => {
-                        self.device.set_blend(true);
-                        self.device.set_blend_mode_subpixel(color);
-                    }
-                }
-                prev_blend_mode = batch.key.blend_mode;
+            // Draw opaque batches front-to-back for maximum
+            // z-buffer efficiency!
+            for batch in target.alpha_batcher
+                               .batch_list
+                               .opaque_batches
+                               .iter()
+                               .rev() {
+                self.submit_batch(batch,
+                                  &projection,
+                                  render_task_data,
+                                  color_cache_texture,
+                                  render_target,
+                                  target_size);
             }
 
-            self.submit_batch(batch,
-                              &projection,
-                              render_task_data,
-                              color_cache_texture,
-                              render_target,
-                              target_size);
+            self.device.disable_depth_write();
+
+            for batch in &target.alpha_batcher.batch_list.alpha_batches {
+                if batch.key.blend_mode != prev_blend_mode {
+                    match batch.key.blend_mode {
+                        BlendMode::None => {
+                            self.device.set_blend(false);
+                        }
+                        BlendMode::Alpha => {
+                            self.device.set_blend(true);
+                            self.device.set_blend_mode_alpha();
+                        }
+                        BlendMode::PremultipliedAlpha => {
+                            self.device.set_blend(true);
+                            self.device.set_blend_mode_premultiplied_alpha();
+                        }
+                        BlendMode::Subpixel(color) => {
+                            self.device.set_blend(true);
+                            self.device.set_blend_mode_subpixel(color);
+                        }
+                    }
+                    prev_blend_mode = batch.key.blend_mode;
+                }
+
+                self.submit_batch(batch,
+                                  &projection,
+                                  render_task_data,
+                                  color_cache_texture,
+                                  render_target,
+                                  target_size);
+            }
+
+            self.device.disable_depth();
+            self.device.set_blend(false);
         }
-
-        self.device.disable_depth();
-        self.device.set_blend(false);
     }
 
     fn draw_alpha_target(&mut self,
                          render_target: (TextureId, i32),
                          target: &AlphaRenderTarget,
                          target_size: DeviceUintSize,
                          projection: &Transform3D<f32>) {
         {
@@ -1985,17 +2067,17 @@ impl Renderer {
                 self.external_images.insert((ext_image.id, ext_image.channel_index), texture_id);
 
                 let update = GpuCacheUpdate::Copy {
                     block_index: 0,
                     block_count: 1,
                     address: deferred_resolve.address,
                 };
                 let blocks = [ [image.u0, image.v0, image.u1, image.v1].into() ];
-                self.gpu_cache_texture.apply_patch(&mut self.device, &update, &blocks);
+                self.gpu_cache_texture.apply_patch(&update, &blocks);
             }
         }
     }
 
     fn unlock_external_images(&mut self) {
         if !self.external_images.is_empty() {
             let handler = self.external_image_handler
                               .as_mut()
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -12,23 +12,23 @@ use profiler::TextureCacheProfileCounter
 use std::collections::{HashMap, HashSet};
 use std::collections::hash_map::Entry::{self, Occupied, Vacant};
 use std::fmt::Debug;
 use std::hash::BuildHasherDefault;
 use std::hash::Hash;
 use std::mem;
 use std::sync::Arc;
 use texture_cache::{TextureCache, TextureCacheItemId};
-use webrender_traits::{Epoch, FontKey, FontTemplate, GlyphKey, ImageKey, ImageRendering};
-use webrender_traits::{FontRenderMode, ImageData, GlyphDimensions, WebGLContextId};
-use webrender_traits::{DevicePoint, DeviceIntSize, DeviceUintRect, ImageDescriptor, ColorF};
-use webrender_traits::{GlyphOptions, GlyphInstance, TileOffset, TileSize};
-use webrender_traits::{BlobImageRenderer, BlobImageDescriptor, BlobImageError, BlobImageRequest, BlobImageData};
-use webrender_traits::BlobImageResources;
-use webrender_traits::{ExternalImageData, ExternalImageType, LayoutPoint};
+use api::{Epoch, FontKey, FontTemplate, GlyphKey, ImageKey, ImageRendering};
+use api::{FontRenderMode, ImageData, GlyphDimensions, WebGLContextId};
+use api::{DevicePoint, DeviceIntSize, DeviceUintRect, ImageDescriptor, ColorF};
+use api::{GlyphOptions, GlyphInstance, TileOffset, TileSize};
+use api::{BlobImageRenderer, BlobImageDescriptor, BlobImageError, BlobImageRequest, BlobImageData};
+use api::BlobImageResources;
+use api::{ExternalImageData, ExternalImageType, LayoutPoint};
 use rayon::ThreadPool;
 use glyph_rasterizer::{GlyphRasterizer, GlyphCache, GlyphRequest};
 
 const DEFAULT_TILE_SIZE: TileSize = 512;
 
 // These coordinates are always in texels.
 // They are converted to normalized ST
 // values in the vertex shader. The reason
@@ -223,23 +223,23 @@ impl ResourceCache {
             cached_glyphs: ResourceClassCache::new(),
             cached_images: ResourceClassCache::new(),
             webgl_textures: HashMap::default(),
             resources: Resources {
                 font_templates: HashMap::default(),
                 image_templates: ImageTemplates::new(),
             },
             cached_glyph_dimensions: HashMap::default(),
-            texture_cache: texture_cache,
+            texture_cache,
             state: State::Idle,
             current_frame_id: FrameId(0),
             pending_image_requests: Vec::new(),
             glyph_rasterizer: GlyphRasterizer::new(workers),
 
-            blob_image_renderer: blob_image_renderer,
+            blob_image_renderer,
             blob_image_requests: HashSet::new(),
 
             requested_glyphs: HashSet::default(),
             requested_images: HashSet::default(),
         }
     }
 
     pub fn max_texture_size(&self) -> u32 {
@@ -289,20 +289,20 @@ impl ResourceCache {
             self.blob_image_renderer.as_mut().unwrap().add(
                 image_key,
                 mem::replace(blob, BlobImageData::new()),
                 tiling
             );
         }
 
         let resource = ImageResource {
-            descriptor: descriptor,
-            data: data,
+            descriptor,
+            data,
             epoch: Epoch(0),
-            tiling: tiling,
+            tiling,
             dirty_rect: None,
         };
 
         self.resources.image_templates.insert(image_key, resource);
     }
 
     pub fn update_image_template(&mut self,
                                  image_key: ImageKey,
@@ -324,20 +324,20 @@ impl ResourceCache {
             if let ImageData::Blob(ref mut blob) = data {
                 self.blob_image_renderer.as_mut().unwrap().update(
                     image_key,
                     mem::replace(blob, BlobImageData::new())
                 );
             }
 
             ImageResource {
-                descriptor: descriptor,
-                data: data,
+                descriptor,
+                data,
                 epoch: next_epoch,
-                tiling: tiling,
+                tiling,
                 dirty_rect: match (dirty_rect, image.dirty_rect) {
                     (Some(rect), Some(prev_rect)) => Some(rect.union(&prev_rect)),
                     (Some(rect), None) => Some(rect),
                     _ => None,
                 },
             }
         } else {
             panic!("Attempt to update non-existant image (key {:?}).", image_key);
@@ -359,17 +359,17 @@ impl ResourceCache {
                 println!("Delete the non-exist key:{:?}", image_key);
             }
         }
     }
 
     pub fn add_webgl_texture(&mut self, id: WebGLContextId, texture_id: SourceTexture, size: DeviceIntSize) {
         self.webgl_textures.insert(id, WebGLTexture {
             id: texture_id,
-            size: size,
+            size,
         });
     }
 
     pub fn update_webgl_texture(&mut self, id: WebGLContextId, texture_id: SourceTexture, size: DeviceIntSize) {
         let webgl_texture = self.webgl_textures.get_mut(&id).unwrap();
 
         // Update new texture id and size
         webgl_texture.id = texture_id;
@@ -378,19 +378,19 @@ impl ResourceCache {
 
     pub fn request_image(&mut self,
                          key: ImageKey,
                          rendering: ImageRendering,
                          tile: Option<TileOffset>) {
 
         debug_assert_eq!(self.state, State::AddResources);
         let request = ImageRequest {
-            key: key,
-            rendering: rendering,
-            tile: tile,
+            key,
+            rendering,
+            tile,
         };
 
         let template = self.resources.image_templates.get(key).unwrap();
         if template.data.uses_texture_cache() {
             self.cached_images.mark_as_needed(&request, self.current_frame_id);
         }
         if template.data.is_blob() {
             if let Some(ref mut renderer) = self.blob_image_renderer {
@@ -428,17 +428,17 @@ impl ResourceCache {
                     };
 
                     renderer.request(
                         &self.resources,
                         request.into(),
                         &BlobImageDescriptor {
                             width: w,
                             height: h,
-                            offset: offset,
+                            offset,
                             format: template.descriptor.format,
                         },
                         template.dirty_rect,
                     );
                 }
             }
         } else {
             self.pending_image_requests.push(request);
@@ -520,17 +520,17 @@ impl ResourceCache {
     pub fn get_cached_image(&self,
                             image_key: ImageKey,
                             image_rendering: ImageRendering,
                             tile: Option<TileOffset>) -> CacheItem {
         debug_assert_eq!(self.state, State::QueryResources);
         let key = ImageRequest {
             key: image_key,
             rendering: image_rendering,
-            tile: tile,
+            tile,
         };
         let image_info = &self.cached_images.get(&key, self.current_frame_id);
         let item = self.texture_cache.get(image_info.texture_cache_id);
         CacheItem {
             texture_id: SourceTexture::TextureCache(item.texture_id),
             uv_rect_handle: item.uv_rect_handle,
         }
     }
@@ -551,17 +551,17 @@ impl ResourceCache {
                 }
             },
             // raw and blob image are all using resource_cache.
             ImageData::Raw(..) | ImageData::Blob(..) => None,
         };
 
         ImageProperties {
             descriptor: image_template.descriptor,
-            external_image: external_image,
+            external_image,
             tiling: image_template.tiling,
         }
     }
 
     pub fn get_webgl_texture(&self, context_id: &WebGLContextId) -> &WebGLTexture {
         &self.webgl_textures[context_id]
     }
 
@@ -630,25 +630,25 @@ impl ResourceCache {
                     }
                 }
             }
         }
 
         for texture_cache_item_id in self.requested_images.drain() {
             let item = self.texture_cache.get_mut(texture_cache_item_id);
             if let Some(mut request) = gpu_cache.request(&mut item.uv_rect_handle) {
-                request.push(item.uv_rect.into());
+                request.push(item.uv_rect);
             }
         }
 
         for texture_cache_item_id in self.requested_glyphs.drain() {
             let item = self.texture_cache.get_mut(texture_cache_item_id);
             if let Some(mut request) = gpu_cache.request(&mut item.uv_rect_handle) {
-                request.push(item.uv_rect.into());
-                request.push([item.user_data[0], item.user_data[1], 0.0, 0.0].into());
+                request.push(item.uv_rect);
+                request.push([item.user_data[0], item.user_data[1], 0.0, 0.0]);
             }
         }
     }
 
     fn update_texture_cache(&mut self,
                             request: &ImageRequest,
                             image_data: Option<ImageData>,
                             texture_cache_profile: &mut TextureCacheProfileCounters) {
@@ -676,18 +676,18 @@ impl ResourceCache {
                 let offset = image_descriptor.offset + tile.y as u32 * tile_size as u32 * stride
                                                      + tile.x as u32 * tile_size as u32 * bpp;
                 (Some(stride), offset)
             };
 
             ImageDescriptor {
                 width: actual_width,
                 height: actual_height,
-                stride: stride,
-                offset: offset,
+                stride,
+                offset,
                 format: image_descriptor.format,
                 is_opaque: image_descriptor.is_opaque,
             }
         } else {
             image_template.descriptor.clone()
         };
 
         let image_id = match self.cached_images.entry(*request, self.current_frame_id) {
--- a/gfx/webrender/src/scene.rs
+++ b/gfx/webrender/src/scene.rs
@@ -1,17 +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/. */
 
 use fnv::FnvHasher;
 use std::collections::HashMap;
 use std::hash::BuildHasherDefault;
-use webrender_traits::{BuiltDisplayList, ColorF, DynamicProperties, Epoch, LayerSize, LayoutSize};
-use webrender_traits::{LayoutTransform, PipelineId, PropertyBinding, PropertyBindingId};
+use api::{BuiltDisplayList, ColorF, DynamicProperties, Epoch, LayerSize, LayoutSize};
+use api::{LayoutTransform, PipelineId, PropertyBinding, PropertyBindingId};
 
 /// Stores a map of the animated property bindings for the current display list. These
 /// can be used to animate the transform and/or opacity of a display list without
 /// re-submitting the display list itself.
 pub struct SceneProperties {
     transform_properties: HashMap<PropertyBindingId, LayoutTransform>,
     float_properties: HashMap<PropertyBindingId, f32>,
 }
@@ -115,18 +115,18 @@ impl Scene {
                             epoch: Epoch,
                             built_display_list: BuiltDisplayList,
                             background_color: Option<ColorF>,
                             viewport_size: LayerSize,
                             content_size: LayoutSize) {
         self.display_lists.insert(pipeline_id, built_display_list);
 
         let new_pipeline = ScenePipeline {
-            pipeline_id: pipeline_id,
-            epoch: epoch,
-            viewport_size: viewport_size,
-            content_size: content_size,
-            background_color: background_color,
+            pipeline_id,
+            epoch,
+            viewport_size,
+            content_size,
+            background_color,
         };
 
         self.pipeline_map.insert(pipeline_id, new_pipeline);
     }
 }
--- a/gfx/webrender/src/spring.rs
+++ b/gfx/webrender/src/spring.rs
@@ -1,13 +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/. */
 
-use webrender_traits::LayerPoint;
+use api::LayerPoint;
 
 /// Some arbitrarily small positive number used as threshold value.
 pub const EPSILON: f32 = 0.1;
 
 /// The default stiffness factor.
 pub const STIFFNESS: f32 = 0.2;
 
 /// The default damping factor.
@@ -29,18 +29,18 @@ pub struct Spring {
 
 impl Spring {
     /// Create a new spring at location.
     pub fn at(pos: LayerPoint, stiffness: f32, damping: f32) -> Spring {
         Spring {
             cur: pos,
             prev: pos,
             dest: pos,
-            stiffness: stiffness,
-            damping: damping,
+            stiffness,
+            damping,
         }
     }
 
     /// Set coords on a spring, mutating spring
     pub fn coords(&mut self, cur: LayerPoint, prev: LayerPoint, dest: LayerPoint) {
         self.cur = cur;
         self.prev = prev;
         self.dest = dest
--- a/gfx/webrender/src/texture_cache.rs
+++ b/gfx/webrender/src/texture_cache.rs
@@ -12,19 +12,19 @@ use profiler::TextureCacheProfileCounter
 use std::cmp;
 use std::collections::HashMap;
 use std::collections::hash_map::Entry;
 use std::hash::BuildHasherDefault;
 use std::mem;
 use std::slice::Iter;
 use time;
 use util;
-use webrender_traits::{ExternalImageType, ImageData, ImageFormat};
-use webrender_traits::{DeviceUintRect, DeviceUintSize, DeviceUintPoint};
-use webrender_traits::{DevicePoint, ImageDescriptor};
+use api::{ExternalImageType, ImageData, ImageFormat};
+use api::{DeviceUintRect, DeviceUintSize, DeviceUintPoint};
+use api::{DevicePoint, ImageDescriptor};
 
 /// The number of bytes we're allowed to use for a texture.
 const MAX_BYTES_PER_TEXTURE: u32 = 1024 * 1024 * 256;  // 256MB
 
 /// The number of RGBA pixels we're allowed to use for a texture.
 const MAX_RGBA_PIXELS_PER_TEXTURE: u32 = MAX_BYTES_PER_TEXTURE / 4;
 
 /// The desired initial size of each texture, in pixels.
@@ -72,18 +72,18 @@ pub struct TexturePage {
     coalesce_vec: Vec<DeviceUintRect>,
     allocations: u32,
     dirty: bool,
 }
 
 impl TexturePage {
     pub fn new(texture_id: CacheTextureId, texture_size: DeviceUintSize) -> TexturePage {
         let mut page = TexturePage {
-            texture_id: texture_id,
-            texture_size: texture_size,
+            texture_id,
+            texture_size,
             free_list: FreeRectList::new(),
             coalesce_vec: Vec::new(),
             allocations: 0,
             dirty: false,
         };
         page.clear();
         page
     }
@@ -491,26 +491,26 @@ impl FreeListItem for TextureCacheItem {
 }
 
 impl TextureCacheItem {
     fn new(texture_id: CacheTextureId,
            rect: DeviceUintRect,
            user_data: [f32; 2])
            -> TextureCacheItem {
         TextureCacheItem {
-            texture_id: texture_id,
+            texture_id,
             uv_rect: UvRect {
                 uv0: DevicePoint::new(rect.origin.x as f32,
                                       rect.origin.y as f32),
                 uv1: DevicePoint::new((rect.origin.x + rect.size.width) as f32,
                                       (rect.origin.y + rect.size.height) as f32),
             },
             allocated_rect: rect,
             uv_rect_handle: GpuCacheHandle::new(),
-            user_data: user_data,
+            user_data,
         }
     }
 }
 
 struct TextureCacheArena {
     pages_a8: Vec<TexturePage>,
     pages_rgb8: Vec<TexturePage>,
     pages_rgba8: Vec<TexturePage>,
@@ -598,17 +598,17 @@ impl TextureCache {
         }
 
         TextureCache {
             cache_id_list: CacheTextureIdList::new(),
             free_texture_levels: HashMap::default(),
             items: FreeList::new(),
             pending_updates: TextureUpdateList::new(),
             arena: TextureCacheArena::new(),
-            max_texture_size: max_texture_size,
+            max_texture_size,
         }
     }
 
     pub fn max_texture_size(&self) -> u32 {
         self.max_texture_size
     }
 
     pub fn pending_updates(&mut self) -> TextureUpdateList {
@@ -637,17 +637,17 @@ impl TextureCache {
                 texture_id,
                 DeviceUintRect::new(DeviceUintPoint::zero(), requested_size),
                 user_data);
             let image_id = self.items.insert(cache_item);
 
             return AllocationResult {
                 item: self.items.get(image_id).clone(),
                 kind: AllocationKind::Standalone,
-                image_id: image_id,
+                image_id,
             }
         }
 
         let mode = RenderTargetMode::SimpleRenderTarget;
         let (page_list, page_profile) = match format {
             ImageFormat::A8 => (&mut self.arena.pages_a8, &mut profile.pages_a8),
             ImageFormat::BGRA8 => (&mut self.arena.pages_rgba8, &mut profile.pages_rgba8),
             ImageFormat::RGB8 => (&mut self.arena.pages_rgb8, &mut profile.pages_rgb8),
@@ -712,17 +712,17 @@ impl TextureCache {
 
                     let update_op = TextureUpdate {
                         id: texture_id,
                         op: texture_create_op(texture_size, format, mode),
                     };
                     self.pending_updates.push(update_op);
 
                     free_texture_levels.push(FreeTextureLevel {
-                        texture_id: texture_id,
+                        texture_id,
                     });
                 }
                 let free_texture_level = free_texture_levels.pop().unwrap();
                 let texture_id = free_texture_level.texture_id;
 
                 let page = TexturePage::new(texture_id, texture_size);
                 page_list.push(page);
                 page_list.last_mut().unwrap()
@@ -734,17 +734,17 @@ impl TextureCache {
         let cache_item = TextureCacheItem::new(page.texture_id,
                                                DeviceUintRect::new(location, requested_size),
                                                user_data);
         let image_id = self.items.insert(cache_item.clone());
 
         AllocationResult {
             item: cache_item,
             kind: AllocationKind::TexturePage,
-            image_id: image_id,
+            image_id,
         }
     }
 
     pub fn update(&mut self,
                   image_id: TextureCacheItemId,
                   descriptor: ImageDescriptor,
                   data: ImageData,
                   dirty_rect: Option<DeviceUintRect>) {
@@ -768,17 +768,17 @@ impl TextureCache {
                         let offset = descriptor.offset + dirty.origin.y * stride + dirty.origin.x;
                         TextureUpdateOp::Update {
                             page_pos_x: existing_item.allocated_rect.origin.x + dirty.origin.x,
                             page_pos_y: existing_item.allocated_rect.origin.y + dirty.origin.y,
                             width: dirty.size.width,
                             height: dirty.size.height,
                             data: bytes,
                             stride: Some(stride),
-                            offset: offset,
+                            offset,
                         }
                     }
                     None => {
                         TextureUpdateOp::Update {
                             page_pos_x: existing_item.allocated_rect.origin.x,
                             page_pos_y: existing_item.allocated_rect.origin.y,
                             width: descriptor.width,
                             height: descriptor.height,
@@ -788,17 +788,17 @@ impl TextureCache {
                         }
                     }
                 }
             }
         };
 
         let update_op = TextureUpdate {
             id: existing_item.texture_id,
-            op: op,
+            op,
         };
 
         self.pending_updates.push(update_op);
     }
 
     pub fn insert(&mut self,
                   descriptor: ImageDescriptor,
                   filter: TextureFilter,
@@ -840,17 +840,17 @@ impl TextureCache {
                             }
                             ExternalImageType::ExternalBuffer => {
                                 let update_op = TextureUpdate {
                                     id: result.item.texture_id,
                                     op: TextureUpdateOp::UpdateForExternalBuffer {
                                         rect: result.item.allocated_rect,
                                         id: ext_image.id,
                                         channel_index: ext_image.channel_index,
-                                        stride: stride,
+                                        stride,
                                         offset: descriptor.offset,
                                     },
                                 };
 
                                 self.pending_updates.push(update_op);
                             }
                         }
                     }
@@ -861,17 +861,17 @@ impl TextureCache {
                         let update_op = TextureUpdate {
                             id: result.item.texture_id,
                             op: TextureUpdateOp::Update {
                                 page_pos_x: result.item.allocated_rect.origin.x,
                                 page_pos_y: result.item.allocated_rect.origin.y,
                                 width: result.item.allocated_rect.size.width,
                                 height: result.item.allocated_rect.size.height,
                                 data: bytes,
-                                stride: stride,
+                                stride,
                                 offset: descriptor.offset,
                             },
                         };
 
                         self.pending_updates.push(update_op);
                     }
                 }
             }
@@ -883,37 +883,37 @@ impl TextureCache {
                             ExternalImageType::TextureRectHandle |
                             ExternalImageType::TextureExternalHandle => {
                                 panic!("External texture handle should not go through texture_cache.");
                             }
                             ExternalImageType::ExternalBuffer => {
                                 let update_op = TextureUpdate {
                                     id: result.item.texture_id,
                                     op: TextureUpdateOp::Create {
-                                        width: width,
-                                        height: height,
-                                        format: format,
-                                        filter: filter,
+                                        width,
+                                        height,
+                                        format,
+                                        filter,
                                         mode: RenderTargetMode::None,
                                         data: Some(data),
                                     },
                                 };
 
                                 self.pending_updates.push(update_op);
                             }
                         }
                     }
                     _ => {
                         let update_op = TextureUpdate {
                             id: result.item.texture_id,
                             op: TextureUpdateOp::Create {
-                                width: width,
-                                height: height,
-                                format: format,
-                                filter: filter,
+                                width,
+                                height,
+                                format,
+                                filter,
                                 mode: RenderTargetMode::None,
                                 data: Some(data),
                             },
                         };
 
                         self.pending_updates.push(update_op);
                     }
                 }
@@ -948,33 +948,33 @@ impl TextureCache {
     }
 }
 
 fn texture_create_op(texture_size: DeviceUintSize, format: ImageFormat, mode: RenderTargetMode)
                      -> TextureUpdateOp {
     TextureUpdateOp::Create {
         width: texture_size.width,
         height: texture_size.height,
-        format: format,
+        format,
         filter: TextureFilter::Linear,
-        mode: mode,
+        mode,
         data: None,
     }
 }
 
 fn texture_grow_op(texture_size: DeviceUintSize,
                    format: ImageFormat,
                    mode: RenderTargetMode)
                    -> TextureUpdateOp {
     TextureUpdateOp::Grow {
         width: texture_size.width,
         height: texture_size.height,
-        format: format,
+        format,
         filter: TextureFilter::Linear,
-        mode: mode,
+        mode,
     }
 }
 
 trait FitsInside {
     fn fits_inside(&self, other: &Self) -> bool;
 }
 
 impl FitsInside for DeviceUintSize {
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -2,40 +2,38 @@
  * 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 border::{BorderCornerInstance, BorderCornerSide};
 use device::TextureId;
 use fnv::FnvHasher;
 use gpu_cache::{GpuCache, GpuCacheHandle, GpuCacheUpdateList};
-use gpu_store::GpuStoreAddress;
 use internal_types::{ANGLE_FLOAT_TO_FIXED, BatchTextures, CacheTextureId, LowLevelFilterOp};
 use internal_types::SourceTexture;
 use mask_cache::MaskCacheInfo;
-use prim_store::{CLIP_DATA_GPU_SIZE, DeferredResolve, GpuBlock32};
-use prim_store::{ImagePrimitiveKind, PrimitiveCacheKey};
+use prim_store::{CLIP_DATA_GPU_BLOCKS, DeferredResolve, ImagePrimitiveKind, PrimitiveCacheKey};
 use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
 use profiler::FrameProfileCounters;
 use render_task::{AlphaRenderItem, MaskGeometryKind, MaskSegment, RenderTask, RenderTaskData};
 use render_task::{RenderTaskId, RenderTaskIndex, RenderTaskKey, RenderTaskKind};
 use render_task::RenderTaskLocation;
 use renderer::BlendMode;
 use renderer::ImageBufferKind;
 use resource_cache::ResourceCache;
 use std::{f32, i32, mem, usize};
 use std::collections::HashMap;
 use std::hash::BuildHasherDefault;
 use texture_cache::TexturePage;
 use util::{TransformedRect, TransformedRectKind};
-use webrender_traits::{BuiltDisplayList, ClipAndScrollInfo, ClipId, ColorF, DeviceIntPoint, ImageKey};
-use webrender_traits::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize};
-use webrender_traits::{ExternalImageType, FontRenderMode, ImageRendering, LayerRect};
-use webrender_traits::{LayerToWorldTransform, MixBlendMode, PipelineId, TransformStyle};
-use webrender_traits::{TileOffset, WorldToLayerTransform, YuvColorSpace, YuvFormat, LayerVector2D};
+use api::{BuiltDisplayList, ClipAndScrollInfo, ClipId, ColorF, DeviceIntPoint, ImageKey};
+use api::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize};
+use api::{ExternalImageType, FontRenderMode, ImageRendering, LayerRect};
+use api::{LayerToWorldTransform, MixBlendMode, PipelineId, TransformStyle};
+use api::{TileOffset, WorldToLayerTransform, YuvColorSpace, YuvFormat, LayerVector2D};
 
 // Special sentinel value recognized by the shader. It is considered to be
 // a dummy task that doesn't mask out anything.
 const OPAQUE_TASK_INDEX: RenderTaskIndex = RenderTaskIndex(i32::MAX as usize);
 
 
 pub type DisplayListMap = HashMap<PipelineId,
                                   BuiltDisplayList,
@@ -104,21 +102,23 @@ pub enum PrimitiveFlags {
 }
 
 #[derive(Debug, Copy, Clone)]
 pub struct RenderTargetIndex(pub usize);
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 pub struct RenderPassIndex(isize);
 
+#[derive(Debug)]
 struct DynamicTaskInfo {
     index: RenderTaskIndex,
     rect: DeviceIntRect,
 }
 
+#[derive(Debug)]
 pub struct RenderTaskCollection {
     pub render_task_data: Vec<RenderTaskData>,
     dynamic_tasks: HashMap<(RenderTaskKey, RenderPassIndex), DynamicTaskInfo, BuildHasherDefault<FnvHasher>>,
 }
 
 impl RenderTaskCollection {
     pub fn new(static_render_task_count: usize) -> RenderTaskCollection {
         RenderTaskCollection {
@@ -131,19 +131,19 @@ impl RenderTaskCollection {
         match task.id {
             RenderTaskId::Static(index) => {
                 self.render_task_data[index.0] = task.write_task_data();
                 index
             }
             RenderTaskId::Dynamic(key) => {
                 let index = RenderTaskIndex(self.render_task_data.len());
                 let key = (key, pass);
-                debug_assert!(self.dynamic_tasks.contains_key(&key) == false);
+                debug_assert!(!self.dynamic_tasks.contains_key(&key));
                 self.dynamic_tasks.insert(key, DynamicTaskInfo {
-                    index: index,
+                    index,
                     rect: match task.location {
                         RenderTaskLocation::Fixed => panic!("Dynamic tasks should not have fixed locations!"),
                         RenderTaskLocation::Dynamic(Some((origin, _)), size) => DeviceIntRect::new(origin, size),
                         RenderTaskLocation::Dynamic(None, _) => panic!("Expect the task to be already allocated here"),
                     },
                 });
                 self.render_task_data.push(task.write_task_data());
                 index
@@ -245,16 +245,28 @@ impl BatchList {
             batches.push(new_batch);
         }
 
         let batch = &mut batches[selected_batch_index.unwrap()];
         batch.item_rects.push(*item_bounding_rect);
 
         batch
     }
+
+    fn finalize(&mut self) {
+        // Reverse the instance arrays in the opaque batches
+        // to get maximum z-buffer efficiency by drawing
+        // front-to-back.
+        // TODO(gw): Maybe we can change the batch code to
+        //           build these in reverse and avoid having
+        //           to reverse the instance array here.
+        for batch in &mut self.opaque_batches {
+            batch.instances.reverse();
+        }
+    }
 }
 
 /// Encapsulates the logic of building batches for items that are blended.
 pub struct AlphaBatcher {
     pub batch_list: BatchList,
     tasks: Vec<AlphaBatchTask>,
 }
 
@@ -363,17 +375,17 @@ impl AlphaRenderItem {
                 let clip_task_index = match prim_metadata.clip_task {
                     Some(ref clip_task) => {
                         render_tasks.get_task_index(&clip_task.id, child_pass_index)
                     }
                     None => {
                         OPAQUE_TASK_INDEX
                     }
                 };
-                let needs_blending = !prim_metadata.is_opaque ||
+                let needs_blending = !prim_metadata.opacity.is_opaque ||
                                      needs_clipping ||
                                      transform_kind == TransformedRectKind::Complex;
                 let blend_mode = ctx.prim_store.get_blend_mode(needs_blending, prim_metadata);
 
                 let prim_cache_address = prim_metadata.gpu_location
                                                       .as_int(gpu_cache);
 
                 let base_instance = SimplePrimitiveInstance::new(prim_cache_address,
@@ -655,16 +667,23 @@ impl AlphaBatcher {
                                   ctx,
                                   gpu_cache,
                                   render_tasks,
                                   child_pass_index,
                                   task_index,
                                   deferred_resolves);
             }
         }
+
+        self.batch_list.finalize();
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.batch_list.opaque_batches.is_empty() &&
+        self.batch_list.alpha_batches.is_empty()
     }
 }
 
 /// Batcher managing draw calls into the clip mask (in the RT cache).
 #[derive(Debug)]
 pub struct ClipBatcher {
     /// Rectangle draws fill up the rectangles with rounded corners.
     pub rectangles: Vec<CacheClipInstance>,
@@ -690,76 +709,87 @@ impl ClipBatcher {
                resource_cache: &ResourceCache,
                gpu_cache: &GpuCache,
                geometry_kind: MaskGeometryKind) {
 
         for &(packed_layer_index, ref info) in clips.iter() {
             let instance = CacheClipInstance {
                 task_id: task_index.0 as i32,
                 layer_index: packed_layer_index.0 as i32,
-                address: GpuStoreAddress(0),
+                address: 0,
                 segment: 0,
                 resource_address: 0,
             };
 
-            for clip_index in 0..info.effective_complex_clip_count as usize {
-                let offset = info.complex_clip_range.start.0 + ((CLIP_DATA_GPU_SIZE * clip_index) as i32);
+            for clip_index in 0 .. info.complex_clip_range.get_count() {
+                let gpu_address = info.complex_clip_range.location.as_int(gpu_cache) +
+                                  (CLIP_DATA_GPU_BLOCKS * clip_index) as i32;
                 match geometry_kind {
                     MaskGeometryKind::Default => {
                         self.rectangles.push(CacheClipInstance {
-                            address: GpuStoreAddress(offset),
+                            address: gpu_address,
                             segment: MaskSegment::All as i32,
                             ..instance
                         });
                     }
                     MaskGeometryKind::CornersOnly => {
-                        self.rectangles.extend(&[
+                        self.rectangles.extend_from_slice(&[
                             CacheClipInstance {
-                                address: GpuStoreAddress(offset),
+                                address: gpu_address,
                                 segment: MaskSegment::TopLeftCorner as i32,
                                 ..instance
                             },
                             CacheClipInstance {
-                                address: GpuStoreAddress(offset),
+                                address: gpu_address,
                                 segment: MaskSegment::TopRightCorner as i32,
                                 ..instance
                             },
                             CacheClipInstance {
-                                address: GpuStoreAddress(offset),
+                                address: gpu_address,
                                 segment: MaskSegment::BottomLeftCorner as i32,
                                 ..instance
                             },
                             CacheClipInstance {
-                                address: GpuStoreAddress(offset),
+                                address: gpu_address,
                                 segment: MaskSegment::BottomRightCorner as i32,
                                 ..instance
                             },
                         ]);
                     }
                 }
             }
 
-            if let Some((ref mask, address)) = info.image {
+            for clip_index in 0 .. info.layer_clip_range.get_count() {
+                let gpu_address = info.layer_clip_range.location.as_int(gpu_cache) +
+                                  (CLIP_DATA_GPU_BLOCKS * clip_index) as i32;
+                self.rectangles.push(CacheClipInstance {
+                    address: gpu_address,
+                    segment: MaskSegment::All as i32,
+                    ..instance
+                });
+            }
+
+            if let Some((ref mask, gpu_location)) = info.image {
                 let cache_item = resource_cache.get_cached_image(mask.image, ImageRendering::Auto, None);
                 self.images.entry(cache_item.texture_id)
                            .or_insert(Vec::new())
                            .push(CacheClipInstance {
-                    address: address,
+                    address: gpu_location.as_int(gpu_cache),
                     resource_address: cache_item.uv_rect_handle.as_int(gpu_cache),
                     ..instance
                 })
             }
 
-            for &(ref source, gpu_address) in &info.border_corners {
+            for &(ref source, gpu_location) in &info.border_corners {
+                let gpu_address = gpu_location.as_int(gpu_cache);
                 self.border_clears.push(CacheClipInstance {
                     address: gpu_address,
                     segment: 0,
                     ..instance
                 });
-
                 for clip_index in 0..source.actual_clip_count {
                     self.borders.push(CacheClipInstance {
                         address: gpu_address,
                         segment: 1 + clip_index as i32,
                         ..instance
                     })
                 }
             }
@@ -847,18 +877,18 @@ pub struct RenderTargetList<T> {
 impl<T: RenderTarget> RenderTargetList<T> {
     fn new(target_size: DeviceUintSize, create_initial_target: bool) -> RenderTargetList<T> {
         let mut targets = Vec::new();
         if create_initial_target {
             targets.push(T::new(target_size));
         }
 
         RenderTargetList {
-            targets: targets,
-            target_size: target_size,
+            targets,
+            target_size,
         }
     }
 
     pub fn target_count(&self) -> usize {
         self.targets.len()
     }
 
     fn build(&mut self,
@@ -1129,17 +1159,17 @@ pub struct RenderPass {
     pub color_texture_id: Option<TextureId>,
     pub alpha_texture_id: Option<TextureId>,
 }
 
 impl RenderPass {
     pub fn new(pass_index: isize, is_framebuffer: bool, size: DeviceUintSize) -> RenderPass {
         RenderPass {
             pass_index: RenderPassIndex(pass_index),
-            is_framebuffer: is_framebuffer,
+            is_framebuffer,
             color_targets: RenderTargetList::new(size, is_framebuffer),
             alpha_targets: RenderTargetList::new(size, false),
             tasks: vec![],
             color_texture_id: None,
             alpha_texture_id: None,
         }
     }
 
@@ -1249,19 +1279,19 @@ pub enum AlphaBatchKind {
     RadialGradient,
     BoxShadow,
     CacheImage,
     BorderCorner,
     BorderEdge,
 }
 
 bitflags! {
-    pub flags AlphaBatchKeyFlags: u8 {
-        const NEEDS_CLIPPING  = 0b00000001,
-        const AXIS_ALIGNED    = 0b00000010,
+    pub struct AlphaBatchKeyFlags: u8 {
+        const NEEDS_CLIPPING  = 0b00000001;
+        const AXIS_ALIGNED    = 0b00000010;
     }
 }
 
 impl AlphaBatchKeyFlags {
     pub fn transform_kind(&self) -> TransformedRectKind {
         if self.contains(AXIS_ALIGNED) {
             TransformedRectKind::AxisAligned
         } else {
@@ -1283,20 +1313,20 @@ pub struct AlphaBatchKey {
 }
 
 impl AlphaBatchKey {
     fn new(kind: AlphaBatchKind,
            flags: AlphaBatchKeyFlags,
            blend_mode: BlendMode,
            textures: BatchTextures) -> AlphaBatchKey {
         AlphaBatchKey {
-            kind: kind,
-            flags: flags,
-            blend_mode: blend_mode,
-            textures: textures,
+            kind,
+            flags,
+            blend_mode,
+            textures,
         }
     }
 
     fn is_compatible_with(&self, other: &AlphaBatchKey) -> bool {
         self.kind == other.kind &&
             self.flags == other.flags &&
             self.blend_mode == other.blend_mode &&
             textures_compatible(self.textures.colors[0], other.textures.colors[0]) &&
@@ -1328,17 +1358,17 @@ pub struct BlurCommand {
 
 /// A clipping primitive drawn into the clipping mask.
 /// Could be an image or a rectangle, which defines the
 /// way `address` is treated.
 #[derive(Clone, Copy, Debug)]
 pub struct CacheClipInstance {
     task_id: i32,
     layer_index: i32,
-    address: GpuStoreAddress,
+    address: i32,
     segment: i32,
     resource_address: i32,
 }
 
 // 32 bytes per instance should be enough for anyone!
 #[derive(Debug, Clone)]
 pub struct PrimitiveInstance {
     data: [i32; 8],
@@ -1360,21 +1390,21 @@ struct SimplePrimitiveInstance {
 
 impl SimplePrimitiveInstance {
     fn new(specific_prim_address: i32,
            task_index: RenderTaskIndex,
            clip_task_index: RenderTaskIndex,
            layer_index: PackedLayerIndex,
            z_sort_index: i32) -> SimplePrimitiveInstance {
         SimplePrimitiveInstance {
-            specific_prim_address: specific_prim_address,
+            specific_prim_address,
             task_index: task_index.0 as i32,
             clip_task_index: clip_task_index.0 as i32,
             layer_index: layer_index.0 as i32,
-            z_sort_index: z_sort_index,
+            z_sort_index,
         }
     }
 
     fn build(&self, data0: i32, data1: i32, data2: i32) -> PrimitiveInstance {
         PrimitiveInstance {
             data: [
                 self.specific_prim_address,
                 self.task_index,
@@ -1401,22 +1431,22 @@ pub struct CompositePrimitiveInstance {
 impl CompositePrimitiveInstance {
     fn new(task_index: RenderTaskIndex,
            src_task_index: RenderTaskIndex,
            backdrop_task_index: RenderTaskIndex,
            data0: i32,
            data1: i32,
            z: i32) -> CompositePrimitiveInstance {
         CompositePrimitiveInstance {
-            task_index: task_index,
-            src_task_index: src_task_index,
-            backdrop_task_index: backdrop_task_index,
-            data0: data0,
-            data1: data1,
-            z: z,
+            task_index,
+            src_task_index,
+            backdrop_task_index,
+            data0,
+            data1,
+            z,
         }
     }
 }
 
 impl From<CompositePrimitiveInstance> for PrimitiveInstance {
     fn from(instance: CompositePrimitiveInstance) -> PrimitiveInstance {
         PrimitiveInstance {
             data: [
@@ -1451,17 +1481,17 @@ pub struct PrimitiveBatch {
     pub key: AlphaBatchKey,
     pub instances: Vec<PrimitiveInstance>,
     pub item_rects: Vec<DeviceIntRect>,
 }
 
 impl PrimitiveBatch {
     fn new(key: AlphaBatchKey) -> PrimitiveBatch {
         PrimitiveBatch {
-            key: key,
+            key,
             instances: Vec::new(),
             item_rects: Vec::new(),
         }
     }
 
     fn add_instance(&mut self, instance: PrimitiveInstance) {
         self.instances.push(instance);
     }
@@ -1494,23 +1524,25 @@ pub struct StackingContext {
 
     /// Offset in the parent reference frame to the origin of this stacking
     /// context's coordinate system.
     pub reference_frame_offset: LayerVector2D,
 
     /// The `ClipId` of the owning reference frame.
     pub reference_frame_id: ClipId,
 
-    /// Local bounding rectangle for this stacking context.
-    pub local_bounds: LayerRect,
-
     /// Screen space bounding rectangle for this stacking context,
     /// calculated based on the size and position of all its children.
     pub screen_bounds: DeviceIntRect,
 
+    /// Local bounding rectangle of this stacking context,
+    /// computed as the union of all contained items that are not
+    /// `ContextIsolation::Items` on their own
+    pub isolated_items_bounds: LayerRect,
+
     pub composite_ops: CompositeOps,
     pub clip_scroll_groups: Vec<ClipScrollGroupIndex>,
 
     /// Type of the isolation of the content.
     pub isolation: ContextIsolation,
 
     /// Set for the root stacking context of a display list or an iframe. Used for determining
     /// when to isolate a mix-blend-mode composite.
@@ -1521,34 +1553,33 @@ pub struct StackingContext {
     pub is_visible: bool,
 }
 
 impl StackingContext {
     pub fn new(pipeline_id: PipelineId,
                reference_frame_offset: LayerVector2D,
                is_page_root: bool,
                reference_frame_id: ClipId,
-               local_bounds: LayerRect,
                transform_style: TransformStyle,
                composite_ops: CompositeOps)
                -> StackingContext {
         let isolation = match transform_style {
             TransformStyle::Flat => ContextIsolation::None,
             TransformStyle::Preserve3D => ContextIsolation::Items,
         };
         StackingContext {
-            pipeline_id: pipeline_id,
-            reference_frame_offset: reference_frame_offset,
-            reference_frame_id: reference_frame_id,
-            local_bounds: local_bounds,
+            pipeline_id,
+            reference_frame_offset,
+            reference_frame_id,
             screen_bounds: DeviceIntRect::zero(),
-            composite_ops: composite_ops,
+            isolated_items_bounds: LayerRect::zero(),
+            composite_ops,
             clip_scroll_groups: Vec::new(),
-            isolation: isolation,
-            is_page_root: is_page_root,
+            isolation,
+            is_page_root,
             is_visible: false,
         }
     }
 
     pub fn clip_scroll_group(&self, clip_and_scroll: ClipAndScrollInfo) -> ClipScrollGroupIndex {
         // Currently there is only one scrolled stacking context per context,
         // but eventually this will be selected from the vector based on the
         // scroll layer of this primitive.
@@ -1605,47 +1636,52 @@ impl Default for PackedLayer {
     }
 }
 
 impl PackedLayer {
     pub fn empty() -> PackedLayer {
         Default::default()
     }
 
-    pub fn set_transform(&mut self, transform: LayerToWorldTransform) {
+    pub fn set_transform(&mut self, transform: LayerToWorldTransform) -> bool {
         self.transform = transform;
-        self.inv_transform = self.transform.inverse().unwrap();
+        match self.transform.inverse() {
+            Some(inv) => {
+                self.inv_transform = inv;
+                true
+            }
+            None => false,
+        }
     }
 
     pub fn set_rect(&mut self,
                     local_rect: &LayerRect,
                     screen_rect: &DeviceIntRect,
                     device_pixel_ratio: f32)
                     -> Option<(TransformedRectKind, DeviceIntRect)> {
-        let xf_rect = TransformedRect::new(&local_rect, &self.transform, device_pixel_ratio);
-        xf_rect.bounding_rect.intersection(screen_rect).map(|rect| {
-            self.local_clip_rect = *local_rect;
-            (xf_rect.kind, rect)
-        })
+        self.local_clip_rect = *local_rect;
+        let xf_rect = TransformedRect::new(local_rect, &self.transform, device_pixel_ratio);
+        xf_rect.bounding_rect.intersection(screen_rect)
+                             .map(|rect| (xf_rect.kind, rect))
     }
 }
 
 #[derive(Debug, Clone)]
 pub struct CompositeOps {
     // Requires only a single texture as input (e.g. most filters)
     pub filters: Vec<LowLevelFilterOp>,
 
     // Requires two source textures (e.g. mix-blend-mode)
     pub mix_blend_mode: Option<MixBlendMode>,
 }
 
 impl CompositeOps {
     pub fn new(filters: Vec<LowLevelFilterOp>, mix_blend_mode: Option<MixBlendMode>) -> CompositeOps {
         CompositeOps {
-            filters: filters,
+            filters,
             mix_blend_mode: mix_blend_mode
         }
     }
 
     pub fn count(&self) -> usize {
         self.filters.len() + if self.mix_blend_mode.is_some() { 1 } else { 0 }
     }
 
@@ -1675,17 +1711,16 @@ pub struct Frame {
     pub background_color: Option<ColorF>,
     pub device_pixel_ratio: f32,
     pub cache_size: DeviceUintSize,
     pub passes: Vec<RenderPass>,
     pub profile_counters: FrameProfileCounters,
 
     pub layer_texture_data: Vec<PackedLayer>,
     pub render_task_data: Vec<RenderTaskData>,
-    pub gpu_data32: Vec<GpuBlock32>,
 
     // List of updates that need to be pushed to the
     // gpu resource cache.
     pub gpu_cache_updates: Option<GpuCacheUpdateList>,
 
     // List of textures that we don't know about yet
     // from the backend thread. The render thread
     // will use a callback to resolve these and
@@ -1705,17 +1740,17 @@ fn resolve_image(image_key: ImageKey,
     // by the render thread.
     match image_properties.external_image {
         Some(external_image) => {
             // This is an external texture - we will add it to
             // the deferred resolves list to be patched by
             // the render thread...
             let cache_handle = gpu_cache.push_deferred_per_frame_blocks(1);
             deferred_resolves.push(DeferredResolve {
-                image_properties: image_properties,
+                image_properties,
                 address: gpu_cache.get_address(&cache_handle),
             });
 
             (SourceTexture::External(external_image), cache_handle)
         }
         None => {
             let cache_item = resource_cache.get_cached_image(image_key,
                                                              image_rendering,
--- a/gfx/webrender/src/util.rs
+++ b/gfx/webrender/src/util.rs
@@ -1,28 +1,30 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use std::f32::consts::{FRAC_1_SQRT_2};
 use euclid::{Point2D, Rect, Size2D};
-use euclid::{TypedRect, TypedPoint2D, TypedSize2D, TypedTransform3D};
-use webrender_traits::{DeviceIntRect, DeviceIntPoint, DeviceIntSize};
-use webrender_traits::{LayerRect, WorldPoint3D, LayerToWorldTransform};
-use webrender_traits::{BorderRadius, ComplexClipRegion, LayoutRect};
+use euclid::{TypedRect, TypedPoint2D, TypedSize2D, TypedTransform2D, TypedTransform3D};
+use api::{DeviceIntRect, DevicePoint, DeviceRect, DeviceSize};
+use api::{LayerRect, WorldPoint3D, LayerToWorldTransform};
+use api::{BorderRadius, ComplexClipRegion, LayoutRect};
 use num_traits::Zero;
 
 // Matches the definition of SK_ScalarNearlyZero in Skia.
 const NEARLY_ZERO: f32 = 1.0 / 4096.0;
 
 // TODO: Implement these in euclid!
 pub trait MatrixHelpers<Src, Dst> {
     fn transform_rect(&self, rect: &TypedRect<f32, Src>) -> TypedRect<f32, Dst>;
     fn is_identity(&self) -> bool;
     fn preserves_2d_axis_alignment(&self) -> bool;
+    fn inverse_project(&self, target: &TypedPoint2D<f32, Dst>) -> Option<TypedPoint2D<f32, Src>>;
+    fn inverse_rect_footprint(&self, rect: &TypedRect<f32, Dst>) -> TypedRect<f32, Src>;
 }
 
 impl<Src, Dst> MatrixHelpers<Src, Dst> for TypedTransform3D<f32, Src, Dst> {
     fn transform_rect(&self, rect: &TypedRect<f32, Src>) -> TypedRect<f32, Dst> {
         let top_left = self.transform_point2d(&rect.origin);
         let top_right = self.transform_point2d(&rect.top_right());
         let bottom_left = self.transform_point2d(&rect.bottom_left());
         let bottom_right = self.transform_point2d(&rect.bottom_right());
@@ -59,16 +61,41 @@ impl<Src, Dst> MatrixHelpers<Src, Dst> f
         }
         if self.m22.abs() > NEARLY_ZERO {
             col1 += 1;
             row1 += 1;
         }
 
         col0 < 2 && col1 < 2 && row0 < 2 && row1 < 2
     }
+
+    fn inverse_project(&self, target: &TypedPoint2D<f32, Dst>) -> Option<TypedPoint2D<f32, Src>> {
+        let m: TypedTransform2D<f32, Src, Dst>;
+        m = TypedTransform2D::column_major(self.m11 - target.x * self.m14,
+                                           self.m21 - target.x * self.m24,
+                                           self.m41 - target.x * self.m44,
+                                           self.m12 - target.y * self.m14,
+                                           self.m22 - target.y * self.m24,
+                                           self.m42 - target.y * self.m44);
+        m.inverse()
+         .map(|inv| TypedPoint2D::new(inv.m31, inv.m32))
+    }
+
+    fn inverse_rect_footprint(&self, rect: &TypedRect<f32, Dst>) -> TypedRect<f32, Src> {
+        TypedRect::from_points(&[
+            self.inverse_project(&rect.origin)
+                .unwrap_or(TypedPoint2D::zero()),
+            self.inverse_project(&rect.top_right())
+                .unwrap_or(TypedPoint2D::zero()),
+            self.inverse_project(&rect.bottom_left())
+                .unwrap_or(TypedPoint2D::zero()),
+            self.inverse_project(&rect.bottom_right())
+                .unwrap_or(TypedPoint2D::zero()),
+        ])
+    }
 }
 
 pub trait RectHelpers<U> where Self: Sized {
     fn contains_rect(&self, other: &Self) -> bool;
     fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self;
     fn is_well_formed_and_nonempty(&self) -> bool;
 }
 
@@ -170,101 +197,71 @@ pub enum TransformedRectKind {
 pub struct TransformedRect {
     pub local_rect: LayerRect,
     pub bounding_rect: DeviceIntRect,
     pub inner_rect: DeviceIntRect,
     pub vertices: [WorldPoint3D; 4],
     pub kind: TransformedRectKind,
 }
 
+// Having an unlimited bounding box is fine up until we try
+// to cast it to `i32`, where we get `-2147483648` for any
+// values larger than or equal to 2^31.
+//Note: clamping to i32::MIN and i32::MAX is not a solution,
+// with explanation left as an exercise for the reader.
+const MAX_COORD: f32 = 1.0e9;
+
 impl TransformedRect {
     pub fn new(rect: &LayerRect,
                transform: &LayerToWorldTransform,
                device_pixel_ratio: f32) -> TransformedRect {
 
         let kind = if transform.preserves_2d_axis_alignment() {
             TransformedRectKind::AxisAligned
         } else {
             TransformedRectKind::Complex
         };
 
-        // FIXME(gw): This code is meant to be a fast path for simple transforms.
-        // However, it fails on transforms that translate Z but result in an
-        // axis aligned rect.
+
+        let vertices = [
+            transform.transform_point3d(&rect.origin.to_3d()),
+            transform.transform_point3d(&rect.bottom_left().to_3d()),
+            transform.transform_point3d(&rect.bottom_right().to_3d()),
+            transform.transform_point3d(&rect.top_right().to_3d()),
+        ];
 
-/*
-        match kind {
-            TransformedRectKind::AxisAligned => {
-                let v0 = transform.transform_point(&rect.origin);
-                let v1 = transform.transform_point(&rect.top_right());
-                let v2 = transform.transform_point(&rect.bottom_left());
-                let v3 = transform.transform_point(&rect.bottom_right());
+        let (mut xs, mut ys) = ([0.0; 4], [0.0; 4]);
 
-                let screen_min_dp = Point2D::new(DevicePixel((v0.x * device_pixel_ratio).floor() as i32),
-                                                 DevicePixel((v0.y * device_pixel_ratio).floor() as i32));
-                let screen_max_dp = Point2D::new(DevicePixel((v3.x * device_pixel_ratio).ceil() as i32),
-                                                 DevicePixel((v3.y * device_pixel_ratio).ceil() as i32));
+        for (vertex, (x, y)) in vertices.iter().zip(xs.iter_mut().zip(ys.iter_mut())) {
+            *x = get_normal(vertex.x).unwrap_or(0.0);
+            *y = get_normal(vertex.y).unwrap_or(0.0);
+        }
 
-                let screen_rect_dp = Rect::new(screen_min_dp, Size2D::new(screen_max_dp.x - screen_min_dp.x,
-                                                                          screen_max_dp.y - screen_min_dp.y));
+        xs.sort_by(|a, b| a.partial_cmp(b).unwrap());
+        ys.sort_by(|a, b| a.partial_cmp(b).unwrap());
 
-                TransformedRect {
-                    local_rect: *rect,
-                    vertices: [
-                        Point4D::new(v0.x, v0.y, 0.0, 1.0),
-                        Point4D::new(v1.x, v1.y, 0.0, 1.0),
-                        Point4D::new(v2.x, v2.y, 0.0, 1.0),
-                        Point4D::new(v3.x, v3.y, 0.0, 1.0),
-                    ],
-                    bounding_rect: screen_rect_dp,
-                    kind: kind,
-                }
-            }
-            TransformedRectKind::Complex => {
-                */
-                let vertices = [
-                    transform.transform_point3d(&rect.origin.to_3d()),
-                    transform.transform_point3d(&rect.bottom_left().to_3d()),
-                    transform.transform_point3d(&rect.bottom_right().to_3d()),
-                    transform.transform_point3d(&rect.top_right().to_3d()),
-                ];
-
-                let (mut xs, mut ys) = ([0.0; 4], [0.0; 4]);
+        let outer_min_dp = (DevicePoint::new(xs[0], ys[0]) * device_pixel_ratio).floor();
+        let outer_max_dp = (DevicePoint::new(xs[3], ys[3]) * device_pixel_ratio).ceil();
+        let inner_min_dp = (DevicePoint::new(xs[1], ys[1]) * device_pixel_ratio).ceil();
+        let inner_max_dp = (DevicePoint::new(xs[2], ys[2]) * device_pixel_ratio).floor();
 
-                for (vertex, (x, y)) in vertices.iter().zip(xs.iter_mut().zip(ys.iter_mut())) {
-                    *x = get_normal(vertex.x).unwrap_or(0.0);
-                    *y = get_normal(vertex.y).unwrap_or(0.0);
-                }
-
-                xs.sort_by(|a, b| a.partial_cmp(b).unwrap());
-                ys.sort_by(|a, b| a.partial_cmp(b).unwrap());
+        let max_rect = DeviceRect::new(DevicePoint::new(-MAX_COORD, -MAX_COORD),
+                                       DeviceSize::new(2.0*MAX_COORD, 2.0*MAX_COORD));
+        let bounding_rect = DeviceRect::new(outer_min_dp, (outer_max_dp - outer_min_dp).to_size())
+            .intersection(&max_rect).unwrap_or(max_rect).to_i32();
+        let inner_rect = DeviceRect::new(inner_min_dp, (inner_max_dp - inner_min_dp).to_size())
+            .intersection(&max_rect).unwrap_or(max_rect).to_i32();
 
-                let outer_min_dp = DeviceIntPoint::new((xs[0] * device_pixel_ratio).floor() as i32,
-                                                       (ys[0] * device_pixel_ratio).floor() as i32);
-                let outer_max_dp = DeviceIntPoint::new((xs[3] * device_pixel_ratio).ceil() as i32,
-                                                       (ys[3] * device_pixel_ratio).ceil() as i32);
-                let inner_min_dp = DeviceIntPoint::new((xs[1] * device_pixel_ratio).ceil() as i32,
-                                                       (ys[1] * device_pixel_ratio).ceil() as i32);
-                let inner_max_dp = DeviceIntPoint::new((xs[2] * device_pixel_ratio).floor() as i32,
-                                                       (ys[2] * device_pixel_ratio).floor() as i32);
-
-                TransformedRect {
-                    local_rect: *rect,
-                    vertices: vertices,
-                    bounding_rect: DeviceIntRect::new(outer_min_dp,
-                                                      DeviceIntSize::new(outer_max_dp.x.saturating_sub(outer_min_dp.x),
-                                                                         outer_max_dp.y.saturating_sub(outer_min_dp.y))),
-                    inner_rect: DeviceIntRect::new(inner_min_dp,
-                                                   DeviceIntSize::new(inner_max_dp.x.saturating_sub(inner_min_dp.x),
-                                                                      inner_max_dp.y.saturating_sub(inner_min_dp.y))),
-                    kind: kind,
-                }
-                /*
-            }
-        }*/
+        TransformedRect {
+            local_rect: *rect,
+            vertices,
+            bounding_rect,
+            inner_rect,
+            kind,
+        }
     }
 }
 
 #[inline(always)]
 pub fn pack_as_float(value: u32) -> f32 {
     value as f32 + 0.5
 }
 
@@ -322,8 +319,27 @@ pub fn recycle_vec<T>(mut old_vec: Vec<T
         // more memory than they need.
         return Vec::with_capacity(old_vec.len());
     }
 
     old_vec.clear();
 
     return old_vec;
 }
+
+
+#[cfg(test)]
+pub mod test {
+    use super::*;
+    use euclid::{Point2D, Radians, Transform3D};
+    use std::f32::consts::PI;
+
+    #[test]
+    fn inverse_project() {
+        let m0 = Transform3D::identity();
+        let p0 = Point2D::new(1.0, 2.0);
+        // an identical transform doesn't need any inverse projection
+        assert_eq!(m0.inverse_project(&p0), Some(p0));
+        let m1 = Transform3D::create_rotation(0.0, 1.0, 0.0, Radians::new(PI / 3.0));
+        // rotation by 60 degrees would imply scaling of X component by a factor of 2
+        assert_eq!(m1.inverse_project(&p0), Some(Point2D::new(2.0, 2.0)));
+    }
+}
--- a/gfx/webrender/src/webgl_stubs.rs
+++ b/gfx/webrender/src/webgl_stubs.rs
@@ -2,19 +2,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Stubs for the types contained in webgl_types.rs
 //!
 //! The API surface provided here should be roughly the same to the one provided
 //! in webgl_types, modulo completely compiled-out stuff.
 
-use webrender_traits::DeviceIntSize;
-use webrender_traits::{GLContextAttributes, GLLimits};
-use webrender_traits::WebGLCommand;
+use api::DeviceIntSize;
+use api::{GLContextAttributes, GLLimits};
+use api::WebGLCommand;
 
 pub struct GLContextHandleWrapper;
 
 impl GLContextHandleWrapper {
     pub fn new_context(&self,
                        _: DeviceIntSize,
                        _: GLContextAttributes,
                        _: Option<Box<GLContextDispatcher>>) -> Result<GLContextWrapper, &'static str> {
--- a/gfx/webrender/src/webgl_types.rs
+++ b/gfx/webrender/src/webgl_types.rs
@@ -5,17 +5,17 @@
 //! A set of WebGL-related types, in their own module so it's easy to
 //! compile it off.
 
 use gleam::gl;
 use offscreen_gl_context::{NativeGLContext, NativeGLContextHandle};
 use offscreen_gl_context::{GLContext, NativeGLContextMethods, GLContextDispatcher};
 use offscreen_gl_context::{OSMesaContext, OSMesaContextHandle};
 use offscreen_gl_context::{ColorAttachmentType, GLContextAttributes, GLLimits};
-use webrender_traits::{WebGLCommand, DeviceIntSize};
+use api::{WebGLCommand, DeviceIntSize};
 
 pub enum GLContextHandleWrapper {
     Native(NativeGLContextHandle),
     OSMesa(OSMesaContextHandle),
 }
 
 impl GLContextHandleWrapper {
     pub fn current_native_handle() -> Option<GLContextHandleWrapper> {
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_api/Cargo.toml
@@ -0,0 +1,30 @@
+[package]
+name = "webrender_api"
+version = "0.47.0"
+authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
+license = "MPL-2.0"
+repository = "https://github.com/servo/webrender"
+
+[features]
+nightly = ["euclid/unstable", "serde/unstable"]
+ipc = ["ipc-channel"]
+webgl = ["offscreen_gl_context"]
+
+[dependencies]
+app_units = "0.5"
+bincode = "0.8"
+byteorder = "1.0"
+euclid = "0.15"
+gleam = "0.4.5"
+heapsize = ">= 0.3.6, < 0.5"
+ipc-channel = {version = "0.8", optional = true}
+offscreen_gl_context = {version = "0.11", features = ["serde"], optional = true}
+serde = { version = "1.0", features = ["rc", "derive"] }
+time = "0.1"
+
+[target.'cfg(target_os = "macos")'.dependencies]
+core-foundation = "0.3"
+core-graphics = "0.8"
+
+[target.'cfg(target_os = "windows")'.dependencies]
+dwrote = "0.4"
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_api/src/api.rs
@@ -0,0 +1,600 @@
+/* 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 channel::{self, MsgSender, Payload, PayloadSenderHelperMethods, PayloadSender};
+#[cfg(feature = "webgl")]
+use offscreen_gl_context::{GLContextAttributes, GLLimits};
+use std::cell::Cell;
+use std::fmt;
+use std::marker::PhantomData;
+use {BuiltDisplayList, BuiltDisplayListDescriptor, ClipId, ColorF, DeviceIntPoint, DeviceIntSize};
+use {DeviceUintRect, DeviceUintSize, FontKey, GlyphDimensions, GlyphKey};
+use {ImageData, ImageDescriptor, ImageKey, LayoutPoint, LayoutVector2D, LayoutSize, LayoutTransform};
+use {NativeFontHandle, WorldPoint};
+#[cfg(feature = "webgl")]
+use {WebGLCommand, WebGLContextId};
+
+pub type TileSize = u16;
+
+#[derive(Clone, Deserialize, Serialize)]
+pub enum ApiMsg {
+    AddRawFont(FontKey, Vec<u8>, u32),
+    AddNativeFont(FontKey, NativeFontHandle),
+    DeleteFont(FontKey),
+    /// Gets the glyph dimensions
+    GetGlyphDimensions(Vec<GlyphKey>, MsgSender<Vec<Option<GlyphDimensions>>>),
+    /// Adds an image from the resource cache.
+    AddImage(ImageKey, ImageDescriptor, ImageData, Option<TileSize>),
+    /// Updates the the resource cache with the new image data.
+    UpdateImage(ImageKey, ImageDescriptor, ImageData, Option<DeviceUintRect>),
+    /// Drops an image from the resource cache.
+    DeleteImage(ImageKey),
+    CloneApi(MsgSender<IdNamespace>),
+    /// Supplies a new frame to WebRender.
+    ///
+    /// After receiving this message, WebRender will read the display list from the payload channel.
+    // TODO: We should consider using named members to avoid confusion.
+    SetDisplayList(Option<ColorF>,
+                   Epoch,
+                   PipelineId,
+                   LayoutSize, // viewport_size
+                   LayoutSize, // content size
+                   BuiltDisplayListDescriptor,
+                   bool),
+    SetPageZoom(ZoomFactor),
+    SetPinchZoom(ZoomFactor),
+    SetPan(DeviceIntPoint),
+    SetRootPipeline(PipelineId),
+    SetWindowParameters(DeviceUintSize, DeviceUintRect),
+    Scroll(ScrollLocation, WorldPoint, ScrollEventPhase),
+    ScrollNodeWithId(LayoutPoint, ClipId, ScrollClamping),
+    TickScrollingBounce,
+    TranslatePointToLayerSpace(WorldPoint, MsgSender<(LayoutPoint, PipelineId)>),
+    GetScrollNodeState(MsgSender<Vec<ScrollLayerState>>),
+    RequestWebGLContext(DeviceIntSize, GLContextAttributes, MsgSender<Result<(WebGLContextId, GLLimits), String>>),
+    ResizeWebGLContext(WebGLContextId, DeviceIntSize),
+    WebGLCommand(WebGLContextId, WebGLCommand),
+    GenerateFrame(Option<DynamicProperties>),
+    // WebVR commands that must be called in the WebGL render thread.
+    VRCompositorCommand(WebGLContextId, VRCompositorCommand),
+    /// An opaque handle that must be passed to the render notifier. It is used by Gecko
+    /// to forward gecko-specific messages to the render thread preserving the ordering
+    /// within the other messages.
+    ExternalEvent(ExternalEvent),
+    ShutDown,
+}
+
+impl fmt::Debug for ApiMsg {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.write_str(match *self {
+            ApiMsg::AddRawFont(..) => "ApiMsg::AddRawFont",
+            ApiMsg::AddNativeFont(..) => "ApiMsg::AddNativeFont",
+            ApiMsg::DeleteFont(..) => "ApiMsg::DeleteFont",
+            ApiMsg::GetGlyphDimensions(..) => "ApiMsg::GetGlyphDimensions",
+            ApiMsg::AddImage(..) => "ApiMsg::AddImage",
+            ApiMsg::UpdateImage(..) => "ApiMsg::UpdateImage",
+            ApiMsg::DeleteImage(..) => "ApiMsg::DeleteImage",
+            ApiMsg::CloneApi(..) => "ApiMsg::CloneApi",
+            ApiMsg::SetDisplayList(..) => "ApiMsg::SetDisplayList",
+            ApiMsg::SetRootPipeline(..) => "ApiMsg::SetRootPipeline",
+            ApiMsg::Scroll(..) => "ApiMsg::Scroll",
+            ApiMsg::ScrollNodeWithId(..) => "ApiMsg::ScrollNodeWithId",
+            ApiMsg::TickScrollingBounce => "ApiMsg::TickScrollingBounce",
+            ApiMsg::TranslatePointToLayerSpace(..) => "ApiMsg::TranslatePointToLayerSpace",
+            ApiMsg::GetScrollNodeState(..) => "ApiMsg::GetScrollNodeState",
+            ApiMsg::RequestWebGLContext(..) => "ApiMsg::RequestWebGLContext",
+            ApiMsg::ResizeWebGLContext(..) => "ApiMsg::ResizeWebGLContext",
+            ApiMsg::WebGLCommand(..) => "ApiMsg::WebGLCommand",
+            ApiMsg::GenerateFrame(..) => "ApiMsg::GenerateFrame",
+            ApiMsg::VRCompositorCommand(..) => "ApiMsg::VRCompositorCommand",
+            ApiMsg::ExternalEvent(..) => "ApiMsg::ExternalEvent",
+            ApiMsg::ShutDown => "ApiMsg::ShutDown",
+            ApiMsg::SetPageZoom(..) => "ApiMsg::SetPageZoom",
+            ApiMsg::SetPinchZoom(..) => "ApiMsg::SetPinchZoom",
+            ApiMsg::SetPan(..) => "ApiMsg::SetPan",
+            ApiMsg::SetWindowParameters(..) => "ApiMsg::SetWindowParameters",
+        })
+    }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
+pub struct Epoch(pub u32);
+
+#[cfg(not(feature = "webgl"))]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
+pub struct WebGLContextId(pub usize);
+
+#[cfg(not(feature = "webgl"))]
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct GLContextAttributes([u8; 0]);
+
+#[cfg(not(feature = "webgl"))]
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct GLLimits([u8; 0]);
+
+#[cfg(not(feature = "webgl"))]
+#[derive(Clone, Deserialize, Serialize)]
+pub enum WebGLCommand {
+    FenceAndWaitSync,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub struct PipelineId(pub u32, pub u32);
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
+pub struct IdNamespace(pub u32);
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
+pub struct ResourceId(pub u32);
+
+/// An opaque pointer-sized value.
+#[repr(C)]
+#[derive(Clone, Deserialize, Serialize)]
+pub struct ExternalEvent {
+    raw: usize,
+}
+
+unsafe impl Send for ExternalEvent {}
+
+impl ExternalEvent {
+    pub fn from_raw(raw: usize) -> Self { ExternalEvent { raw: raw } }
+    /// Consumes self to make it obvious that the event should be forwarded only once.
+    pub fn unwrap(self) -> usize { self.raw }
+}
+
+#[derive(Clone, Deserialize, Serialize)]
+pub enum ScrollClamping {
+    ToContentBounds,
+    NoClamping,
+}
+
+#[derive(Clone, Deserialize, Serialize)]
+pub struct RenderApiSender {
+    api_sender: MsgSender<ApiMsg>,
+    payload_sender: PayloadSender,
+}
+
+impl RenderApiSender {
+    pub fn new(api_sender: MsgSender<ApiMsg>,
+               payload_sender: PayloadSender)
+               -> RenderApiSender {
+        RenderApiSender {
+            api_sender,
+            payload_sender,
+        }
+    }
+
+    pub fn create_api(&self) -> RenderApi {
+        let RenderApiSender {
+            ref api_sender,
+            ref payload_sender
+        } = *self;
+        let (sync_tx, sync_rx) = channel::msg_channel().unwrap();
+        let msg = ApiMsg::CloneApi(sync_tx);
+        api_sender.send(msg).unwrap();
+        RenderApi {
+            api_sender: api_sender.clone(),
+            payload_sender: payload_sender.clone(),
+            id_namespace: sync_rx.recv().unwrap(),
+            next_id: Cell::new(ResourceId(0)),
+        }
+    }
+}
+
+pub struct RenderApi {
+    pub api_sender: MsgSender<ApiMsg>,
+    pub payload_sender: PayloadSender,
+    pub id_namespace: IdNamespace,
+    pub next_id: Cell<ResourceId>,
+}
+
+impl RenderApi {
+    pub fn clone_sender(&self) -> RenderApiSender {
+        RenderApiSender::new(self.api_sender.clone(), self.payload_sender.clone())
+    }
+
+    pub fn generate_font_key(&self) -> FontKey {
+        let new_id = self.next_unique_id();
+        FontKey::new(new_id.0, new_id.1)
+    }
+
+    pub fn add_raw_font(&self, key: FontKey, bytes: Vec<u8>, index: u32) {
+        let msg = ApiMsg::AddRawFont(key, bytes, index);
+        self.api_sender.send(msg).unwrap();
+    }
+
+    pub fn add_native_font(&self, key: FontKey, native_font_handle: NativeFontHandle) {
+        let msg = ApiMsg::AddNativeFont(key, native_font_handle);
+        self.api_sender.send(msg).unwrap();
+    }
+
+    pub fn delete_font(&self, key: FontKey) {
+        let msg = ApiMsg::DeleteFont(key);
+        self.api_sender.send(msg).unwrap();
+    }
+
+    /// Gets the dimensions for the supplied glyph keys
+    ///
+    /// Note: Internally, the internal texture cache doesn't store
+    /// 'empty' textures (height or width = 0)
+    /// This means that glyph dimensions e.g. for spaces (' ') will mostly be None.
+    pub fn get_glyph_dimensions(&self, glyph_keys: Vec<GlyphKey>)
+                                -> Vec<Option<GlyphDimensions>> {
+        let (tx, rx) = channel::msg_channel().unwrap();
+        let msg = ApiMsg::GetGlyphDimensions(glyph_keys, tx);
+        self.api_sender.send(msg).unwrap();
+        rx.recv().unwrap()
+    }
+
+    /// Creates an `ImageKey`.
+    pub fn generate_image_key(&self) -> ImageKey {
+        let new_id = self.next_unique_id();
+        ImageKey::new(new_id.0, new_id.1)
+    }
+
+    /// Adds an image identified by the `ImageKey`.
+    pub fn add_image(&self,
+                     key: ImageKey,
+                     descriptor: ImageDescriptor,
+                     data: ImageData,
+                     tiling: Option<TileSize>) {
+        let msg = ApiMsg::AddImage(key, descriptor, data, tiling);
+        self.api_sender.send(msg).unwrap();
+    }
+
+    /// Updates a specific image.
+    ///
+    /// Currently doesn't support changing dimensions or format by updating.
+    // TODO: Support changing dimensions (and format) during image update?
+    pub fn update_image(&self,
+                        key: ImageKey,
+                        descriptor: ImageDescriptor,
+                        data: ImageData,
+                        dirty_rect: Option<DeviceUintRect>) {
+        let msg = ApiMsg::UpdateImage(key, descriptor, data, dirty_rect);
+        self.api_sender.send(msg).unwrap();
+    }
+
+    /// Deletes the specific image.
+    pub fn delete_image(&self, key: ImageKey) {
+        let msg = ApiMsg::DeleteImage(key);
+        self.api_sender.send(msg).unwrap();
+    }
+
+    /// Sets the root pipeline.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use webrender_api::{PipelineId, RenderApiSender};
+    /// # fn example(sender: RenderApiSender) {
+    /// let api = sender.create_api();
+    /// // ...
+    /// let pipeline_id = PipelineId(0, 0);
+    /// api.set_root_pipeline(pipeline_id);
+    /// # }
+    /// ```
+    pub fn set_root_pipeline(&self, pipeline_id: PipelineId) {
+        let msg = ApiMsg::SetRootPipeline(pipeline_id);
+        self.api_sender.send(msg).unwrap();
+    }
+
+    /// Supplies a new frame to WebRender.
+    ///
+    /// Non-blocking, it notifies a worker process which processes the display list.
+    /// When it's done and a RenderNotifier has been set in `webrender::renderer::Renderer`,
+    /// [new_frame_ready()][notifier] gets called.
+    ///
+    /// Note: Scrolling doesn't require an own Frame.
+    ///
+    /// Arguments:
+    ///
+    /// * `background_color`: The background color of this pipeline.
+    /// * `epoch`: The unique Frame ID, monotonically increasing.
+    /// * `viewport_size`: The size of the viewport for this frame.
+    /// * `pipeline_id`: The ID of the pipeline that is supplying this display list.
+    /// * `content_size`: The total screen space size of this display list's display items.
+    /// * `display_list`: The root Display list used in this frame.
+    /// * `preserve_frame_state`: If a previous frame exists which matches this pipeline
+    ///                           id, this setting determines if frame state (such as scrolling
+    ///                           position) should be preserved for this new display list.
+    ///
+    /// [notifier]: trait.RenderNotifier.html#tymethod.new_frame_ready
+    pub fn set_display_list(&self,
+                            background_color: Option<ColorF>,
+                            epoch: Epoch,
+                            viewport_size: LayoutSize,
+                            (pipeline_id, content_size, display_list): (PipelineId, LayoutSize, BuiltDisplayList),
+                            preserve_frame_state: bool) {
+        let (display_list_data, display_list_descriptor) = display_list.into_data();
+        let msg = ApiMsg::SetDisplayList(background_color,
+                                         epoch,
+                                         pipeline_id,
+                                         viewport_size,
+                                         content_size,
+                                         display_list_descriptor,
+                                         preserve_frame_state);
+        self.api_sender.send(msg).unwrap();
+
+        self.payload_sender.send_payload(Payload {
+            epoch,
+            pipeline_id,
+            display_list_data,
+        }).unwrap();
+    }
+
+    /// Scrolls the scrolling layer under the `cursor`
+    ///
+    /// WebRender looks for the layer closest to the user
+    /// which has `ScrollPolicy::Scrollable` set.
+    pub fn scroll(&self, scroll_location: ScrollLocation, cursor: WorldPoint, phase: ScrollEventPhase) {
+        let msg = ApiMsg::Scroll(scroll_location, cursor, phase);
+        self.api_sender.send(msg).unwrap();
+    }
+
+    pub fn scroll_node_with_id(&self, origin: LayoutPoint, id: ClipId, clamp: ScrollClamping) {
+        let msg = ApiMsg::ScrollNodeWithId(origin, id, clamp);
+        self.api_sender.send(msg).unwrap();
+    }
+
+    pub fn set_page_zoom(&self, page_zoom: ZoomFactor) {
+        let msg = ApiMsg::SetPageZoom(page_zoom);
+        self.api_sender.send(msg).unwrap();
+    }
+
+    pub fn set_pinch_zoom(&self, pinch_zoom: ZoomFactor) {
+        let msg = ApiMsg::SetPinchZoom(pinch_zoom);
+        self.api_sender.send(msg).unwrap();
+    }
+
+    pub fn set_pan(&self, pan: DeviceIntPoint) {
+        let msg = ApiMsg::SetPan(pan);
+        self.api_sender.send(msg).unwrap();
+    }
+
+    pub fn set_window_parameters(&self,
+                                 window_size: DeviceUintSize,
+                                 inner_rect: DeviceUintRect) {
+        let msg = ApiMsg::SetWindowParameters(window_size, inner_rect);
+        self.api_sender.send(msg).unwrap();
+    }
+
+    pub fn tick_scrolling_bounce_animations(&self) {
+        let msg = ApiMsg::TickScrollingBounce;
+        self.api_sender.send(msg).unwrap();
+    }
+
+    /// Translates a point from viewport coordinates to layer space
+    pub fn translate_point_to_layer_space(&self, point: &WorldPoint)
+                                          -> (LayoutPoint, PipelineId) {
+        let (tx, rx) = channel::msg_channel().unwrap();
+        let msg = ApiMsg::TranslatePointToLayerSpace(*point, tx);
+        self.api_sender.send(msg).unwrap();
+        rx.recv().unwrap()
+    }
+
+    pub fn get_scroll_node_state(&self) -> Vec<ScrollLayerState> {
+        let (tx, rx) = channel::msg_channel().unwrap();
+        let msg = ApiMsg::GetScrollNodeState(tx);
+        self.api_sender.send(msg).unwrap();
+        rx.recv().unwrap()
+    }
+
+    pub fn request_webgl_context(&self, size: &DeviceIntSize, attributes: GLContextAttributes)
+                                 -> Result<(WebGLContextId, GLLimits), String> {
+        let (tx, rx) = channel::msg_channel().unwrap();
+        let msg = ApiMsg::RequestWebGLContext(*size, attributes, tx);
+        self.api_sender.send(msg).unwrap();
+        rx.recv().unwrap()
+    }
+
+    pub fn resize_webgl_context(&self, context_id: WebGLContextId, size: &DeviceIntSize) {
+        let msg = ApiMsg::ResizeWebGLContext(context_id, *size);
+        self.api_sender.send(msg).unwrap();
+    }
+
+    pub fn send_webgl_command(&self, context_id: WebGLContextId, command: WebGLCommand) {
+        let msg = ApiMsg::WebGLCommand(context_id, command);
+        self.api_sender.send(msg).unwrap();
+    }
+
+    /// Generate a new frame. Optionally, supply a list of animated
+    /// property bindings that should be used to resolve bindings
+    /// in the current display list.
+    pub fn generate_frame(&self, property_bindings: Option<DynamicProperties>) {
+        let msg = ApiMsg::GenerateFrame(property_bindings);
+        self.api_sender.send(msg).unwrap();
+    }
+
+    pub fn send_vr_compositor_command(&self, context_id: WebGLContextId, command: VRCompositorCommand) {
+        let msg = ApiMsg::VRCompositorCommand(context_id, command);
+        self.api_sender.send(msg).unwrap();
+    }
+
+    pub fn send_external_event(&self, evt: ExternalEvent) {
+        let msg = ApiMsg::ExternalEvent(evt);
+        self.api_sender.send(msg).unwrap();
+    }
+
+    pub fn shut_down(&self) {
+        self.api_sender.send(ApiMsg::ShutDown).unwrap();
+    }
+
+    /// Create a new unique key that can be used for
+    /// animated property bindings.
+    pub fn generate_property_binding_key<T: Copy>(&self) -> PropertyBindingKey<T> {
+        let new_id = self.next_unique_id();
+        PropertyBindingKey {
+            id: PropertyBindingId {
+                namespace: new_id.0,
+                uid: new_id.1,
+            },
+            _phantom: PhantomData,
+        }
+    }
+
+    #[inline]
+    fn next_unique_id(&self) -> (u32, u32) {
+        let IdNamespace(namespace) = self.id_namespace;
+        let ResourceId(id) = self.next_id.get();
+        self.next_id.set(ResourceId(id + 1));
+        (namespace, id)
+    }
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub enum ScrollEventPhase {
+    /// The user started scrolling.
+    Start,
+    /// The user performed a scroll. The Boolean flag indicates whether the user's fingers are
+    /// down, if a touchpad is in use. (If false, the event is a touchpad fling.)
+    Move(bool),
+    /// The user ended scrolling.
+    End,
+}
+
+#[derive(Clone, Deserialize, Serialize)]
+pub struct ScrollLayerState {
+    pub id: ClipId,
+    pub scroll_offset: LayoutVector2D,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
+pub enum ScrollLocation {
+    /// Scroll by a certain amount.
+    Delta(LayoutVector2D),
+    /// Scroll to very top of element.
+    Start,
+    /// Scroll to very bottom of element.
+    End
+}
+
+/// Represents a zoom factor.
+#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
+pub struct ZoomFactor(f32);
+
+impl ZoomFactor {
+    /// Construct a new zoom factor.
+    pub fn new(scale: f32) -> ZoomFactor {
+        ZoomFactor(scale)
+    }
+
+    /// Get the zoom factor as an untyped float.
+    pub fn get(&self) -> f32 {
+        self.0
+    }
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, Eq, Hash)]
+pub struct PropertyBindingId {
+    namespace: u32,
+    uid: u32,
+}
+
+impl PropertyBindingId {
+    pub fn new(value: u64) -> Self {
+        PropertyBindingId {
+            namespace: (value>>32) as u32,
+            uid: value as u32,
+        }
+    }
+}
+
+/// A unique key that is used for connecting animated property
+/// values to bindings in the display list.
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct PropertyBindingKey<T> {
+    pub id: PropertyBindingId,
+    _phantom: PhantomData<T>,
+}
+
+/// Construct a property value from a given key and value.
+impl<T: Copy> PropertyBindingKey<T> {
+    pub fn with(&self, value: T) -> PropertyValue<T> {
+        PropertyValue {
+            key: *self,
+            value,
+        }
+    }
+}
+
+impl<T> PropertyBindingKey<T> {
+    pub fn new(value: u64) -> Self {
+        PropertyBindingKey {
+            id: PropertyBindingId::new(value),
+            _phantom: PhantomData,
+        }
+    }
+}
+
+/// A binding property can either be a specific value
+/// (the normal, non-animated case) or point to a binding location
+/// to fetch the current value from.
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub enum PropertyBinding<T> {
+    Value(T),
+    Binding(PropertyBindingKey<T>),
+}
+
+impl<T> From<T> for PropertyBinding<T> {
+    fn from(value: T) -> PropertyBinding<T> {
+        PropertyBinding::Value(value)
+    }
+}
+
+impl<T> From<PropertyBindingKey<T>> for PropertyBinding<T> {
+    fn from(key: PropertyBindingKey<T>) -> PropertyBinding<T> {
+        PropertyBinding::Binding(key)
+    }
+}
+
+/// The current value of an animated property. This is
+/// supplied by the calling code.
+#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
+pub struct PropertyValue<T> {
+    pub key: PropertyBindingKey<T>,
+    pub value: T,
+}
+
+/// When using `generate_frame()`, a list of `PropertyValue` structures
+/// can optionally be supplied to provide the current value of any
+/// animated properties.
+#[derive(Clone, Deserialize, Serialize, Debug)]
+pub struct DynamicProperties {
+    pub transforms: Vec<PropertyValue<LayoutTransform>>,
+    pub floats: Vec<PropertyValue<f32>>,
+}
+
+pub type VRCompositorId = u64;
+
+// WebVR commands that must be called in the WebGL render thread.
+#[derive(Clone, Deserialize, Serialize)]
+pub enum VRCompositorCommand {
+    Create(VRCompositorId),
+    SyncPoses(VRCompositorId, f64, f64, MsgSender<Result<Vec<u8>,()>>),
+    SubmitFrame(VRCompositorId, [f32; 4], [f32; 4]),
+    Release(VRCompositorId)
+}
+
+// Trait object that handles WebVR commands.
+// Receives the texture id and size associated to the WebGLContext.
+pub trait VRCompositorHandler: Send {
+    fn handle(&mut self, command: VRCompositorCommand, texture: Option<(u32, DeviceIntSize)>);
+}
+
+pub trait RenderNotifier: Send {
+    fn new_frame_ready(&mut self);
+    fn new_scroll_frame_ready(&mut self, composite_needed: bool);
+    fn external_event(&mut self, _evt: ExternalEvent) { unimplemented!() }
+    fn shut_down(&mut self) {}
+}
+
+/// Trait to allow dispatching functions to a specific thread or event loop.
+pub trait RenderDispatcher: Send {
+    fn dispatch(&self, Box<Fn() + Send>);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_api/src/channel.rs
@@ -0,0 +1,78 @@
+/* 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 byteorder::{LittleEndian, WriteBytesExt, ReadBytesExt};
+use std::io::{Cursor, Read};
+use api::{Epoch, PipelineId};
+use std::mem;
+
+#[derive(Clone)]
+pub struct Payload {
+    /// An epoch used to get the proper payload for a pipeline id frame request.
+    ///
+    /// TODO(emilio): Is this still relevant? We send the messages for the same
+    /// pipeline in order, so we shouldn't need it. Seems like this was only
+    /// wallpapering (in most cases) the underlying problem in #991.
+    pub epoch: Epoch,
+    /// A pipeline id to key the payload with, along with the epoch.
+    pub pipeline_id: PipelineId,
+    pub display_list_data: Vec<u8>,
+}
+
+impl Payload {
+    /// Convert the payload to a raw byte vector, in order for it to be
+    /// efficiently shared via shmem, for example.
+    ///
+    /// TODO(emilio, #1049): Consider moving the IPC boundary to the
+    /// constellation in Servo and remove this complexity from WR.
+    pub fn to_data(&self) -> Vec<u8> {
+        let mut data = Vec::with_capacity(mem::size_of::<u32>() +
+                                          2 * mem::size_of::<u32>() +
+                                          mem::size_of::<u64>() +
+                                          self.display_list_data.len());
+        data.write_u32::<LittleEndian>(self.epoch.0).unwrap();
+        data.write_u32::<LittleEndian>(self.pipeline_id.0).unwrap();
+        data.write_u32::<LittleEndian>(self.pipeline_id.1).unwrap();
+        data.write_u64::<LittleEndian>(self.display_list_data.len() as u64).unwrap();
+        data.extend_from_slice(&self.display_list_data);
+        data
+    }
+
+    /// Deserializes the given payload from a raw byte vector.
+    pub fn from_data(data: &[u8]) -> Payload {
+        let mut payload_reader = Cursor::new(data);
+        let epoch = Epoch(payload_reader.read_u32::<LittleEndian>().unwrap());
+        let pipeline_id = PipelineId(payload_reader.read_u32::<LittleEndian>().unwrap(),
+                                     payload_reader.read_u32::<LittleEndian>().unwrap());
+
+        let dl_size = payload_reader.read_u64::<LittleEndian>().unwrap() as usize;
+        let mut built_display_list_data = vec![0; dl_size];
+        payload_reader.read_exact(&mut built_display_list_data[..]).unwrap();
+
+        assert_eq!(payload_reader.position(), data.len() as u64);
+
+        Payload {
+            epoch,
+            pipeline_id,
+            display_list_data: built_display_list_data,
+        }
+    }
+}
+
+
+/// A helper to handle the interface difference between `IpcBytesSender`
+/// and `Sender<Vec<u8>>`.
+pub trait PayloadSenderHelperMethods {
+    fn send_payload(&self, data: Payload) -> Result<(), Error>;
+}
+
+pub trait PayloadReceiverHelperMethods {
+    fn recv_payload(&self) -> Result<Payload, Error>;
+}
+
+#[cfg(not(feature = "ipc"))]
+include!("channel_mpsc.rs");
+
+#[cfg(feature = "ipc")]
+include!("channel_ipc.rs");
rename from gfx/webrender_traits/src/channel_ipc.rs
rename to gfx/webrender_api/src/channel_ipc.rs
rename from gfx/webrender_traits/src/channel_mpsc.rs
rename to gfx/webrender_api/src/channel_mpsc.rs
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_api/src/color.rs
@@ -0,0 +1,74 @@
+/* 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/. */
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct ColorF {
+    pub r: f32,
+    pub g: f32,
+    pub b: f32,
+    pub a: f32,
+}
+known_heap_size!(0, ColorF);
+
+impl ColorF {
+    pub fn premultiplied(&self) -> ColorF {
+        ColorF {
+            r: self.r * self.a,
+            g: self.g * self.a,
+            b: self.b * self.a,
+            a: self.a,
+        }
+    }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Hash, Eq, Debug, Deserialize, PartialEq, PartialOrd, Ord, Serialize)]
+pub struct ColorU {
+    pub r: u8,
+    pub g: u8,
+    pub b: u8,
+    pub a: u8,
+}
+
+impl From<ColorF> for ColorU {
+    fn from(color: ColorF) -> ColorU {
+        ColorU {
+            r: ColorU::round_to_int(color.r),
+            g: ColorU::round_to_int(color.g),
+            b: ColorU::round_to_int(color.b),
+            a: ColorU::round_to_int(color.a),
+        }
+    }
+}
+
+impl Into<ColorF> for ColorU {
+    fn into(self) -> ColorF {
+        ColorF {
+            r: self.r as f32 / 255.0,
+            g: self.g as f32 / 255.0,
+            b: self.b as f32 / 255.0,
+            a: self.a as f32 / 255.0,
+        }
+    }
+}
+
+impl ColorU {
+    fn round_to_int(x: f32) -> u8 {
+        debug_assert!((0.0 <= x) && (x <= 1.0));
+        let f = (255.0 * x) + 0.5;
+        let val = f.floor();
+        debug_assert!(val <= 255.0);
+        val as u8
+    }
+
+    pub fn new(r: u8, g: u8, b: u8, a: u8) -> ColorU {
+        ColorU {
+            r,
+            g,
+            b,
+            a,
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_api/src/display_item.rs
@@ -0,0 +1,639 @@
+/* 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::SideOffsets2D;
+use {ColorF, FontKey, ImageKey, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform};
+use {LayoutVector2D, PipelineId, PropertyBinding, WebGLContextId};
+
+// NOTE: some of these structs have an "IMPLICIT" comment.
+// This indicates that the BuiltDisplayList will have serialized
+// a list of values nearby that this item consumes. The traversal
+// iterator should handle finding these.
+
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub struct ClipAndScrollInfo {
+    pub scroll_node_id: ClipId,
+    pub clip_node_id: Option<ClipId>,
+}
+
+impl ClipAndScrollInfo {
+    pub fn simple(node_id: ClipId) -> ClipAndScrollInfo {
+        ClipAndScrollInfo {
+            scroll_node_id: node_id,
+            clip_node_id: None,
+        }
+    }
+
+    pub fn new(scroll_node_id: ClipId, clip_node_id: ClipId) -> ClipAndScrollInfo {
+        ClipAndScrollInfo {
+            scroll_node_id,
+            clip_node_id: Some(clip_node_id),
+        }
+    }
+
+    pub fn clip_node_id(&self) -> ClipId {
+        self.clip_node_id.unwrap_or(self.scroll_node_id)
+    }
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct DisplayItem {
+    pub item: SpecificDisplayItem,
+    pub rect: LayoutRect,
+    pub local_clip: LocalClip,
+    pub clip_and_scroll: ClipAndScrollInfo,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub enum SpecificDisplayItem {
+    Clip(ClipDisplayItem),
+    ScrollFrame(ClipDisplayItem),
+    Rectangle(RectangleDisplayItem),
+    Text(TextDisplayItem),
+    Image(ImageDisplayItem),
+    YuvImage(YuvImageDisplayItem),
+    WebGL(WebGLDisplayItem),
+    Border(BorderDisplayItem),
+    BoxShadow(BoxShadowDisplayItem),
+    Gradient(GradientDisplayItem),
+    RadialGradient(RadialGradientDisplayItem),
+    Iframe(IframeDisplayItem),
+    PushStackingContext(PushStackingContextDisplayItem),
+    PopStackingContext,
+    SetGradientStops,
+    PushNestedDisplayList,
+    PopNestedDisplayList,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct ClipDisplayItem {
+    pub id: ClipId,
+    pub parent_id: ClipId,
+    pub image_mask: Option<ImageMask>,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct RectangleDisplayItem {
+    pub color: ColorF,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct TextDisplayItem {
+    pub font_key: FontKey,
+    pub size: Au,
+    pub color: ColorF,
+    pub blur_radius: f32,
+    pub glyph_options: Option<GlyphOptions>,
+} // IMPLICIT: glyphs: Vec<GlyphInstance>
+
+#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
+pub struct GlyphOptions {
+    // These are currently only used on windows for dwrite fonts.
+    pub use_embedded_bitmap: bool,
+    pub force_gdi_rendering: bool,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct WebGLDisplayItem {
+    pub context_id: WebGLContextId,
+}
+
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct NormalBorder {
+    pub left: BorderSide,
+    pub right: BorderSide,
+    pub top: BorderSide,
+    pub bottom: BorderSide,
+    pub radius: BorderRadius,
+}
+
+#[repr(u32)]
+#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
+pub enum RepeatMode {
+    Stretch,
+    Repeat,
+    Round,
+    Space,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct NinePatchDescriptor {
+    pub width: u32,
+    pub height: u32,
+    pub slice: SideOffsets2D<u32>,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct ImageBorder {
+    pub image_key: ImageKey,
+    pub patch: NinePatchDescriptor,
+    /// Controls whether the center of the 9 patch image is
+    /// rendered or ignored.
+    pub fill: bool,
+    pub outset: SideOffsets2D<f32>,
+    pub repeat_horizontal: RepeatMode,
+    pub repeat_vertical: RepeatMode,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct GradientBorder {
+    pub gradient: Gradient,
+    pub outset: SideOffsets2D<f32>,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct RadialGradientBorder {
+    pub gradient: RadialGradient,
+    pub outset: SideOffsets2D<f32>,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub enum BorderDetails {
+    Normal(NormalBorder),
+    Image(ImageBorder),
+    Gradient(GradientBorder),
+    RadialGradient(RadialGradientBorder),
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct BorderDisplayItem {
+    pub widths: BorderWidths,
+    pub details: BorderDetails,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct BorderRadius {
+    pub top_left: LayoutSize,
+    pub top_right: LayoutSize,
+    pub bottom_left: LayoutSize,
+    pub bottom_right: LayoutSize,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct BorderWidths {
+    pub left: f32,
+    pub top: f32,
+    pub right: f32,
+    pub bottom: f32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct BorderSide {
+    pub color: ColorF,
+    pub style: BorderStyle,
+}
+
+#[repr(u32)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub enum BorderStyle {
+    None    = 0,
+    Solid   = 1,
+    Double  = 2,
+    Dotted  = 3,
+    Dashed  = 4,
+    Hidden  = 5,
+    Groove  = 6,
+    Ridge   = 7,
+    Inset   = 8,
+    Outset  = 9,
+}
+
+#[repr(u32)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub enum BoxShadowClipMode {
+    None    = 0,
+    Outset  = 1,
+    Inset   = 2,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct BoxShadowDisplayItem {
+    pub box_bounds: LayoutRect,
+    pub offset: LayoutVector2D,
+    pub color: ColorF,
+    pub blur_radius: f32,
+    pub spread_radius: f32,
+    pub border_radius: f32,
+    pub clip_mode: BoxShadowClipMode,
+}
+
+#[repr(u32)]
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Ord, PartialOrd)]
+pub enum ExtendMode {
+    Clamp,
+    Repeat,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct Gradient {
+    pub start_point: LayoutPoint,
+    pub end_point: LayoutPoint,
+    pub extend_mode: ExtendMode,
+} // IMPLICIT: stops: Vec<GradientStop>
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct GradientDisplayItem {
+    pub gradient: Gradient,
+    pub tile_size: LayoutSize,
+    pub tile_spacing: LayoutSize,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct GradientStop {
+    pub offset: f32,
+    pub color: ColorF,
+}
+known_heap_size!(0, GradientStop);
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct RadialGradient {
+    pub start_center: LayoutPoint,
+    pub start_radius: f32,
+    pub end_center: LayoutPoint,
+    pub end_radius: f32,
+    pub ratio_xy: f32,
+    pub extend_mode: ExtendMode,
+} // IMPLICIT stops: Vec<GradientStop>
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct RadialGradientDisplayItem {
+    pub gradient: RadialGradient,
+    pub tile_size: LayoutSize,
+    pub tile_spacing: LayoutSize,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct PushStackingContextDisplayItem {
+    pub stacking_context: StackingContext,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct StackingContext {
+    pub scroll_policy: ScrollPolicy,
+    pub transform: Option<PropertyBinding<LayoutTransform>>,
+    pub transform_style: TransformStyle,
+    pub perspective: Option<LayoutTransform>,
+    pub mix_blend_mode: MixBlendMode,
+} // IMPLICIT: filters: Vec<FilterOp>
+
+#[repr(u32)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
+pub enum ScrollPolicy {
+    Scrollable  = 0,
+    Fixed       = 1,
+}
+
+known_heap_size!(0, ScrollPolicy);
+
+#[repr(u32)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub enum TransformStyle {
+    Flat        = 0,
+    Preserve3D  = 1,
+}
+
+#[repr(u32)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub enum MixBlendMode {
+    Normal      = 0,
+    Multiply    = 1,
+    Screen      = 2,
+    Overlay     = 3,
+    Darken      = 4,
+    Lighten     = 5,
+    ColorDodge  = 6,
+    ColorBurn   = 7,
+    HardLight   = 8,
+    SoftLight   = 9,
+    Difference  = 10,
+    Exclusion   = 11,
+    Hue         = 12,
+    Saturation  = 13,
+    Color       = 14,
+    Luminosity  = 15,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
+pub enum FilterOp {
+    Blur(Au),
+    Brightness(f32),
+    Contrast(f32),
+    Grayscale(f32),
+    HueRotate(f32),
+    Invert(f32),
+    Opacity(PropertyBinding<f32>),
+    Saturate(f32),
+    Sepia(f32),
+}
+
+impl FilterOp {
+    pub fn is_noop(&self) -> bool {
+        match *self {
+            FilterOp::Blur(length) if length == Au(0) => true,
+            FilterOp::Brightness(amount) if amount == 1.0 => true,
+            FilterOp::Contrast(amount) if amount == 1.0 => true,
+            FilterOp::Grayscale(amount) if amount == 0.0 => true,
+            FilterOp::HueRotate(amount) if amount == 0.0 => true,
+            FilterOp::Invert(amount) if amount == 0.0 => true,
+            FilterOp::Opacity(amount) if amount == PropertyBinding::Value(1.0) => true,
+            FilterOp::Saturate(amount) if amount == 1.0 => true,
+            FilterOp::Sepia(amount) if amount == 0.0 => true,
+            _ => false,
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct IframeDisplayItem {
+    pub pipeline_id: PipelineId,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct ImageDisplayItem {
+    pub image_key: ImageKey,
+    pub stretch_size: LayoutSize,
+    pub tile_spacing: LayoutSize,
+    pub image_rendering: ImageRendering,
+}
+
+#[repr(u32)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub enum ImageRendering {
+    Auto        = 0,
+    CrispEdges  = 1,
+    Pixelated   = 2,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct YuvImageDisplayItem {
+    pub yuv_data: YuvData,
+    pub color_space: YuvColorSpace,
+    pub image_rendering: ImageRendering
+}
+
+#[repr(u32)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub enum YuvColorSpace {
+    Rec601 = 0,
+    Rec709 = 1,
+}
+pub const YUV_COLOR_SPACES: [YuvColorSpace; 2] = [YuvColorSpace::Rec601, YuvColorSpace::Rec709];
+
+impl YuvColorSpace {
+    pub fn get_feature_string(&self) -> &'static str {
+        match *self {
+            YuvColorSpace::Rec601 => "YUV_REC601",
+            YuvColorSpace::Rec709 => "YUV_REC709",
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub enum YuvData {
+    NV12(ImageKey, ImageKey),   // (Y channel, CbCr interleaved channel)
+    PlanarYCbCr(ImageKey, ImageKey, ImageKey),  // (Y channel, Cb channel, Cr Channel)
+    InterleavedYCbCr(ImageKey), // (YCbCr interleaved channel)
+}
+
+impl YuvData {
+    pub fn get_format(&self) -> YuvFormat {
+        match *self {
+            YuvData::NV12(..) => YuvFormat::NV12,
+            YuvData::PlanarYCbCr(..) => YuvFormat::PlanarYCbCr,
+            YuvData::InterleavedYCbCr(..) => YuvFormat::InterleavedYCbCr,
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub enum YuvFormat {
+    NV12 = 0,
+    PlanarYCbCr = 1,
+    InterleavedYCbCr = 2,
+}
+pub const YUV_FORMATS: [YuvFormat; 3] = [YuvFormat::NV12, YuvFormat::PlanarYCbCr, YuvFormat::InterleavedYCbCr];
+
+impl YuvFormat {
+    pub fn get_plane_num(&self) -> usize {
+        match *self {
+            YuvFormat::NV12 => 2,
+            YuvFormat::PlanarYCbCr => 3,
+            YuvFormat::InterleavedYCbCr => 1,
+        }
+    }
+
+    pub fn get_feature_string(&self) -> &'static str {
+        match *self {
+            YuvFormat::NV12 => "NV12",
+            YuvFormat::PlanarYCbCr => "",
+            YuvFormat::InterleavedYCbCr => "INTERLEAVED_Y_CB_CR"
+        }
+    }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct ImageMask {
+    pub image: ImageKey,
+    pub rect: LayoutRect,
+    pub repeat: bool,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub enum LocalClip {
+    Rect(LayoutRect),
+    RoundedRect(LayoutRect, ComplexClipRegion),
+}
+
+impl From<LayoutRect> for LocalClip {
+    fn from(rect: LayoutRect) -> Self {
+        LocalClip::Rect(rect)
+    }
+}
+
+impl LocalClip {
+    pub fn clip_rect(&self) -> &LayoutRect {
+        match *self {
+            LocalClip::Rect(ref rect) => rect,
+            LocalClip::RoundedRect(ref rect, _) => &rect,
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct ComplexClipRegion {
+    /// The boundaries of the rectangle.
+    pub rect: LayoutRect,
+    /// Border radii of this rectangle.
+    pub radii: BorderRadius,
+}
+
+impl BorderRadius {
+    pub fn zero() -> BorderRadius {
+        BorderRadius {
+            top_left: LayoutSize::new(0.0, 0.0),
+            top_right: LayoutSize::new(0.0, 0.0),
+            bottom_left: LayoutSize::new(0.0, 0.0),
+            bottom_right: LayoutSize::new(0.0, 0.0),
+        }
+    }
+
+    pub fn uniform(radius: f32) -> BorderRadius {
+        BorderRadius {
+            top_left: LayoutSize::new(radius, radius),
+            top_right: LayoutSize::new(radius, radius),
+            bottom_left: LayoutSize::new(radius, radius),
+            bottom_right: LayoutSize::new(radius, radius),
+        }
+    }
+
+    pub fn uniform_size(radius: LayoutSize) -> BorderRadius {
+        BorderRadius {
+            top_left: radius,
+            top_right: radius,
+            bottom_left: radius,
+            bottom_right: radius,
+        }
+    }
+
+    pub fn is_uniform(&self) -> Option<f32> {
+        match self.is_uniform_size() {
+            Some(radius) if radius.width == radius.height => Some(radius.width),
+            _ => None
+        }
+    }
+
+    pub fn is_uniform_size(&self) -> Option<LayoutSize> {
+        let uniform_radius = self.top_left;
+        if self.top_right == uniform_radius &&
+           self.bottom_left == uniform_radius &&
+           self.bottom_right == uniform_radius {
+            Some(uniform_radius)
+        } else {
+            None
+        }
+    }
+
+    pub fn is_zero(&self) -> bool {
+        if let Some(radius) = self.is_uniform() {
+            radius == 0.0
+        } else {
+            false
+        }
+    }
+}
+
+impl ColorF {
+    pub fn new(r: f32, g: f32, b: f32, a: f32) -> ColorF {
+        ColorF {
+            r,
+            g,
+            b,
+            a,
+        }
+    }
+
+    pub fn scale_rgb(&self, scale: f32) -> ColorF {
+        ColorF {
+            r: self.r * scale,
+            g: self.g * scale,
+            b: self.b * scale,
+            a: self.a,
+        }
+    }
+
+    pub fn to_array(&self) -> [f32; 4] {
+        [self.r, self.g, self.b, self.a]
+    }
+}
+
+impl ComplexClipRegion {
+    /// Create a new complex clip region.
+    pub fn new(rect: LayoutRect, radii: BorderRadius) -> ComplexClipRegion {
+        ComplexClipRegion {
+            rect,
+            radii,
+        }
+    }
+}
+
+pub type NestingIndex = u64;
+
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub enum ClipId {
+    Clip(u64, NestingIndex, PipelineId),
+    ClipExternalId(u64, PipelineId),
+    DynamicallyAddedNode(u64, PipelineId),
+}
+
+impl ClipId {
+    pub fn root_scroll_node(pipeline_id: PipelineId) -> ClipId {
+        ClipId::Clip(0, 0, pipeline_id)
+    }
+
+    pub fn root_reference_frame(pipeline_id: PipelineId) -> ClipId {
+        ClipId::DynamicallyAddedNode(0, pipeline_id)
+    }
+
+    pub fn new(id: u64, pipeline_id: PipelineId) -> ClipId {
+        // We do this because it is very easy to create accidentally create something that
+        // seems like a root scroll node, but isn't one.
+        if id == 0 {
+            return ClipId::root_scroll_node(pipeline_id);
+        }
+
+        ClipId::ClipExternalId(id, pipeline_id)
+    }
+
+    pub fn pipeline_id(&self) -> PipelineId {
+        match *self {
+            ClipId::Clip(_, _, pipeline_id) |
+            ClipId::ClipExternalId(_, pipeline_id) |
+            ClipId::DynamicallyAddedNode(_, pipeline_id) => pipeline_id,
+        }
+    }
+
+    pub fn external_id(&self) -> Option<u64> {
+        match *self {
+            ClipId::ClipExternalId(id, _) => Some(id),
+            _ => None,
+        }
+    }
+
+    pub fn is_root_scroll_node(&self) -> bool {
+        match *self {
+            ClipId::Clip(0, 0, _)  => true,
+            _ => false,
+        }
+    }
+
+    pub fn is_nested(&self) -> bool {
+        match *self {
+            ClipId::Clip(_, nesting_level, _) => nesting_level != 0,
+            _ => false,
+        }
+    }
+}
+
+macro_rules! define_empty_heap_size_of {
+    ($name:ident) => {
+        impl ::heapsize::HeapSizeOf for $name {
+            fn heap_size_of_children(&self) -> usize { 0 }
+        }
+    }
+}
+
+define_empty_heap_size_of!(ClipId);
+define_empty_heap_size_of!(RepeatMode);
+define_empty_heap_size_of!(ImageKey);
+define_empty_heap_size_of!(MixBlendMode);
+define_empty_heap_size_of!(TransformStyle);
+define_empty_heap_size_of!(LocalClip);
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_api/src/display_list.rs
@@ -0,0 +1,930 @@
+/* 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 bincode;
+use serde::{Deserialize, Serialize, Serializer};
+use serde::ser::{SerializeSeq, SerializeMap};
+use time::precise_time_ns;
+use {BorderDetails, BorderDisplayItem, BorderWidths, BoxShadowClipMode, BoxShadowDisplayItem};
+use {ClipAndScrollInfo, ClipDisplayItem, ClipId, ColorF, ComplexClipRegion, DisplayItem};
+use {ExtendMode, FilterOp, FontKey, GlyphInstance, GlyphOptions, Gradient, GradientDisplayItem};
+use {GradientStop, IframeDisplayItem, ImageDisplayItem, ImageKey, ImageMask, ImageRendering};
+use {LayoutPoint, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D, LocalClip};
+use {MixBlendMode, PipelineId, PropertyBinding, PushStackingContextDisplayItem, RadialGradient};
+use {RadialGradientDisplayItem, RectangleDisplayItem, ScrollPolicy, SpecificDisplayItem};
+use {StackingContext, TextDisplayItem, TransformStyle, WebGLContextId, WebGLDisplayItem};
+use {YuvColorSpace, YuvData, YuvImageDisplayItem};
+use std::marker::PhantomData;
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub struct ItemRange<T> {
+    start: usize,
+    length: usize,
+    _boo: PhantomData<T>,
+}
+
+impl<T> Default for ItemRange<T> {
+    fn default() -> Self {
+        ItemRange { start: 0, length: 0, _boo: PhantomData }
+    }
+}
+
+impl<T> ItemRange<T> {
+    pub fn is_empty(&self) -> bool {
+        // Nothing more than space for a length (0).
+        self.length <= ::std::mem::size_of::<u64>()
+    }
+}
+
+/// A display list.
+#[derive(Clone, Default)]
+pub struct BuiltDisplayList {
+    /// Serde encoded bytes. Mostly DisplayItems, but some mixed in slices.
+    data: Vec<u8>,
+    descriptor: BuiltDisplayListDescriptor,
+}
+
+/// Describes the memory layout of a display list.
+///
+/// A display list consists of some number of display list items, followed by a number of display
+/// items.
+#[repr(C)]
+#[derive(Copy, Clone, Default, Deserialize, Serialize)]
+pub struct BuiltDisplayListDescriptor {
+    /// The first IPC time stamp: before any work has been done
+    builder_start_time: u64,
+    /// The second IPC time stamp: after serialization
+    builder_finish_time: u64,
+}
+
+pub struct BuiltDisplayListIter<'a> {
+    list: &'a BuiltDisplayList,
+    data: &'a [u8],
+    cur_item: DisplayItem,
+    cur_stops: ItemRange<GradientStop>,
+    cur_glyphs: ItemRange<GlyphInstance>,
+    cur_filters: ItemRange<FilterOp>,
+    cur_complex_clip: (ItemRange<ComplexClipRegion>, usize),
+    peeking: Peek,
+}
+
+pub struct DisplayItemRef<'a: 'b, 'b> {
+    iter: &'b BuiltDisplayListIter<'a>,
+}
+
+#[derive(PartialEq)]
+enum Peek {
+    StartPeeking,
+    IsPeeking,
+    NotPeeking,
+}
+
+#[derive(Clone)]
+pub struct AuxIter<'a, T> {
+    data: &'a [u8],
+    size: usize,
+    _boo: PhantomData<T>,
+}
+
+impl BuiltDisplayListDescriptor {
+}
+
+impl BuiltDisplayList {
+    pub fn from_data(data: Vec<u8>, descriptor: BuiltDisplayListDescriptor) -> BuiltDisplayList {
+        BuiltDisplayList {
+            data,
+            descriptor,
+        }
+    }
+
+    pub fn into_data(self) -> (Vec<u8>, BuiltDisplayListDescriptor) {
+        (self.data, self.descriptor)
+    }
+
+    pub fn data(&self) -> &[u8] {
+        &self.data[..]
+    }
+
+    pub fn descriptor(&self) -> &BuiltDisplayListDescriptor {
+        &self.descriptor
+    }
+
+    pub fn times(&self) -> (u64, u64) {
+      (self.descriptor.builder_start_time, self.descriptor.builder_finish_time)
+    }
+
+    pub fn iter(&self) -> BuiltDisplayListIter {
+        BuiltDisplayListIter::new(self)
+    }
+
+    pub fn get<'de, T: Deserialize<'de>>(&self, range: ItemRange<T>) -> AuxIter<T> {
+        AuxIter::new(&self.data[range.start .. range.start + range.length])
+    }
+}
+
+impl<'a> BuiltDisplayListIter<'a> {
+    pub fn new(list: &'a BuiltDisplayList) -> Self {
+        Self::new_with_list_and_data(list, &list.data)
+    }
+
+    pub fn new_with_list_and_data(list: &'a BuiltDisplayList, data: &'a [u8]) -> Self {
+        BuiltDisplayListIter {
+            list,
+            data: &data,
+            cur_item: DisplayItem { // Dummy data, will be overwritten by `next`
+                item: SpecificDisplayItem::PopStackingContext,
+                rect: LayoutRect::zero(),
+                local_clip: LocalClip::from(LayoutRect::zero()),
+                clip_and_scroll: ClipAndScrollInfo::simple(ClipId::new(0, PipelineId(0, 0))),
+            },
+            cur_stops: ItemRange::default(),
+            cur_glyphs: ItemRange::default(),
+            cur_filters: ItemRange::default(),
+            cur_complex_clip: (ItemRange::default(), 0),
+            peeking: Peek::NotPeeking,
+        }
+    }
+
+    pub fn display_list(&self) -> &'a BuiltDisplayList {
+        self.list
+    }
+
+    pub fn next<'b>(&'b mut self) -> Option<DisplayItemRef<'a, 'b>> {
+        use SpecificDisplayItem::*;
+
+        match self.peeking {
+            Peek::IsPeeking => {
+                self.peeking = Peek::NotPeeking;
+                return Some(self.as_ref())
+            }
+            Peek::StartPeeking => {
+                self.peeking = Peek::IsPeeking;
+            }
+            Peek::NotPeeking => { /* do nothing */ }
+        }
+
+        // Don't let these bleed into another item
+        self.cur_stops = ItemRange::default();
+        self.cur_complex_clip = (ItemRange::default(), 0);
+
+        loop {
+            if self.data.len() == 0 {
+                return None
+            }
+
+            self.cur_item = bincode::deserialize_from(&mut self.data, bincode::Infinite)
+                                    .expect("MEH: malicious process?");
+
+            match self.cur_item.item {
+                SetGradientStops => {
+                    self.cur_stops = self.skip_slice::<GradientStop>().0;
+
+                    // This is a dummy item, skip over it
+                    continue;
+                }
+                Clip(_) | ScrollFrame(_) =>
+                    self.cur_complex_clip = self.skip_slice::<ComplexClipRegion>(),
+                Text(_) => self.cur_glyphs = self.skip_slice::<GlyphInstance>().0,
+                PushStackingContext(_) => self.cur_filters = self.skip_slice::<FilterOp>().0,
+                _ => { /* do nothing */ }
+            }
+
+            break;
+        }
+
+        Some(self.as_ref())
+    }
+
+    /// Returns the byte-range the slice occupied, and the number of elements
+    /// in the slice.
+    fn skip_slice<T: for<'de> Deserialize<'de>>(&mut self) -> (ItemRange<T>, usize) {
+        let base = self.list.data.as_ptr() as usize;
+        let start = self.data.as_ptr() as usize;
+
+        // Read through the values (this is a bit of a hack to reuse logic)
+        let mut iter = AuxIter::<T>::new(self.data);
+        let count = iter.len();
+        for _ in &mut iter {}
+        let end = iter.data.as_ptr() as usize;
+
+        let range = ItemRange { start: start - base, length: end - start, _boo: PhantomData };
+
+        // Adjust data pointer to skip read values
+        self.data = &self.data[range.length..];
+        (range, count)
+    }
+
+    pub fn as_ref<'b>(&'b self) -> DisplayItemRef<'a, 'b> {
+        DisplayItemRef { iter: self }
+    }
+
+    pub fn starting_stacking_context(&mut self)
+        -> Option<(StackingContext, LayoutRect, ItemRange<FilterOp>)> {
+
+        self.next().and_then(|item| match *item.item() {
+            SpecificDisplayItem::PushStackingContext(ref specific_item) => {
+                Some((specific_item.stacking_context, item.rect(), item.filters()))
+            },
+            _ => None,
+        })
+    }
+
+    pub fn skip_current_stacking_context(&mut self) {
+        let mut depth = 0;
+        while let Some(item) = self.next() {
+            match *item.item() {
+                SpecificDisplayItem::PushStackingContext(..) => depth += 1,
+                SpecificDisplayItem::PopStackingContext if depth == 0 => return,
+                SpecificDisplayItem::PopStackingContext => depth -= 1,
+                _ => {}
+            }
+            debug_assert!(depth >= 0);
+        }
+    }
+
+    pub fn current_stacking_context_empty(&mut self) -> bool {
+        match self.peek() {
+            Some(item) => *item.item() == SpecificDisplayItem::PopStackingContext,
+            None => true,
+        }
+    }
+
+    pub fn peek<'b>(&'b mut self) -> Option<DisplayItemRef<'a, 'b>> {
+        if self.peeking == Peek::NotPeeking {
+            self.peeking = Peek::StartPeeking;
+            self.next()
+        } else {
+            Some(self.as_ref())
+        }
+    }
+}
+
+// Some of these might just become ItemRanges
+impl<'a, 'b> DisplayItemRef<'a, 'b> {
+    pub fn display_item(&self) -> &DisplayItem {
+        &self.iter.cur_item
+    }
+
+    pub fn rect(&self) -> LayoutRect {
+        self.iter.cur_item.rect
+    }
+
+    pub fn local_clip(&self) -> &LocalClip {
+        &self.iter.cur_item.local_clip
+    }
+
+    pub fn clip_and_scroll(&self) -> ClipAndScrollInfo {
+        self.iter.cur_item.clip_and_scroll
+    }
+
+    pub fn item(&self) -> &SpecificDisplayItem {
+        &self.iter.cur_item.item
+    }
+
+    pub fn complex_clip(&self) -> &(ItemRange<ComplexClipRegion>, usize) {
+        &self.iter.cur_complex_clip
+    }
+
+    pub fn gradient_stops(&self) -> ItemRange<GradientStop> {
+        self.iter.cur_stops
+    }
+
+    pub fn glyphs(&self) -> ItemRange<GlyphInstance> {
+        self.iter.cur_glyphs
+    }
+
+    pub fn filters(&self) -> ItemRange<FilterOp> {
+        self.iter.cur_filters
+    }
+
+    pub fn display_list(&self) -> &BuiltDisplayList {
+        self.iter.display_list()
+    }
+
+    // Creates a new iterator where this element's iterator is, to hack around borrowck.
+    pub fn sub_iter(&self) -> BuiltDisplayListIter<'a> {
+        BuiltDisplayListIter::new_with_list_and_data(self.iter.list, self.iter.data)
+    }
+}
+
+impl<'de, 'a, T: Deserialize<'de>> AuxIter<'a, T> {
+    pub fn new(mut data: &'a [u8]) -> Self {
+
+        let size: usize = if data.len() == 0 {
+            0   // Accept empty ItemRanges pointing anywhere
+        } else {
+            bincode::deserialize_from(&mut data, bincode::Infinite)
+                                  .expect("MEH: malicious input?")
+        };
+
+        AuxIter {
+            data,
+            size,
+            _boo: PhantomData,
+        }
+    }
+}
+
+impl<'a, T: for<'de> Deserialize<'de>> Iterator for AuxIter<'a, T> {
+    type Item = T;
+
+    fn next(&mut self) -> Option<T> {
+        if self.size == 0 {
+            None
+        } else {
+            self.size -= 1;
+            Some(bincode::deserialize_from(&mut self.data, bincode::Infinite)
+                         .expect("MEH: malicious input?"))
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.size, Some(self.size))
+    }
+}
+
+impl<'a, T: for<'de> Deserialize<'de>> ::std::iter::ExactSizeIterator for AuxIter<'a, T> { }
+
+
+// This is purely for the JSON writer in wrench
+impl Serialize for BuiltDisplayList {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut seq = serializer.serialize_seq(None)?;
+        let mut traversal = self.iter();
+        while let Some(item) = traversal.next() {
+            seq.serialize_element(&item)?
+        }
+        seq.end()
+    }
+}
+
+impl<'a, 'b> Serialize for DisplayItemRef<'a, 'b> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(None)?;
+
+        map.serialize_entry("item", self.display_item())?;
+
+        match *self.item() {
+            SpecificDisplayItem::Text(_) => {
+                map.serialize_entry("glyphs",
+                    &self.iter.list.get(self.glyphs()).collect::<Vec<_>>())?;
+            }
+            SpecificDisplayItem::PushStackingContext(_) => {
+                map.serialize_entry("filters",
+                    &self.iter.list.get(self.filters()).collect::<Vec<_>>())?;
+            }
+            _ => { }
+        }
+
+        let &(complex_clips, number_of_complex_clips) = self.complex_clip();
+        let gradient_stops = self.gradient_stops();
+
+        if number_of_complex_clips > 0 {
+            map.serialize_entry("complex_clips",
+                &self.iter.list.get(complex_clips).collect::<Vec<_>>())?;
+        }
+
+        if !gradient_stops.is_empty() {
+            map.serialize_entry("gradient_stops",
+                &self.iter.list.get(gradient_stops).collect::<Vec<_>>())?;
+        }
+
+        map.end()
+    }
+}
+
+#[derive(Clone)]
+pub struct DisplayListBuilder {
+    pub data: Vec<u8>,
+    pub pipeline_id: PipelineId,
+    clip_stack: Vec<ClipAndScrollInfo>,
+    next_clip_id: u64,
+    builder_start_time: u64,
+
+    /// The size of the content of this display list. This is used to allow scrolling
+    /// outside the bounds of the display list items themselves.
+    content_size: LayoutSize,
+}
+
+impl DisplayListBuilder {
+    pub fn new(pipeline_id: PipelineId, content_size: LayoutSize) -> DisplayListBuilder {
+        Self::with_capacity(pipeline_id, content_size, 0)
+    }
+
+    pub fn with_capacity(pipeline_id: PipelineId,
+                         content_size: LayoutSize,
+                         capacity: usize) -> DisplayListBuilder {
+        let start_time = precise_time_ns();
+
+        // We start at 1 here, because the root scroll id is always 0.
+        const FIRST_CLIP_ID : u64 = 1;
+
+        DisplayListBuilder {
+            data: Vec::with_capacity(capacity),
+            pipeline_id,
+            clip_stack: vec![ClipAndScrollInfo::simple(ClipId::root_scroll_node(pipeline_id))],
+            next_clip_id: FIRST_CLIP_ID,
+            builder_start_time: start_time,
+            content_size,
+        }
+    }
+
+    pub fn print_display_list(&mut self) {
+        let mut temp = BuiltDisplayList::default();
+        ::std::mem::swap(&mut temp.data, &mut self.data);
+
+        {
+            let mut iter = BuiltDisplayListIter::new(&temp);
+            while let Some(item) = iter.next() {
+                println!("{:?}", item.display_item());
+            }
+        }
+
+        self.data = temp.data;
+    }
+
+    fn push_item(&mut self,
+                 item: SpecificDisplayItem,
+                 rect: LayoutRect,
+                 local_clip: Option<LocalClip>) {
+        let local_clip = local_clip.unwrap_or_else(|| LocalClip::from(rect));
+        bincode::serialize_into(&mut self.data, &DisplayItem {
+            item,
+            rect,
+            local_clip: local_clip,
+            clip_and_scroll: *self.clip_stack.last().unwrap(),
+        }, bincode::Infinite).unwrap();
+    }
+
+    fn push_new_empty_item(&mut self, item: SpecificDisplayItem) {
+        bincode::serialize_into(&mut self.data, &DisplayItem {
+            item,
+            rect: LayoutRect::zero(),
+            local_clip: LocalClip::from(LayoutRect::zero()),
+            clip_and_scroll: *self.clip_stack.last().unwrap(),
+        }, bincode::Infinite).unwrap();
+    }
+
+    fn push_iter<I>(&mut self, iter: I)
+    where I: IntoIterator,
+          I::IntoIter: ExactSizeIterator,
+          I::Item: Serialize,
+    {
+        let iter = iter.into_iter();
+        let len = iter.len();
+        let mut count = 0;
+
+        bincode::serialize_into(&mut self.data, &len, bincode::Infinite).unwrap();
+        for elem in iter {
+            count += 1;
+            bincode::serialize_into(&mut self.data, &elem, bincode::Infinite).unwrap();
+        }
+
+        debug_assert_eq!(len, count);
+    }
+
+    pub fn push_rect(&mut self, rect: LayoutRect, local_clip: Option<LocalClip>, color: ColorF) {
+        let item = SpecificDisplayItem::Rectangle(RectangleDisplayItem {
+            color,
+        });
+
+        self.push_item(item, rect, local_clip);
+    }
+
+    pub fn push_image(&mut self,
+                      rect: LayoutRect,
+                      local_clip: Option<LocalClip>,
+                      stretch_size: LayoutSize,
+                      tile_spacing: LayoutSize,
+                      image_rendering: ImageRendering,
+                      key: ImageKey) {
+        let item = SpecificDisplayItem::Image(ImageDisplayItem {
+            image_key: key,
+            stretch_size,
+            tile_spacing,
+            image_rendering,
+        });
+
+        self.push_item(item, rect, local_clip);
+    }
+
+    /// Push a yuv image. All planar data in yuv image should use the same buffer type.
+    pub fn push_yuv_image(&mut self,
+                          rect: LayoutRect,
+                          local_clip: Option<LocalClip>,
+                          yuv_data: YuvData,
+                          color_space: YuvColorSpace,
+                          image_rendering: ImageRendering) {
+        let item = SpecificDisplayItem::YuvImage(YuvImageDisplayItem {
+            yuv_data,
+            color_space,
+            image_rendering,
+        });
+        self.push_item(item, rect, local_clip);
+    }
+
+    pub fn push_webgl_canvas(&mut self,
+                             rect: LayoutRect,
+                             local_clip: Option<LocalClip>,
+                             context_id: WebGLContextId) {
+        let item = SpecificDisplayItem::WebGL(WebGLDisplayItem {
+            context_id,
+        });
+        self.push_item(item, rect, local_clip);
+    }
+
+    pub fn push_text(&mut self,
+                     rect: LayoutRect,
+                     local_clip: Option<LocalClip>,
+                     glyphs: &[GlyphInstance],
+                     font_key: FontKey,
+                     color: ColorF,
+                     size: Au,
+                     blur_radius: f32,
+                     glyph_options: Option<GlyphOptions>) {
+        // Sanity check - anything with glyphs bigger than this
+        // is probably going to consume too much memory to render
+        // efficiently anyway. This is specifically to work around
+        // the font_advance.html reftest, which creates a very large
+        // font as a crash test - the rendering is also ignored
+        // by the azure renderer.
+        if size < Au::from_px(4096) {
+            let item = SpecificDisplayItem::Text(TextDisplayItem {
+                color,
+                font_key,
+                size,
+                blur_radius,
+                glyph_options,
+            });
+
+            self.push_item(item, rect, local_clip);
+            self.push_iter(glyphs);
+        }
+    }
+
+    // Gradients can be defined with stops outside the range of [0, 1]
+    // when this happens the gradient needs to be normalized by adjusting
+    // the gradient stops and gradient line into an equivalent gradient
+    // with stops in the range [0, 1]. this is done by moving the beginning
+    // of the gradient line to where stop[0] and the end of the gradient line
+    // to stop[n-1]. this function adjusts the stops in place, and returns
+    // the amount to adjust the gradient line start and stop
+    fn normalize_stops(stops: &mut Vec<GradientStop>, extend_mode: ExtendMode) -> (f32, f32) {
+        assert!(stops.len() >= 2);
+
+        let first = *stops.first().unwrap();
+        let last = *stops.last().unwrap();
+
+        assert!(first.offset <= last.offset);
+
+        let stops_origin = first.offset;
+        let stops_delta = last.offset - first.offset;
+
+        if stops_delta > 0.000001 {
+            for stop in stops {
+                stop.offset = (stop.offset - stops_origin) / stops_delta;
+            }
+
+            (first.offset, last.offset)
+        } else {
+            // We have a degenerate gradient and can't accurately transform the stops
+            // what happens here depends on the repeat behavior, but in any case
+            // we reconstruct the gradient stops to something simpler and equivalent
+            stops.clear();
+
+            match extend_mode {
+                ExtendMode::Clamp => {
+                    // This gradient is two colors split at the offset of the stops,
+                    // so create a gradient with two colors split at 0.5 and adjust
+                    // the gradient line so 0.5 is at the offset of the stops
+                    stops.push(GradientStop {
+                        color: first.color,
+                        offset: 0.0,
+                    });
+                    stops.push(GradientStop {
+                        color: first.color,
+                        offset: 0.5,
+                    });
+                    stops.push(GradientStop {
+                        color: last.color,
+                        offset: 0.5,
+                    });
+                    stops.push(GradientStop {
+                        color: last.color,
+                        offset: 1.0,
+                    });
+
+                    let offset = last.offset;
+
+                    (offset - 0.5, offset + 0.5)
+                }
+                ExtendMode::Repeat => {
+                    // A repeating gradient with stops that are all in the same
+                    // position should just display the last color. I believe the
+                    // spec says that it should be the average color of the gradient,
+                    // but this matches what Gecko and Blink does
+                    stops.push(GradientStop {
+                        color: last.color,
+                        offset: 0.0,
+                    });
+                    stops.push(GradientStop {
+                        color: last.color,
+                        offset: 1.0,
+                    });
+
+                    (0.0, 1.0)
+                }
+            }
+        }
+    }
+
+    // NOTE: gradients must be pushed in the order they're created
+    // because create_gradient stores the stops in anticipation
+    pub fn create_gradient(&mut self,
+                           start_point: LayoutPoint,
+                           end_point: LayoutPoint,
+                           mut stops: Vec<GradientStop>,
+                           extend_mode: ExtendMode) -> Gradient {
+        let (start_offset,
+             end_offset) = DisplayListBuilder::normalize_stops(&mut stops, extend_mode);
+
+        let start_to_end = end_point - start_point;
+
+        self.push_stops(&stops);
+
+        Gradient {
+            start_point: start_point + start_to_end * start_offset,
+            end_point: start_point + start_to_end * end_offset,
+            extend_mode,
+        }
+    }
+
+    // NOTE: gradients must be pushed in the order they're created
+    // because create_gradient stores the stops in anticipation
+    pub fn create_radial_gradient(&mut self,
+                                  center: LayoutPoint,
+                                  radius: LayoutSize,
+                                  mut stops: Vec<GradientStop>,
+                                  extend_mode: ExtendMode) -> RadialGradient {
+        if radius.width <= 0.0 || radius.height <= 0.0 {
+            // The shader cannot handle a non positive radius. So
+            // reuse the stops vector and construct an equivalent
+            // gradient.
+            let last_color = stops.last().unwrap().color;
+
+            let stops = [
+                GradientStop {
+                    offset: 0.0,
+                    color: last_color,
+                },
+                GradientStop {
+                    offset: 1.0,
+                    color: last_color,
+                },
+            ];
+
+            self.push_stops(&stops);
+
+            return RadialGradient {
+                start_center: center,
+                start_radius: 0.0,
+                end_center: center,
+                end_radius: 1.0,
+                ratio_xy: 1.0,
+                extend_mode,
+            };
+        }
+
+        let (start_offset,
+             end_offset) = DisplayListBuilder::normalize_stops(&mut stops, extend_mode);
+
+        self.push_stops(&stops);
+
+        RadialGradient {
+            start_center: center,
+            start_radius: radius.width * start_offset,
+            end_center: center,
+            end_radius: radius.width * end_offset,
+            ratio_xy: radius.width / radius.height,
+            extend_mode,
+        }
+    }
+
+    // NOTE: gradients must be pushed in the order they're created
+    // because create_gradient stores the stops in anticipation
+    pub fn create_complex_radial_gradient(&mut self,
+                                          start_center: LayoutPoint,
+                                          start_radius: f32,
+                                          end_center: LayoutPoint,
+                                          end_radius: f32,
+                                          ratio_xy: f32,
+                                          stops: Vec<GradientStop>,
+                                          extend_mode: ExtendMode) -> RadialGradient {
+
+        self.push_stops(&stops);
+
+        RadialGradient {
+            start_center,
+            start_radius,
+            end_center,
+            end_radius,
+            ratio_xy,
+            extend_mode,
+        }
+    }
+
+    pub fn push_border(&mut self,
+                       rect: LayoutRect,
+                       local_clip: Option<LocalClip>,
+                       widths: BorderWidths,
+                       details: BorderDetails) {
+        let item = SpecificDisplayItem::Border(BorderDisplayItem {
+            details,
+            widths,
+        });
+
+        self.push_item(item, rect, local_clip);
+    }
+
+    pub fn push_box_shadow(&mut self,
+                           rect: LayoutRect,
+                           local_clip: Option<LocalClip>,
+                           box_bounds: LayoutRect,
+                           offset: LayoutVector2D,
+                           color: ColorF,
+                           blur_radius: f32,
+                           spread_radius: f32,
+                           border_radius: f32,
+                           clip_mode: BoxShadowClipMode) {
+        let item = SpecificDisplayItem::BoxShadow(BoxShadowDisplayItem {
+            box_bounds,
+            offset,
+            color,
+            blur_radius,
+            spread_radius,
+            border_radius,
+            clip_mode,
+        });
+
+        self.push_item(item, rect, local_clip);
+    }
+
+    pub fn push_gradient(&mut self,
+                         rect: LayoutRect,
+                         local_clip: Option<LocalClip>,
+                         gradient: Gradient,
+                         tile_size: LayoutSize,
+                         tile_spacing: LayoutSize) {
+        let item = SpecificDisplayItem::Gradient(GradientDisplayItem {
+            gradient,
+            tile_size,
+            tile_spacing,
+        });
+
+        self.push_item(item, rect, local_clip);
+    }
+
+    pub fn push_radial_gradient(&mut self,
+                                rect: LayoutRect,
+                                local_clip: Option<LocalClip>,
+                                gradient: RadialGradient,
+                                tile_size: LayoutSize,
+                                tile_spacing: LayoutSize) {
+        let item = SpecificDisplayItem::RadialGradient(RadialGradientDisplayItem {
+            gradient,
+            tile_size,
+            tile_spacing,
+        });
+
+        self.push_item(item, rect, local_clip);
+    }
+
+    pub fn push_stacking_context(&mut self,
+                                 scroll_policy: ScrollPolicy,
+                                 bounds: LayoutRect,
+                                 transform: Option<PropertyBinding<LayoutTransform>>,
+                                 transform_style: TransformStyle,
+                                 perspective: Option<LayoutTransform>,
+                                 mix_blend_mode: MixBlendMode,
+                                 filters: Vec<FilterOp>) {
+        let item = SpecificDisplayItem::PushStackingContext(PushStackingContextDisplayItem {
+            stacking_context: StackingContext {
+                scroll_policy,
+                transform,
+                transform_style,
+                perspective,
+                mix_blend_mode,
+            }
+        });
+
+        self.push_item(item, bounds, None);
+        self.push_iter(&filters);
+    }
+
+    pub fn pop_stacking_context(&mut self) {
+        self.push_new_empty_item(SpecificDisplayItem::PopStackingContext);
+    }
+
+    pub fn push_stops(&mut self, stops: &[GradientStop]) {
+        if stops.is_empty() {
+            return
+        }
+        self.push_new_empty_item(SpecificDisplayItem::SetGradientStops);
+        self.push_iter(stops);
+    }
+
+    fn generate_clip_id(&mut self, id: Option<ClipId>) -> ClipId {
+        id.unwrap_or_else(|| {
+            self.next_clip_id += 1;
+            ClipId::Clip(self.next_clip_id - 1, 0, self.pipeline_id)
+        })
+    }
+
+    pub fn define_scroll_frame<I>(&mut self,
+                                  id: Option<ClipId>,
+                                  content_rect: LayoutRect,
+                                  clip_rect: LayoutRect,
+                                  complex_clips: I,
+                                  image_mask: Option<ImageMask>)
+                                  -> ClipId
+                                  where I: IntoIterator<Item = ComplexClipRegion>,
+                                        I::IntoIter: ExactSizeIterator {
+        let id = self.generate_clip_id(id);
+        let item = SpecificDisplayItem::ScrollFrame(ClipDisplayItem {
+            id: id,
+            parent_id: self.clip_stack.last().unwrap().scroll_node_id,
+            image_mask: image_mask,
+        });
+
+        self.push_item(item, content_rect, Some(LocalClip::from(clip_rect)));
+        self.push_iter(complex_clips);
+        id
+    }
+
+    pub fn define_clip<I>(&mut self,
+                          id: Option<ClipId>,
+                          clip_rect: LayoutRect,
+                          complex_clips: I,
+                          image_mask: Option<ImageMask>)
+                          -> ClipId
+                          where I: IntoIterator<Item = ComplexClipRegion>,
+                                I::IntoIter: ExactSizeIterator {
+        let id = self.generate_clip_id(id);
+        let item = SpecificDisplayItem::Clip(ClipDisplayItem {
+            id,
+            parent_id: self.clip_stack.last().unwrap().scroll_node_id,
+            image_mask: image_mask,
+        });
+
+        self.push_item(item, clip_rect, Some(LocalClip::from(clip_rect)));
+        self.push_iter(complex_clips);
+        id
+    }
+
+    pub fn push_clip_id(&mut self, id: ClipId) {
+        self.clip_stack.push(ClipAndScrollInfo::simple(id));
+    }
+
+    pub fn push_clip_and_scroll_info(&mut self, info: ClipAndScrollInfo) {
+        self.clip_stack.push(info);
+    }
+
+    pub fn pop_clip_id(&mut self) {
+        self.clip_stack.pop();
+        assert!(self.clip_stack.len() > 0);
+    }
+
+    pub fn push_iframe(&mut self, rect: LayoutRect, pipeline_id: PipelineId) {
+        let item = SpecificDisplayItem::Iframe(IframeDisplayItem { pipeline_id: pipeline_id });
+        self.push_item(item, rect, None);
+    }
+
+    // Don't use this function. It will go away.
+    //
+    // We're using this method as a hack in Gecko to retain parts sub-parts of display
+    // lists so that we can regenerate them without building Gecko display items. WebRender
+    // will replace references to the root scroll frame id with the current scroll frame
+    // id.
+    pub fn push_nested_display_list(&mut self, built_display_list: &BuiltDisplayList) {
+        self.push_new_empty_item(SpecificDisplayItem::PushNestedDisplayList);
+        self.data.extend_from_slice(&built_display_list.data);
+        self.push_new_empty_item(SpecificDisplayItem::PopNestedDisplayList);
+    }
+
+    pub fn finalize(self) -> (PipelineId, LayoutSize, BuiltDisplayList) {
+        let end_time = precise_time_ns();
+
+        (self.pipeline_id,
+         self.content_size,
+         BuiltDisplayList {
+            descriptor: BuiltDisplayListDescriptor {
+                builder_start_time: self.builder_start_time,
+                builder_finish_time: end_time,
+            },
+            data: self.data,
+         })
+    }
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_api/src/font.rs
@@ -0,0 +1,195 @@
+/* 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 {ColorU, ColorF, LayoutPoint};
+use std::sync::Arc;
+
+#[cfg(target_os = "macos")] use core_foundation::string::CFString;
+#[cfg(target_os = "macos")] use core_graphics::font::CGFont;
+#[cfg(target_os = "macos")] use serde::de::{self, Deserialize, Deserializer};
+#[cfg(target_os = "macos")] use serde::ser::{Serialize, Serializer};
+#[cfg(target_os = "windows")] use dwrote::FontDescriptor;
+
+
+#[cfg(target_os = "macos")]
+#[derive(Clone)]
+pub struct NativeFontHandle(pub CGFont);
+
+#[cfg(target_os = "macos")]
+impl Serialize for NativeFontHandle {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
+        let postscript_name = self.0.postscript_name().to_string();
+        postscript_name.serialize(serializer)
+    }
+}
+
+#[cfg(target_os = "macos")]
+impl<'de> Deserialize<'de> for NativeFontHandle {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
+        let postscript_name: String = try!(Deserialize::deserialize(deserializer));
+
+        match CGFont::from_name(&CFString::new(&*postscript_name)) {
+            Ok(font) => Ok(NativeFontHandle(font)),
+            _ => Err(de::Error::custom("Couldn't find a font with that PostScript name!")),
+        }
+    }
+}
+
+/// Native fonts are not used on Linux; all fonts are raw.
+#[cfg(not(any(target_os = "macos", target_os = "windows")))]
+#[cfg_attr(not(any(target_os = "macos", target_os = "windows")), derive(Clone, Serialize, Deserialize))]
+pub struct NativeFontHandle;
+
+#[cfg(target_os = "windows")]
+pub type NativeFontHandle = FontDescriptor;
+
+#[repr(C)]
+#[derive(Copy, Clone, Deserialize, Serialize, Debug)]
+pub struct GlyphDimensions {
+    pub left: i32,
+    pub top: i32,
+    pub width: u32,
+    pub height: u32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Ord, PartialOrd)]
+pub struct FontKey(pub u32, pub u32);
+
+impl FontKey {
+    pub fn new(key0: u32, key1: u32) -> FontKey {
+        FontKey(key0, key1)
+    }
+}
+
+
+#[derive(Clone)]
+pub enum FontTemplate {
+    Raw(Arc<Vec<u8>>, u32),
+    Native(NativeFontHandle),
+}
+
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Ord, PartialOrd)]
+pub enum FontRenderMode {
+    Mono,
+    Alpha,
+    Subpixel,
+}
+
+const FIXED16_SHIFT: i32 = 16;
+
+// This matches the behaviour of SkScalarToFixed
+fn f32_truncate_to_fixed16(x: f32) -> i32 {
+    let fixed1 = (1 << FIXED16_SHIFT) as f32;
+    (x * fixed1) as i32
+}
+
+impl FontRenderMode {
+    // Skia quantizes subpixel offets into 1/4 increments.
+    // Given the absolute position, return the quantized increment
+    fn subpixel_quantize_offset(&self, pos: f32) -> SubpixelOffset {
+        if *self != FontRenderMode::Subpixel {
+            return SubpixelOffset::Zero;
+        }
+
+        const SUBPIXEL_BITS: i32 = 2;
+        const SUBPIXEL_FIXED16_MASK: i32 = ((1 << SUBPIXEL_BITS) - 1) << (FIXED16_SHIFT - SUBPIXEL_BITS);
+
+        const SUBPIXEL_ROUNDING: f32 = 0.5 / (1 << SUBPIXEL_BITS) as f32;
+        let pos = pos + SUBPIXEL_ROUNDING;
+        let fraction = (f32_truncate_to_fixed16(pos) & SUBPIXEL_FIXED16_MASK) >> (FIXED16_SHIFT - SUBPIXEL_BITS);
+
+        match fraction {
+            0 => SubpixelOffset::Zero,
+            1 => SubpixelOffset::Quarter,
+            2 => SubpixelOffset::Half,
+            3 => SubpixelOffset::ThreeQuarters,
+            _ => panic!("Should only be given the fractional part"),
+        }
+    }
+}
+
+#[repr(u8)]
+#[derive(Hash, Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
+pub enum SubpixelOffset {
+    Zero            = 0,
+    Quarter         = 1,
+    Half            = 2,
+    ThreeQuarters   = 3,
+}
+
+impl Into<f64> for SubpixelOffset {
+    fn into(self) -> f64 {
+        match self {
+            SubpixelOffset::Zero => 0.0,
+            SubpixelOffset::Quarter => 0.25,
+            SubpixelOffset::Half => 0.5,
+            SubpixelOffset::ThreeQuarters => 0.75,
+        }
+    }
+}
+
+#[derive(Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)]
+pub struct SubpixelPoint {
+    pub x: SubpixelOffset,
+    pub y: SubpixelOffset,
+}
+
+impl SubpixelPoint {
+    pub fn new(point: LayoutPoint,
+               render_mode: FontRenderMode) -> SubpixelPoint {
+        SubpixelPoint {
+            x: render_mode.subpixel_quantize_offset(point.x),
+            y: render_mode.subpixel_quantize_offset(point.y),
+        }
+    }
+
+    pub fn to_f64(&self) -> (f64, f64) {
+        (self.x.into(), self.y.into())
+    }
+
+    pub fn set_offset(&mut self, point: LayoutPoint, render_mode: FontRenderMode) {
+        self.x = render_mode.subpixel_quantize_offset(point.x);
+        self.y = render_mode.subpixel_quantize_offset(point.y);
+    }
+}
+
+#[derive(Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)]
+pub struct GlyphKey {
+    pub font_key: FontKey,
+    // The font size is in *device* pixels, not logical pixels.
+    // It is stored as an Au since we need sub-pixel sizes, but
+    // can't store as a f32 due to use of this type as a hash key.
+    // TODO(gw): Perhaps consider having LogicalAu and DeviceAu
+    //           or something similar to that.
+    pub size: Au,
+    pub index: u32,
+    pub color: ColorU,
+    pub subpixel_point: SubpixelPoint,
+}
+
+impl GlyphKey {
+    pub fn new(font_key: FontKey,
+               size: Au,
+               color: ColorF,
+               index: u32,
+               point: LayoutPoint,
+               render_mode: FontRenderMode) -> GlyphKey {
+        GlyphKey {
+            font_key,
+            size,
+            color: ColorU::from(color),
+            index,
+            subpixel_point: SubpixelPoint::new(point, render_mode),
+        }
+    }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct GlyphInstance {
+    pub index: u32,
+    pub point: LayoutPoint,
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_api/src/image.rs
@@ -0,0 +1,194 @@
+/* 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 std::sync::Arc;
+use {DeviceUintRect, DevicePoint};
+use {TileOffset, TileSize};
+use font::{FontKey, FontTemplate};
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub struct ImageKey(pub u32, pub u32);
+
+impl ImageKey {
+    pub fn new(key0: u32, key1: u32) -> ImageKey {
+        ImageKey(key0, key1)
+    }
+}
+
+/// An arbitrary identifier for an external image provided by the
+/// application. It must be a unique identifier for each external
+/// image.
+#[repr(C)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
+pub struct ExternalImageId(pub u64);
+
+#[repr(u32)]
+#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub enum ExternalImageType {
+    Texture2DHandle,        // gl TEXTURE_2D handle
+    TextureRectHandle,      // gl TEXTURE_RECT handle
+    TextureExternalHandle,  // gl TEXTURE_EXTERNAL handle
+    ExternalBuffer,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub struct ExternalImageData {
+    pub id: ExternalImageId,
+    pub channel_index: u8,
+    pub image_type: ExternalImageType,
+}
+
+#[repr(u32)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub enum ImageFormat {
+    Invalid  = 0,
+    A8       = 1,
+    RGB8     = 2,
+    BGRA8    = 3,
+    RGBAF32  = 4,
+    RG8      = 5,
+}
+
+impl ImageFormat {
+    pub fn bytes_per_pixel(self) -> Option<u32> {
+        match self {
+            ImageFormat::A8 => Some(1),
+            ImageFormat::RGB8 => Some(3),
+            ImageFormat::BGRA8 => Some(4),
+            ImageFormat::RGBAF32 => Some(16),
+            ImageFormat::RG8 => Some(2),
+            ImageFormat::Invalid => None,
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
+pub struct ImageDescriptor {
+    pub format: ImageFormat,
+    pub width: u32,
+    pub height: u32,
+    pub stride: Option<u32>,
+    pub offset: u32,
+    pub is_opaque: bool,
+}
+
+impl ImageDescriptor {
+    pub fn new(width: u32, height: u32, format: ImageFormat, is_opaque: bool) -> Self {
+        ImageDescriptor {
+            width,
+            height,
+            format,
+            stride: None,
+            offset: 0,
+            is_opaque,
+        }
+    }
+
+    pub fn compute_stride(&self) -> u32 {
+        self.stride.unwrap_or(self.width * self.format.bytes_per_pixel().unwrap())
+    }
+}
+
+#[derive(Clone, Serialize, Deserialize)]
+pub enum ImageData {
+    Raw(Arc<Vec<u8>>),
+    Blob(BlobImageData),
+    External(ExternalImageData),
+}
+
+impl ImageData {
+    pub fn new(bytes: Vec<u8>) -> ImageData {
+        ImageData::Raw(Arc::new(bytes))
+    }
+
+    pub fn new_shared(bytes: Arc<Vec<u8>>) -> ImageData {
+        ImageData::Raw(bytes)
+    }
+
+    pub fn new_blob_image(commands: Vec<u8>) -> ImageData {
+        ImageData::Blob(commands)
+    }
+
+    #[inline]
+    pub fn is_blob(&self) -> bool {
+        match self {
+            &ImageData::Blob(_) => true,
+            _ => false,
+        }
+    }
+
+    #[inline]
+    pub fn uses_texture_cache(&self) -> bool {
+        match self {
+            &ImageData::External(ext_data) => {
+                match ext_data.image_type {
+                    ExternalImageType::Texture2DHandle => false,
+                    ExternalImageType::TextureRectHandle => false,
+                    ExternalImageType::TextureExternalHandle => false,
+                    ExternalImageType::ExternalBuffer => true,
+                }
+            }
+            &ImageData::Blob(_) => true,
+            &ImageData::Raw(_) => true,
+        }
+    }
+}
+
+pub trait BlobImageResources {
+    fn get_font_data(&self, key: FontKey) -> &FontTemplate;
+    fn get_image(&self, key: ImageKey) -> Option<(&ImageData, &ImageDescriptor)>;
+}
+
+pub trait BlobImageRenderer: Send {
+    fn add(&mut self, key: ImageKey, data: BlobImageData, tiling: Option<TileSize>);
+
+    fn update(&mut self, key: ImageKey, data: BlobImageData);
+
+    fn delete(&mut self, key: ImageKey);
+
+    fn request(&mut self,
+               services: &BlobImageResources,
+               key: BlobImageRequest,
+               descriptor: &BlobImageDescriptor,
+               dirty_rect: Option<DeviceUintRect>);
+
+    fn resolve(&mut self, key: BlobImageRequest) -> BlobImageResult;
+
+    fn delete_font(&mut self, key: FontKey);
+}
+
+pub type BlobImageData = Vec<u8>;
+
+pub type BlobImageResult = Result<RasterizedBlobImage, BlobImageError>;
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+pub struct BlobImageDescriptor {
+    pub width: u32,
+    pub height: u32,
+    pub offset: DevicePoint,
+    pub format: ImageFormat,
+}
+
+pub struct RasterizedBlobImage {
+    pub width: u32,
+    pub height: u32,
+    pub data: Vec<u8>,
+}
+
+#[derive(Clone, Debug)]
+pub enum BlobImageError {
+    Oom,
+    InvalidKey,
+    InvalidData,
+    Other(String),
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct BlobImageRequest {
+    pub key: ImageKey,
+    pub tile: Option<TileOffset>,
+}
rename from gfx/webrender_traits/src/lib.rs
rename to gfx/webrender_api/src/lib.rs
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_api/src/units.rs
@@ -0,0 +1,109 @@
+/* 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/. */
+
+//! A collection of coordinate spaces and their corresponding Point, Size and Rect types.
+//!
+//! Physical pixels take into account the device pixel ratio and their dimensions tend
+//! to correspond to the allocated size of resources in memory, while logical pixels
+//! don't have the device pixel ratio applied which means they are agnostic to the usage
+//! of hidpi screens and the like.
+//!
+//! The terms "layer" and "stacking context" can be used interchangeably
+//! in the context of coordinate systems.
+
+use euclid::{Length, TypedTransform3D, TypedRect, TypedSize2D};
+use euclid::{TypedPoint2D, TypedPoint3D, TypedVector2D, TypedVector3D};
+
+/// Geometry in the coordinate system of the render target (screen or intermediate
+/// surface) in physical pixels.
+#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
+pub struct DevicePixel;
+
+pub type DeviceIntRect = TypedRect<i32, DevicePixel>;
+pub type DeviceIntPoint = TypedPoint2D<i32, DevicePixel>;
+pub type DeviceIntSize = TypedSize2D<i32, DevicePixel>;
+pub type DeviceIntLength = Length<i32, DevicePixel>;
+
+pub type DeviceUintRect = TypedRect<u32, DevicePixel>;
+pub type DeviceUintPoint = TypedPoint2D<u32, DevicePixel>;
+pub type DeviceUintSize = TypedSize2D<u32, DevicePixel>;
+
+pub type DeviceRect = TypedRect<f32, DevicePixel>;
+pub type DevicePoint = TypedPoint2D<f32, DevicePixel>;
+pub type DeviceVector2D = TypedVector2D<f32, DevicePixel>;
+pub type DeviceSize = TypedSize2D<f32, DevicePixel>;
+
+/// Geometry in a stacking context's local coordinate space (logical pixels).
+///
+/// For now layout pixels are equivalent to layer pixels, but it may change.
+pub type LayoutPixel = LayerPixel;
+
+pub type LayoutRect = LayerRect;
+pub type LayoutPoint = LayerPoint;
+pub type LayoutVector2D = LayerVector2D;
+pub type LayoutVector3D = LayerVector3D;
+pub type LayoutSize = LayerSize;
+
+/// Geometry in a layer's local coordinate space (logical pixels).
+#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
+pub struct LayerPixel;
+
+pub type LayerRect = TypedRect<f32, LayerPixel>;
+pub type LayerPoint = TypedPoint2D<f32, LayerPixel>;
+pub type LayerPoint3D = TypedPoint3D<f32, LayerPixel>;
+pub type LayerVector2D = TypedVector2D<f32, LayerPixel>;
+pub type LayerVector3D = TypedVector3D<f32, LayerPixel>;
+pub type LayerSize = TypedSize2D<f32, LayerPixel>;
+
+/// Geometry in a layer's scrollable parent coordinate space (logical pixels).
+///
+/// Some layers are scrollable while some are not. There is a distinction between
+/// a layer's parent layer and a layer's scrollable parent layer (its closest parent
+/// that is scrollable, but not necessarily its immediate parent). Most of the internal
+/// transforms are expressed in terms of the scrollable parent and not the immediate
+/// parent.
+#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
+pub struct ScrollLayerPixel;
+
+pub type ScrollLayerRect = TypedRect<f32, ScrollLayerPixel>;
+pub type ScrollLayerPoint = TypedPoint2D<f32, ScrollLayerPixel>;
+pub type ScrollLayerVector2D = TypedVector2D<f32, ScrollLayerPixel>;
+pub type ScrollLayerSize = TypedSize2D<f32, ScrollLayerPixel>;
+
+/// Geometry in the document's coordinate space (logical pixels).
+#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
+pub struct WorldPixel;
+
+pub type WorldRect = TypedRect<f32, WorldPixel>;
+pub type WorldPoint = TypedPoint2D<f32, WorldPixel>;
+pub type WorldSize = TypedSize2D<f32, WorldPixel>;
+pub type WorldPoint3D = TypedPoint3D<f32, WorldPixel>;
+pub type WorldVector2D = TypedVector2D<f32, WorldPixel>;
+pub type WorldVector3D = TypedVector3D<f32, WorldPixel>;
+
+/// Offset in number of tiles.
+#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
+pub struct Tiles;
+pub type TileOffset = TypedPoint2D<u16, Tiles>;
+
+pub type LayoutTransform = TypedTransform3D<f32, LayoutPixel, LayoutPixel>;
+pub type LayerTransform = TypedTransform3D<f32, LayerPixel, LayerPixel>;
+pub type LayerToScrollTransform = TypedTransform3D<f32, LayerPixel, ScrollLayerPixel>;
+pub type ScrollToLayerTransform = TypedTransform3D<f32, ScrollLayerPixel, LayerPixel>;
+pub type LayerToWorldTransform = TypedTransform3D<f32, LayerPixel, WorldPixel>;
+pub type WorldToLayerTransform = TypedTransform3D<f32, WorldPixel, LayerPixel>;
+pub type ScrollToWorldTransform = TypedTransform3D<f32, ScrollLayerPixel, WorldPixel>;
+
+
+pub fn device_length(value: f32, device_pixel_ratio: f32) -> DeviceIntLength {
+    DeviceIntLength::new((value * device_pixel_ratio).round() as i32)
+}
+
+pub fn as_scroll_parent_rect(rect: &LayerRect) -> ScrollLayerRect {
+    ScrollLayerRect::from_untyped(&rect.to_untyped())
+}
+
+pub fn as_scroll_parent_vector(vector: &LayerVector2D) -> ScrollLayerVector2D {
+    ScrollLayerVector2D::from_untyped(&vector.to_untyped())
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_api/src/webgl.rs
@@ -0,0 +1,1056 @@
+/* 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 channel::MsgSender;
+use gleam::gl;
+
+#[cfg(feature = "nightly")]
+use core::nonzero::NonZero;
+
+use offscreen_gl_context::{GLContext, NativeGLContextMethods, GLContextAttributes};
+use std::fmt;
+
+#[derive(Clone, Deserialize, Serialize)]
+pub enum WebGLCommand {
+    GetContextAttributes(MsgSender<GLContextAttributes>),
+    ActiveTexture(u32),
+    BlendColor(f32, f32, f32, f32),
+    BlendEquation(u32),
+    BlendEquationSeparate(u32, u32),
+    BlendFunc(u32, u32),
+    BlendFuncSeparate(u32, u32, u32, u32),
+    AttachShader(WebGLProgramId, WebGLShaderId),
+    DetachShader(WebGLProgramId, WebGLShaderId),
+    BindAttribLocation(WebGLProgramId, u32, String),
+    BufferData(u32, Vec<u8>, u32),
+    BufferSubData(u32, isize, Vec<u8>),
+    Clear(u32),
+    ClearColor(f32, f32, f32, f32),
+    ClearDepth(f64),
+    ClearStencil(i32),
+    ColorMask(bool, bool, bool, bool),
+    CullFace(u32),
+    FrontFace(u32),
+    DepthFunc(u32),
+    DepthMask(bool),
+    DepthRange(f64, f64),
+    Enable(u32),
+    Disable(u32),
+    CompileShader(WebGLShaderId, String),
+    CopyTexImage2D(u32, i32, u32, i32, i32, i32, i32, i32),
+    CopyTexSubImage2D(u32, i32, i32, i32, i32, i32, i32, i32),
+    CreateBuffer(MsgSender<Option<WebGLBufferId>>),
+    CreateFramebuffer(MsgSender<Option<WebGLFramebufferId>>),
+    CreateRenderbuffer(MsgSender<Option<WebGLRenderbufferId>>),
+    CreateTexture(MsgSender<Option<WebGLTextureId>>),
+    CreateProgram(MsgSender<Option<WebGLProgramId>>),
+    CreateShader(u32, MsgSender<Option<WebGLShaderId>>),
+    DeleteBuffer(WebGLBufferId),
+    DeleteFramebuffer(WebGLFramebufferId),
+    DeleteRenderbuffer(WebGLRenderbufferId),
+    DeleteTexture(WebGLTextureId),
+    DeleteProgram(WebGLProgramId),
+    DeleteShader(WebGLShaderId),
+    BindBuffer(u32, Option<WebGLBufferId>),
+    BindFramebuffer(u32, WebGLFramebufferBindingRequest),
+    BindRenderbuffer(u32, Option<WebGLRenderbufferId>),
+    BindTexture(u32, Option<WebGLTextureId>),
+    DisableVertexAttribArray(u32),
+    DrawArrays(u32, i32, i32),
+    DrawElements(u32, i32, u32, i64),
+    EnableVertexAttribArray(u32),
+    FramebufferRenderbuffer(u32, u32, u32, Option<WebGLRenderbufferId>),
+    FramebufferTexture2D(u32, u32, u32, Option<WebGLTextureId>, i32),
+    GetBufferParameter(u32, u32, MsgSender<WebGLResult<WebGLParameter>>),
+    GetExtensions(MsgSender<String>),
+    GetParameter(u32, MsgSender<WebGLResult<WebGLParameter>>),
+    GetProgramParameter(WebGLProgramId, u32, MsgSender<WebGLResult<WebGLParameter>>),
+    GetShaderParameter(WebGLShaderId, u32, MsgSender<WebGLResult<WebGLParameter>>),
+    GetShaderPrecisionFormat(u32, u32, MsgSender<WebGLResult<(i32, i32, i32)>>),
+    GetActiveAttrib(WebGLProgramId, u32, MsgSender<WebGLResult<(i32, u32, String)>>),
+    GetActiveUniform(WebGLProgramId, u32, MsgSender<WebGLResult<(i32, u32, String)>>),
+    GetAttribLocation(WebGLProgramId, String, MsgSender<Option<i32>>),
+    GetUniformLocation(WebGLProgramId, String, MsgSender<Option<i32>>),
+    GetVertexAttrib(u32, u32, MsgSender<WebGLResult<WebGLParameter>>),
+    GetVertexAttribOffset(u32, u32, MsgSender<WebGLResult<isize>>),
+    GetShaderInfoLog(WebGLShaderId, MsgSender<String>),
+    GetProgramInfoLog(WebGLProgramId, MsgSender<String>),
+    PolygonOffset(f32, f32),
+    RenderbufferStorage(u32, u32, i32, i32),
+    ReadPixels(i32, i32, i32, i32, u32, u32, MsgSender<Vec<u8>>),
+    SampleCoverage(f32, bool),
+    Scissor(i32, i32, i32, i32),
+    StencilFunc(u32, i32, u32),
+    StencilFuncSeparate(u32, u32, i32, u32),
+    StencilMask(u32),
+    StencilMaskSeparate(u32, u32),
+    StencilOp(u32, u32, u32),
+    StencilOpSeparate(u32, u32, u32, u32),
+    Hint(u32, u32),
+    IsEnabled(u32, MsgSender<bool>),
+    LineWidth(f32),
+    PixelStorei(u32, i32),
+    LinkProgram(WebGLProgramId),
+    Uniform1f(i32, f32),
+    Uniform1fv(i32, Vec<f32>),
+    Uniform1i(i32, i32),
+    Uniform1iv(i32, Vec<i32>),
+    Uniform2f(i32, f32, f32),
+    Uniform2fv(i32, Vec<f32>),
+    Uniform2i(i32, i32, i32),
+    Uniform2iv(i32, Vec<i32>),
+    Uniform3f(i32, f32, f32, f32),
+    Uniform3fv(i32, Vec<f32>),
+    Uniform3i(i32, i32, i32, i32),
+    Uniform3iv(i32, Vec<i32>),
+    Uniform4f(i32, f32, f32, f32, f32),
+    Uniform4fv(i32, Vec<f32>),
+    Uniform4i(i32, i32, i32, i32, i32),
+    Uniform4iv(i32, Vec<i32>),
+    UniformMatrix2fv(i32, bool, Vec<f32>),
+    UniformMatrix3fv(i32, bool, Vec<f32>),
+    UniformMatrix4fv(i32, bool, Vec<f32>),
+    UseProgram(WebGLProgramId),
+    ValidateProgram(WebGLProgramId),
+    VertexAttrib(u32, f32, f32, f32, f32),
+    VertexAttribPointer(u32, i32, u32, bool, i32, u32),
+    VertexAttribPointer2f(u32, i32, bool, i32, u32),
+    Viewport(i32, i32, i32, i32),
+    TexImage2D(u32, i32, i32, i32, i32, u32, u32, Vec<u8>),
+    TexParameteri(u32, u32, i32),
+    TexParameterf(u32, u32, f32),
+    TexSubImage2D(u32, i32, i32, i32, i32, i32, u32, u32, Vec<u8>),
+    DrawingBufferWidth(MsgSender<i32>),
+    DrawingBufferHeight(MsgSender<i32>),
+    Finish(MsgSender<()>),
+    Flush,
+    GenerateMipmap(u32),
+    CreateVertexArray(MsgSender<Option<WebGLVertexArrayId>>),
+    DeleteVertexArray(WebGLVertexArrayId),
+    BindVertexArray(Option<WebGLVertexArrayId>),
+    FenceAndWaitSync,
+}
+
+#[cfg(feature = "nightly")]
+macro_rules! define_resource_id_struct {
+    ($name:ident) => {
+        #[derive(Clone, Copy, Eq, Hash, PartialEq)]
+        pub struct $name(NonZero<u32>);
+
+        impl $name {
+            #[inline]
+            unsafe fn new(id: u32) -> Self {
+                $name(NonZero::new(id))
+            }
+
+            #[inline]
+            fn get(self) -> u32 {
+                *self.0
+            }
+        }
+
+    };
+}
+
+#[cfg(not(feature = "nightly"))]
+macro_rules! define_resource_id_struct {
+    ($name:ident) => {
+        #[derive(Clone, Copy, Eq, Hash, PartialEq)]
+        pub struct $name(u32);
+
+        impl $name {
+            #[inline]
+            unsafe fn new(id: u32) -> Self {
+                $name(id)
+            }
+
+            #[inline]
+            fn get(self) -> u32 {
+                self.0
+            }
+        }
+    };
+}
+
+macro_rules! define_resource_id {
+    ($name:ident) => {
+        define_resource_id_struct!($name);
+
+        impl<'de> ::serde::Deserialize<'de> for $name {
+            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+                where D: ::serde::Deserializer<'de>
+            {
+                let id = try!(u32::deserialize(deserializer));
+                if id == 0 {
+                    Err(::serde::de::Error::custom("expected a non-zero value"))
+                } else {
+                    Ok(unsafe { $name::new(id) })
+                }
+            }
+        }
+
+        impl ::serde::Serialize for $name {
+            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+                where S: ::serde::Serializer
+            {
+                self.get().serialize(serializer)
+            }
+        }
+
+        impl ::std::fmt::Debug for $name {
+            fn fmt(&self, fmt: &mut ::std::fmt::Formatter)
+                  -> Result<(), ::std::fmt::Error> {
+                fmt.debug_tuple(stringify!($name))
+                   .field(&self.get())
+                   .finish()
+            }
+        }
+
+        impl ::std::fmt::Display for $name {
+            fn fmt(&self, fmt: &mut ::std::fmt::Formatter)
+                  -> Result<(), ::std::fmt::Error> {
+                write!(fmt, "{}", self.get())
+            }
+        }
+
+        impl ::heapsize::HeapSizeOf for $name {
+            fn heap_size_of_children(&self) -> usize { 0 }
+        }
+    }
+}
+
+define_resource_id!(WebGLBufferId);
+define_resource_id!(WebGLFramebufferId);
+define_resource_id!(WebGLRenderbufferId);
+define_resource_id!(WebGLTextureId);
+define_resource_id!(WebGLProgramId);
+define_resource_id!(WebGLShaderId);
+define_resource_id!(WebGLVertexArrayId);
+
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
+pub struct WebGLContextId(pub usize);
+
+impl ::heapsize::HeapSizeOf for WebGLContextId {
+    fn heap_size_of_children(&self) -> usize { 0 }
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub enum WebGLError {
+    InvalidEnum,
+    InvalidFramebufferOperation,
+    InvalidOperation,
+    InvalidValue,
+    OutOfMemory,
+    ContextLost,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub enum WebGLFramebufferBindingRequest {
+    Explicit(WebGLFramebufferId),
+    Default,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub enum WebGLParameter {
+    Int(i32),
+    Bool(bool),
+    String(String),
+    Float(f32),
+    FloatArray(Vec<f32>),
+    Invalid,
+}
+
+pub type WebGLResult<T> = Result<T, WebGLError>;
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub enum WebGLShaderParameter {
+    Int(i32),
+    Bool(bool),
+    Invalid,
+}
+
+impl fmt::Debug for WebGLCommand {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use WebGLCommand::*;
+        let name = match *self {
+            GetContextAttributes(..) => "GetContextAttributes",
+            ActiveTexture(..) => "ActiveTexture",
+            BlendColor(..) => "BlendColor",
+            BlendEquation(..) => "BlendEquation",
+            BlendEquationSeparate(..) => "BlendEquationSeparate",
+            BlendFunc(..) => "BlendFunc",
+            BlendFuncSeparate(..) => "BlendFuncSeparate",
+            AttachShader(..) => "AttachShader",
+            DetachShader(..) => "DetachShader",
+            BindAttribLocation(..) => "BindAttribLocation",
+            BufferData(..) => "BufferData",
+            BufferSubData(..) => "BufferSubData",
+            Clear(..) => "Clear",
+            ClearColor(..) => "ClearColor",
+            ClearDepth(..) => "ClearDepth",
+            ClearStencil(..) => "ClearStencil",
+            ColorMask(..) => "ColorMask",
+            CopyTexImage2D(..) => "CopyTexImage2D",
+            CopyTexSubImage2D(..) => "CopyTexSubImage2D",
+            CullFace(..) => "CullFace",
+            FrontFace(..) => "FrontFace",
+            DepthFunc(..) => "DepthFunc",
+            DepthMask(..) => "DepthMask",
+            DepthRange(..) => "DepthRange",
+            Enable(..) => "Enable",
+            Disable(..) => "Disable",
+            CompileShader(..) => "CompileShader",
+            CreateBuffer(..) => "CreateBuffer",
+            CreateFramebuffer(..) => "CreateFramebuffer",
+            CreateRenderbuffer(..) => "CreateRenderbuffer",
+            CreateTexture(..) => "CreateTexture",
+            CreateProgram(..) => "CreateProgram",
+            CreateShader(..) => "CreateShader",
+            DeleteBuffer(..) => "DeleteBuffer",
+            DeleteFramebuffer(..) => "DeleteFramebuffer",
+            DeleteRenderbuffer(..) => "DeleteRenderBuffer",
+            DeleteTexture(..) => "DeleteTexture",
+            DeleteProgram(..) => "DeleteProgram",
+            DeleteShader(..) => "DeleteShader",
+            BindBuffer(..) => "BindBuffer",
+            BindFramebuffer(..) => "BindFramebuffer",
+            BindRenderbuffer(..) => "BindRenderbuffer",
+            BindTexture(..) => "BindTexture",
+            DisableVertexAttribArray(..) => "DisableVertexAttribArray",
+            DrawArrays(..) => "DrawArrays",
+            DrawElements(..) => "DrawElements",
+            EnableVertexAttribArray(..) => "EnableVertexAttribArray",
+            FramebufferRenderbuffer(..) => "FramebufferRenderbuffer",
+            FramebufferTexture2D(..) => "FramebufferTexture2D",
+            GetBufferParameter(..) => "GetBufferParameter",
+            GetExtensions(..) => "GetExtensions",
+            GetParameter(..) => "GetParameter",
+            GetProgramParameter(..) => "GetProgramParameter",
+            GetShaderParameter(..) => "GetShaderParameter",
+            GetShaderPrecisionFormat(..) => "GetShaderPrecisionFormat",
+            GetActiveAttrib(..) => "GetActiveAttrib",
+            GetActiveUniform(..) => "GetActiveUniform",
+            GetAttribLocation(..) => "GetAttribLocation",
+            GetUniformLocation(..) => "GetUniformLocation",
+            GetShaderInfoLog(..) => "GetShaderInfoLog",
+            GetProgramInfoLog(..) => "GetProgramInfoLog",
+            GetVertexAttrib(..) => "GetVertexAttrib",
+            GetVertexAttribOffset(..) => "GetVertexAttribOffset",
+            PolygonOffset(..) => "PolygonOffset",
+            ReadPixels(..) => "ReadPixels",
+            RenderbufferStorage(..) => "RenderbufferStorage",
+            SampleCoverage(..) => "SampleCoverage",
+            Scissor(..) => "Scissor",
+            StencilFunc(..) => "StencilFunc",
+            StencilFuncSeparate(..) => "StencilFuncSeparate",
+            StencilMask(..) => "StencilMask",
+            StencilMaskSeparate(..) => "StencilMaskSeparate",
+            StencilOp(..) => "StencilOp",
+            StencilOpSeparate(..) => "StencilOpSeparate",
+            Hint(..) => "Hint",
+            IsEnabled(..) => "IsEnabled",
+            LineWidth(..) => "LineWidth",
+            PixelStorei(..) => "PixelStorei",
+            LinkProgram(..) => "LinkProgram",
+            Uniform1f(..) => "Uniform1f",
+            Uniform1fv(..) => "Uniform1fv",
+            Uniform1i(..) => "Uniform1i",
+            Uniform1iv(..) => "Uniform1iv",
+            Uniform2f(..) => "Uniform2f",
+            Uniform2fv(..) => "Uniform2fv",
+            Uniform2i(..) => "Uniform2i",
+            Uniform2iv(..) => "Uniform2iv",
+            Uniform3f(..) => "Uniform3f",
+            Uniform3fv(..) => "Uniform3fv",
+            Uniform3i(..) => "Uniform3i",
+            Uniform3iv(..) => "Uniform3iv",
+            Uniform4f(..) => "Uniform4f",
+            Uniform4fv(..) => "Uniform4fv",
+            Uniform4i(..) => "Uniform4i",
+            Uniform4iv(..) => "Uniform4iv",
+            UniformMatrix2fv(..) => "UniformMatrix2fv",
+            UniformMatrix3fv(..) => "UniformMatrix3fv",
+            UniformMatrix4fv(..) => "UniformMatrix4fv",
+            UseProgram(..) => "UseProgram",
+            ValidateProgram(..) => "ValidateProgram",
+            VertexAttrib(..) => "VertexAttrib",
+            VertexAttribPointer2f(..) => "VertexAttribPointer2f",
+            VertexAttribPointer(..) => "VertexAttribPointer",
+            Viewport(..) => "Viewport",
+            TexImage2D(..) => "TexImage2D",
+            TexParameteri(..) => "TexParameteri",
+            TexParameterf(..) => "TexParameterf",
+            TexSubImage2D(..) => "TexSubImage2D",
+            DrawingBufferWidth(..) => "DrawingBufferWidth",
+            DrawingBufferHeight(..) => "DrawingBufferHeight",
+            Finish(..) => "Finish",
+            Flush => "Flush",
+            GenerateMipmap(..) => "GenerateMipmap",
+            CreateVertexArray(..) => "CreateVertexArray",
+            DeleteVertexArray(..) => "DeleteVertexArray",
+            BindVertexArray(..) => "BindVertexArray",
+            FenceAndWaitSync => "FenceAndWaitSync",
+        };
+
+        write!(f, "CanvasWebGLMsg::{}(..)", name)
+    }
+}
+
+impl WebGLCommand {
+    /// NOTE: This method consumes the command
+    pub fn apply<Native: NativeGLContextMethods>(self, ctx: &GLContext<Native>) {
+        match self {
+            WebGLCommand::GetContextAttributes(sender) =>
+                sender.send(*ctx.borrow_attributes()).unwrap(),
+            WebGLCommand::ActiveTexture(target) =>
+                ctx.gl().active_texture(target),
+            WebGLCommand::AttachShader(program_id, shader_id) =>
+                ctx.gl().attach_shader(program_id.get(), shader_id.get()),
+            WebGLCommand::DetachShader(program_id, shader_id) =>
+                ctx.gl().detach_shader(program_id.get(), shader_id.get()),
+            WebGLCommand::BindAttribLocation(program_id, index, name) =>
+                ctx.gl().bind_attrib_location(program_id.get(), index, &name),
+            WebGLCommand::BlendColor(r, g, b, a) =>
+                ctx.gl().blend_color(r, g, b, a),
+            WebGLCommand::BlendEquation(mode) =>
+                ctx.gl().blend_equation(mode),
+            WebGLCommand::BlendEquationSeparate(mode_rgb, mode_alpha) =>
+                ctx.gl().blend_equation_separate(mode_rgb, mode_alpha),
+            WebGLCommand::BlendFunc(src, dest) =>
+                ctx.gl().blend_func(src, dest),
+            WebGLCommand::BlendFuncSeparate(src_rgb, dest_rgb, src_alpha, dest_alpha) =>
+                ctx.gl().blend_func_separate(src_rgb, dest_rgb, src_alpha, dest_alpha),
+            WebGLCommand::BufferData(buffer_type, data, usage) =>
+                gl::buffer_data(ctx.gl(), buffer_type, &data, usage),
+            WebGLCommand::BufferSubData(buffer_type, offset, data) =>
+                gl::buffer_sub_data(ctx.gl(), buffer_type, offset, &data),
+            WebGLCommand::Clear(mask) =>
+                ctx.gl().clear(mask),
+            WebGLCommand::ClearColor(r, g, b, a) =>
+                ctx.gl().clear_color(r, g, b, a),
+            WebGLCommand::ClearDepth(depth) =>
+                ctx.gl().clear_depth(depth),
+            WebGLCommand::ClearStencil(stencil) =>
+                ctx.gl().clear_stencil(stencil),
+            WebGLCommand::ColorMask(r, g, b, a) =>
+                ctx.gl().color_mask(r, g, b, a),
+            WebGLCommand::CopyTexImage2D(target, level, internal_format, x, y, width, height, border) =>
+                ctx.gl().copy_tex_image_2d(target, level, internal_format, x, y, width, height, border),
+            WebGLCommand::CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height) =>
+                ctx.gl().copy_tex_sub_image_2d(target, level, xoffset, yoffset, x, y, width, height),
+            WebGLCommand::CullFace(mode) =>
+                ctx.gl().cull_face(mode),
+            WebGLCommand::DepthFunc(func) =>
+                ctx.gl().depth_func(func),
+            WebGLCommand::DepthMask(flag) =>
+                ctx.gl().depth_mask(flag),
+            WebGLCommand::DepthRange(near, far) =>
+                ctx.gl().depth_range(near, far),
+            WebGLCommand::Disable(cap) =>
+                ctx.gl().disable(cap),
+            WebGLCommand::Enable(cap) =>
+                ctx.gl().enable(cap),
+            WebGLCommand::FramebufferRenderbuffer(target, attachment, renderbuffertarget, rb) =>
+                ctx.gl().framebuffer_renderbuffer(target, attachment, renderbuffertarget, rb.map_or(0, WebGLRenderbufferId::get)),
+            WebGLCommand::FramebufferTexture2D(target, attachment, textarget, texture, level) =>
+                ctx.gl().framebuffer_texture_2d(target, attachment, textarget, texture.map_or(0, WebGLTextureId::get), level),
+            WebGLCommand::FrontFace(mode) =>
+                ctx.gl().front_face(mode),
+            WebGLCommand::DisableVertexAttribArray(attrib_id) =>
+                ctx.gl().disable_vertex_attrib_array(attrib_id),
+            WebGLCommand::DrawArrays(mode, first, count) =>
+                ctx.gl().draw_arrays(mode, first, count),
+            WebGLCommand::DrawElements(mode, count, type_, offset) =>
+                ctx.gl().draw_elements(mode, count, type_, offset as u32),
+            WebGLCommand::EnableVertexAttribArray(attrib_id) =>
+                ctx.gl().enable_vertex_attrib_array(attrib_id),
+            WebGLCommand::Hint(name, val) =>
+                ctx.gl().hint(name, val),
+            WebGLCommand::IsEnabled(cap, chan) =>
+                chan.send(ctx.gl().is_enabled(cap) != 0).unwrap(),
+            WebGLCommand::LineWidth(width) =>
+                ctx.gl().line_width(width),
+            WebGLCommand::PixelStorei(name, val) =>
+                ctx.gl().pixel_store_i(name, val),
+            WebGLCommand::PolygonOffset(factor, units) =>
+                ctx.gl().polygon_offset(factor, units),
+            WebGLCommand::ReadPixels(x, y, width, height, format, pixel_type, chan) =>
+                Self::read_pixels(ctx.gl(), x, y, width, height, format, pixel_type, chan),
+            WebGLCommand::RenderbufferStorage(target, format, width, height) =>
+                ctx.gl().renderbuffer_storage(target, format, width, height),
+            WebGLCommand::SampleCoverage(value, invert) =>
+                ctx.gl().sample_coverage(value, invert),
+            WebGLCommand::Scissor(x, y, width, height) =>
+                ctx.gl().scissor(x, y, width, height),
+            WebGLCommand::StencilFunc(func, ref_, mask) =>
+                ctx.gl().stencil_func(func, ref_, mask),
+            WebGLCommand::StencilFuncSeparate(face, func, ref_, mask) =>
+                ctx.gl().stencil_func_separate(face, func, ref_, mask),
+            WebGLCommand::StencilMask(mask) =>
+                ctx.gl().stencil_mask(mask),
+            WebGLCommand::StencilMaskSeparate(face, mask) =>
+                ctx.gl().stencil_mask_separate(face, mask),
+            WebGLCommand::StencilOp(fail, zfail, zpass) =>
+                ctx.gl().stencil_op(fail, zfail, zpass),
+            WebGLCommand::StencilOpSeparate(face, fail, zfail, zpass) =>
+                ctx.gl().stencil_op_separate(face, fail, zfail, zpass),
+            WebGLCommand::GetActiveAttrib(program_id, index, chan) =>
+                Self::active_attrib(ctx.gl(), program_id, index, chan),
+            WebGLCommand::GetActiveUniform(program_id, index, chan) =>
+                Self::active_uniform(ctx.gl(), program_id, index, chan),
+            WebGLCommand::GetAttribLocation(program_id, name, chan) =>
+                Self::attrib_location(ctx.gl(), program_id, name, chan),
+            WebGLCommand::GetVertexAttrib(index, pname, chan) =>
+                Self::vertex_attrib(ctx.gl(), index, pname, chan),
+            WebGLCommand::GetVertexAttribOffset(index, pname, chan) =>
+                Self::vertex_attrib_offset(ctx.gl(), index, pname, chan),
+            WebGLCommand::GetBufferParameter(target, param_id, chan) =>
+                Self::buffer_parameter(ctx.gl(), target, param_id, chan),
+            WebGLCommand::GetParameter(param_id, chan) =>
+                Self::parameter(ctx.gl(), param_id, chan),
+            WebGLCommand::GetProgramParameter(program_id, param_id, chan) =>
+                Self::program_parameter(ctx.gl(), program_id, param_id, chan),
+            WebGLCommand::GetShaderParameter(shader_id, param_id, chan) =>
+                Self::shader_parameter(ctx.gl(), shader_id, param_id, chan),
+            WebGLCommand::GetShaderPrecisionFormat(shader_type, precision_type, chan) =>
+                Self::shader_precision_format(ctx.gl(), shader_type, precision_type, chan),
+            WebGLCommand::GetExtensions(chan) =>
+                Self::get_extensions(ctx.gl(), chan),
+            WebGLCommand::GetUniformLocation(program_id, name, chan) =>
+                Self::uniform_location(ctx.gl(), program_id, name, chan),
+            WebGLCommand::GetShaderInfoLog(shader_id, chan) =>
+                Self::shader_info_log(ctx.gl(), shader_id, chan),
+            WebGLCommand::GetProgramInfoLog(program_id, chan) =>
+                Self::program_info_log(ctx.gl(), program_id, chan),
+            WebGLCommand::CompileShader(shader_id, source) =>
+                Self::compile_shader(ctx.gl(), shader_id, source),
+            WebGLCommand::CreateBuffer(chan) =>
+                Self::create_buffer(ctx.gl(), chan),
+            WebGLCommand::CreateFramebuffer(chan) =>
+                Self::create_framebuffer(ctx.gl(), chan),
+            WebGLCommand::CreateRenderbuffer(chan) =>
+                Self::create_renderbuffer(ctx.gl(), chan),
+            WebGLCommand::CreateTexture(chan) =>
+                Self::create_texture(ctx.gl(), chan),
+            WebGLCommand::CreateProgram(chan) =>
+                Self::create_program(ctx.gl(), chan),
+            WebGLCommand::CreateShader(shader_type, chan) =>
+                Self::create_shader(ctx.gl(), shader_type, chan),
+            WebGLCommand::DeleteBuffer(id) =>
+                ctx.gl().delete_buffers(&[id.get()]),
+            WebGLCommand::DeleteFramebuffer(id) =>
+                ctx.gl().delete_framebuffers(&[id.get()]),
+            WebGLCommand::DeleteRenderbuffer(id) =>
+                ctx.gl().delete_renderbuffers(&[id.get()]),
+            WebGLCommand::DeleteTexture(id) =>
+                ctx.gl().delete_textures(&[id.get()]),
+            WebGLCommand::DeleteProgram(id) =>
+                ctx.gl().delete_program(id.get()),
+            WebGLCommand::DeleteShader(id) =>
+                ctx.gl().delete_shader(id.get()),
+            WebGLCommand::BindBuffer(target, id) =>
+                ctx.gl().bind_buffer(target, id.map_or(0, WebGLBufferId::get)),
+            WebGLCommand::BindFramebuffer(target, request) =>
+                Self::bind_framebuffer(ctx.gl(), target, request, ctx),
+            WebGLCommand::BindRenderbuffer(target, id) =>
+                ctx.gl().bind_renderbuffer(target, id.map_or(0, WebGLRenderbufferId::get)),
+            WebGLCommand::BindTexture(target, id) =>
+                ctx.gl().bind_texture(target, id.map_or(0, WebGLTextureId::get)),
+            WebGLCommand::LinkProgram(program_id) =>
+                ctx.gl().link_program(program_id.get()),
+            WebGLCommand::Uniform1f(uniform_id, v) =>
+                ctx.gl().uniform_1f(uniform_id, v),
+            WebGLCommand::Uniform1fv(uniform_id, v) =>
+                ctx.gl().uniform_1fv(uniform_id, &v),
+            WebGLCommand::Uniform1i(uniform_id, v) =>
+                ctx.gl().uniform_1i(uniform_id, v),
+            WebGLCommand::Uniform1iv(uniform_id, v) =>
+                ctx.gl().uniform_1iv(uniform_id, &v),
+            WebGLCommand::Uniform2f(uniform_id, x, y) =>
+                ctx.gl().uniform_2f(uniform_id, x, y),
+            WebGLCommand::Uniform2fv(uniform_id, v) =>
+                ctx.gl().uniform_2fv(uniform_id, &v),
+            WebGLCommand::Uniform2i(uniform_id, x, y) =>
+                ctx.gl().uniform_2i(uniform_id, x, y),
+            WebGLCommand::Uniform2iv(uniform_id, v) =>
+                ctx.gl().uniform_2iv(uniform_id, &v),
+            WebGLCommand::Uniform3f(uniform_id, x, y, z) =>
+                ctx.gl().uniform_3f(uniform_id, x, y, z),
+            WebGLCommand::Uniform3fv(uniform_id, v) =>
+                ctx.gl().uniform_3fv(uniform_id, &v),
+            WebGLCommand::Uniform3i(uniform_id, x, y, z) =>
+                ctx.gl().uniform_3i(uniform_id, x, y, z),
+            WebGLCommand::Uniform3iv(uniform_id, v) =>
+                ctx.gl().uniform_3iv(uniform_id, &v),
+            WebGLCommand::Uniform4f(uniform_id, x, y, z, w) =>
+                ctx.gl().uniform_4f(uniform_id, x, y, z, w),
+            WebGLCommand::Uniform4fv(uniform_id, v) =>
+                ctx.gl().uniform_4fv(uniform_id, &v),
+            WebGLCommand::Uniform4i(uniform_id, x, y, z, w) =>
+                ctx.gl().uniform_4i(uniform_id, x, y, z, w),
+            WebGLCommand::Uniform4iv(uniform_id, v) =>
+                ctx.gl().uniform_4iv(uniform_id, &v),
+            WebGLCommand::UniformMatrix2fv(uniform_id, transpose,  v) =>
+                ctx.gl().uniform_matrix_2fv(uniform_id, transpose, &v),
+            WebGLCommand::UniformMatrix3fv(uniform_id, transpose,  v) =>
+                ctx.gl().uniform_matrix_3fv(uniform_id, transpose, &v),
+            WebGLCommand::UniformMatrix4fv(uniform_id, transpose,  v) =>
+                ctx.gl().uniform_matrix_4fv(uniform_id, transpose, &v),
+            WebGLCommand::UseProgram(program_id) =>
+                ctx.gl().use_program(program_id.get()),
+            WebGLCommand::ValidateProgram(program_id) =>
+                ctx.gl().validate_program(program_id.get()),
+            WebGLCommand::VertexAttrib(attrib_id, x, y, z, w) =>
+                ctx.gl().vertex_attrib_4f(attrib_id, x, y, z, w),
+            WebGLCommand::VertexAttribPointer2f(attrib_id, size, normalized, stride, offset) =>
+                ctx.gl().vertex_attrib_pointer_f32(attrib_id, size, normalized, stride, offset),
+            WebGLCommand::VertexAttribPointer(attrib_id, size, data_type, normalized, stride, offset) =>
+                ctx.gl().vertex_attrib_pointer(attrib_id, size, data_type, normalized, stride, offset),
+            WebGLCommand::Viewport(x, y, width, height) =>
+                ctx.gl().viewport(x, y, width, height),
+            WebGLCommand::TexImage2D(target, level, internal, width, height, format, data_type, data) =>
+                ctx.gl().tex_image_2d(target, level, internal, width, height, /*border*/0, format, data_type, Some(&data)),
+            WebGLCommand::TexParameteri(target, name, value) =>
+                ctx.gl().tex_parameter_i(target, name, value),
+            WebGLCommand::TexParameterf(target, name, value) =>
+                ctx.gl().tex_parameter_f(target, name, value),
+            WebGLCommand::TexSubImage2D(target, level, xoffset, yoffset, x, y, width, height, data) =>
+                ctx.gl().tex_sub_image_2d(target, level, xoffset, yoffset, x, y, width, height, &data),
+            WebGLCommand::DrawingBufferWidth(sender) =>
+                sender.send(ctx.borrow_draw_buffer().unwrap().size().width).unwrap(),
+            WebGLCommand::DrawingBufferHeight(sender) =>
+                sender.send(ctx.borrow_draw_buffer().unwrap().size().height).unwrap(),
+            WebGLCommand::Finish(sender) =>
+                Self::finish(ctx.gl(), sender),
+            WebGLCommand::Flush =>
+                ctx.gl().flush(),
+            WebGLCommand::GenerateMipmap(target) =>
+                ctx.gl().generate_mipmap(target),
+            WebGLCommand::CreateVertexArray(chan) =>
+                Self::create_vertex_array(ctx.gl(), chan),
+            WebGLCommand::DeleteVertexArray(id) =>
+                ctx.gl().delete_vertex_arrays(&[id.get()]),
+            WebGLCommand::BindVertexArray(id) =>
+                ctx.gl().bind_vertex_array(id.map_or(0, WebGLVertexArrayId::get)),
+            WebGLCommand::FenceAndWaitSync =>
+                Self::fence_and_wait_sync(ctx.gl()),
+        }
+
+        // FIXME: Use debug_assertions once tests are run with them
+        let error = ctx.gl().get_error();
+        assert!(error == gl::NO_ERROR, "Unexpected WebGL error: 0x{:x} ({})", error, error);
+    }
+
+    fn read_pixels(gl: &gl::Gl, x: i32, y: i32, width: i32, height: i32, format: u32, pixel_type: u32,
+                   chan: MsgSender<Vec<u8>>) {
+      let result = gl.read_pixels(x, y, width, height, format, pixel_type);
+      chan.send(result).unwrap()
+    }
+
+    fn active_attrib(gl: &gl::Gl,
+                     program_id: WebGLProgramId,
+                     index: u32,
+                     chan: MsgSender<WebGLResult<(i32, u32, String)>>) {
+        let result = if index >= gl.get_program_iv(program_id.get(), gl::ACTIVE_ATTRIBUTES) as u32 {
+            Err(WebGLError::InvalidValue)
+        } else {
+            Ok(gl.get_active_attrib(program_id.get(), index))
+        };
+        chan.send(result).unwrap();
+    }
+
+    fn active_uniform(gl: &gl::Gl,
+                      program_id: WebGLProgramId,
+                      index: u32,
+                      chan: MsgSender<WebGLResult<(i32, u32, String)>>) {
+        let result = if index >= gl.get_program_iv(program_id.get(), gl::ACTIVE_UNIFORMS) as u32 {
+            Err(WebGLError::InvalidValue)
+        } else {
+            Ok(gl.get_active_uniform(program_id.get(), index))
+        };
+        chan.send(result).unwrap();
+    }
+
+    fn attrib_location(gl: &gl::Gl,
+                       program_id: WebGLProgramId,
+                       name: String,
+                       chan: MsgSender<Option<i32>> ) {
+        let attrib_location = gl.get_attrib_location(program_id.get(), &name);
+
+        let attrib_location = if attrib_location == -1 {
+            None
+        } else {
+            Some(attrib_location)
+        };
+
+        chan.send(attrib_location).unwrap();
+    }
+
+    fn parameter(gl: &gl::Gl,
+                 param_id: u32,
+                 chan: MsgSender<WebGLResult<WebGLParameter>>) {
+        let result = match param_id {
+            gl::ACTIVE_TEXTURE |
+            gl::ALPHA_BITS |
+            gl::BLEND_DST_ALPHA |
+            gl::BLEND_DST_RGB |
+            gl::BLEND_EQUATION_ALPHA |
+            gl::BLEND_EQUATION_RGB |
+            gl::BLEND_SRC_ALPHA |
+            gl::BLEND_SRC_RGB |
+            gl::BLUE_BITS |
+            gl::CULL_FACE_MODE |
+            gl::DEPTH_BITS |
+            gl::DEPTH_FUNC |
+            gl::FRONT_FACE |
+            //gl::GENERATE_MIPMAP_HINT |
+            gl::GREEN_BITS |
+            //gl::IMPLEMENTATION_COLOR_READ_FORMAT |
+            //gl::IMPLEMENTATION_COLOR_READ_TYPE |
+            gl::MAX_COMBINED_TEXTURE_IMAGE_UNITS |
+            gl::MAX_CUBE_MAP_TEXTURE_SIZE |
+            //gl::MAX_FRAGMENT_UNIFORM_VECTORS |
+            gl::MAX_RENDERBUFFER_SIZE |
+            gl::MAX_TEXTURE_IMAGE_UNITS |
+            gl::MAX_TEXTURE_SIZE |
+            //gl::MAX_VARYING_VECTORS |
+            gl::MAX_VERTEX_ATTRIBS |
+            gl::MAX_VERTEX_TEXTURE_IMAGE_UNITS |
+            //gl::MAX_VERTEX_UNIFORM_VECTORS |
+            gl::PACK_ALIGNMENT |
+            gl::RED_BITS |
+            gl::SAMPLE_BUFFERS |
+            gl::SAMPLES |
+            gl::STENCIL_BACK_FAIL |
+            gl::STENCIL_BACK_FUNC |
+            gl::STENCIL_BACK_PASS_DEPTH_FAIL |
+            gl::STENCIL_BACK_PASS_DEPTH_PASS |
+            gl::STENCIL_BACK_REF |
+            gl::STENCIL_BACK_VALUE_MASK |
+            gl::STENCIL_BACK_WRITEMASK |
+            gl::STENCIL_BITS |
+            gl::STENCIL_CLEAR_VALUE |
+            gl::STENCIL_FAIL |
+            gl::STENCIL_FUNC |
+            gl::STENCIL_PASS_DEPTH_FAIL |
+            gl::STENCIL_PASS_DEPTH_PASS |
+            gl::STENCIL_REF |
+            gl::STENCIL_VALUE_MASK |
+            gl::STENCIL_WRITEMASK |
+            gl::SUBPIXEL_BITS |
+            gl::UNPACK_ALIGNMENT =>
+            //gl::UNPACK_COLORSPACE_CONVERSION_WEBGL =>
+                Ok(WebGLParameter::Int(gl.get_integer_v(param_id))),
+
+            gl::BLEND |
+            gl::CULL_FACE |
+            gl::DEPTH_TEST |
+            gl::DEPTH_WRITEMASK |
+            gl::DITHER |
+            gl::POLYGON_OFFSET_FILL |
+            gl::SAMPLE_COVERAGE_INVERT |
+            gl::STENCIL_TEST =>
+            //gl::UNPACK_FLIP_Y_WEBGL |
+            //gl::UNPACK_PREMULTIPLY_ALPHA_WEBGL =>
+                Ok(WebGLParameter::Bool(gl.get_boolean_v(param_id) != 0)),
+
+            gl::DEPTH_CLEAR_VALUE |
+            gl::LINE_WIDTH |
+            gl::POLYGON_OFFSET_FACTOR |
+            gl::POLYGON_OFFSET_UNITS |
+            gl::SAMPLE_COVERAGE_VALUE =>
+                Ok(WebGLParameter::Float(gl.get_float_v(param_id))),
+
+            gl::VERSION => Ok(WebGLParameter::String("WebGL 1.0".to_owned())),
+            gl::RENDERER |
+            gl::VENDOR => Ok(WebGLParameter::String("Mozilla/Servo".to_owned())),
+            gl::SHADING_LANGUAGE_VERSION => Ok(WebGLParameter::String("WebGL GLSL ES 1.0".to_owned())),
+
+            // TODO(zbarsky, emilio): Implement support for the following valid parameters
+            // Float32Array
+            gl::ALIASED_LINE_WIDTH_RANGE |
+            //gl::ALIASED_POINT_SIZE_RANGE |
+            //gl::BLEND_COLOR |
+            gl::COLOR_CLEAR_VALUE |
+            gl::DEPTH_RANGE |
+
+            // WebGLBuffer
+            gl::ARRAY_BUFFER_BINDING |
+            gl::ELEMENT_ARRAY_BUFFER_BINDING |
+
+            // WebGLFrameBuffer
+            gl::FRAMEBUFFER_BINDING |
+
+            // WebGLRenderBuffer
+            gl::RENDERBUFFER_BINDING |
+
+            // WebGLProgram
+            gl::CURRENT_PROGRAM |
+
+            // WebGLTexture
+            gl::TEXTURE_BINDING_2D |
+            gl::TEXTURE_BINDING_CUBE_MAP |
+
+            // sequence<GlBoolean>
+            gl::COLOR_WRITEMASK |
+
+            // Uint32Array
+            gl::COMPRESSED_TEXTURE_FORMATS |
+
+            // Int32Array
+            gl::MAX_VIEWPORT_DIMS |
+            gl::SCISSOR_BOX |
+            gl::VIEWPORT => Err(WebGLError::InvalidEnum),
+
+            // Invalid parameters
+            _ => Err(WebGLError::InvalidEnum)
+        };
+
+        chan.send(result).unwrap();
+    }
+
+    fn finish(gl: &gl::Gl, chan: MsgSender<()>) {
+        gl.finish();
+        chan.send(()).unwrap();
+    }
+
+    fn vertex_attrib(gl: &gl::Gl,
+                     index: u32,
+                     pname: u32,
+                     chan: MsgSender<WebGLResult<WebGLParameter>>) {
+        let result = if index >= gl.get_integer_v(gl::MAX_VERTEX_ATTRIBS) as u32 {
+            Err(WebGLError::InvalidValue)
+        } else {
+            match pname {
+                gl::VERTEX_ATTRIB_ARRAY_ENABLED |
+                gl::VERTEX_ATTRIB_ARRAY_NORMALIZED =>
+                    Ok(WebGLParameter::Bool(gl.get_vertex_attrib_iv(index, pname) != 0)),
+                gl::VERTEX_ATTRIB_ARRAY_SIZE |
+                gl::VERTEX_ATTRIB_ARRAY_STRIDE |
+                gl::VERTEX_ATTRIB_ARRAY_TYPE =>
+                    Ok(WebGLParameter::Int(gl.get_vertex_attrib_iv(index, pname))),
+                gl::CURRENT_VERTEX_ATTRIB =>
+                    Ok(WebGLParameter::FloatArray(gl.get_vertex_attrib_fv(index, pname))),
+                // gl::VERTEX_ATTRIB_ARRAY_BUFFER_BINDING should return WebGLBuffer
+                _ => Err(WebGLError::InvalidEnum),
+            }
+        };
+
+        chan.send(result).unwrap();
+    }
+
+    fn vertex_attrib_offset(gl: &gl::Gl,
+                            index: u32,
+                            pname: u32,
+                            chan: MsgSender<WebGLResult<isize>>) {
+        let result = match pname {
+                gl::VERTEX_ATTRIB_ARRAY_POINTER => Ok(gl.get_vertex_attrib_pointer_v(index, pname)),
+                _ => Err(WebGLError::InvalidEnum),
+        };
+
+        chan.send(result).unwrap();
+    }
+
+    fn buffer_parameter(gl: &gl::Gl,
+                        target: u32,
+                        param_id: u32,
+                        chan: MsgSender<WebGLResult<WebGLParameter>>) {
+        let result = match param_id {
+            gl::BUFFER_SIZE |
+            gl::BUFFER_USAGE =>
+                Ok(WebGLParameter::Int(gl.get_buffer_parameter_iv(target, param_id))),
+            _ => Err(WebGLError::InvalidEnum),
+        };
+
+        chan.send(result).unwrap();
+    }
+
+    fn program_parameter(gl: &gl::Gl,
+                         program_id: WebGLProgramId,
+                         param_id: u32,
+                         chan: MsgSender<WebGLResult<WebGLParameter>>) {
+        let result = match param_id {
+            gl::DELETE_STATUS |
+            gl::LINK_STATUS |
+            gl::VALIDATE_STATUS =>
+                Ok(WebGLParameter::Bool(gl.get_program_iv(program_id.get(), param_id) != 0)),
+            gl::ATTACHED_SHADERS |
+            gl::ACTIVE_ATTRIBUTES |
+            gl::ACTIVE_UNIFORMS =>
+                Ok(WebGLParameter::Int(gl.get_program_iv(program_id.get(), param_id))),
+            _ => Err(WebGLError::InvalidEnum),
+        };
+
+        chan.send(result).unwrap();
+    }
+
+    fn shader_parameter(gl: &gl::Gl,
+                        shader_id: WebGLShaderId,
+                        param_id: u32,
+                        chan: MsgSender<WebGLResult<WebGLParameter>>) {
+        let result = match param_id {
+            gl::SHADER_TYPE =>
+                Ok(WebGLParameter::Int(gl.get_shader_iv(shader_id.get(), param_id))),
+            gl::DELETE_STATUS |
+            gl::COMPILE_STATUS =>
+                Ok(WebGLParameter::Bool(gl.get_shader_iv(shader_id.get(), param_id) != 0)),
+            _ => Err(WebGLError::InvalidEnum),
+        };
+
+        chan.send(result).unwrap();
+    }
+
+    fn shader_precision_format(gl: &gl::Gl,
+                               shader_type: u32,
+                               precision_type: u32,
+                               chan: MsgSender<WebGLResult<(i32, i32, i32)>>) {
+
+        let result = match precision_type {
+            gl::LOW_FLOAT |
+            gl::MEDIUM_FLOAT |
+            gl::HIGH_FLOAT |
+            gl::LOW_INT |
+            gl::MEDIUM_INT |
+            gl::HIGH_INT => {
+                Ok(gl.get_shader_precision_format(shader_type, precision_type))
+            },
+            _=> {
+                Err(WebGLError::InvalidEnum)
+            }
+        };
+
+        chan.send(result).unwrap();
+    }
+
+    fn get_extensions(gl: &gl::Gl, chan: MsgSender<String>) {
+        chan.send(gl.get_string(gl::EXTENSIONS)).unwrap();
+    }
+
+    fn uniform_location(gl: &gl::Gl,
+                        program_id: WebGLProgramId,
+                        name: String,
+                        chan: MsgSender<Option<i32>>) {
+        let location = gl.get_uniform_location(program_id.get(), &name);
+        let location = if location == -1 {
+            None
+        } else {
+            Some(location)
+        };
+
+        chan.send(location).unwrap();
+    }
+
+
+    fn shader_info_log(gl: &gl::Gl, shader_id: WebGLShaderId, chan: MsgSender<String>) {
+        let log = gl.get_shader_info_log(shader_id.get());
+        chan.send(log).unwrap();
+    }
+
+    fn program_info_log(gl: &gl::Gl, program_id: WebGLProgramId, chan: MsgSender<String>) {
+        let log = gl.get_program_info_log(program_id.get());
+        chan.send(log).unwrap();
+    }
+
+    fn create_buffer(gl: &gl::Gl, chan: MsgSender<Option<WebGLBufferId>>) {
+        let buffer = gl.gen_buffers(1)[0];
+        let buffer = if buffer == 0 {
+            None
+        } else {
+            Some(unsafe { WebGLBufferId::new(buffer) })
+        };
+        chan.send(buffer).unwrap();
+    }
+
+    fn create_framebuffer(gl: &gl::Gl, chan: MsgSender<Option<WebGLFramebufferId>>) {
+        let framebuffer = gl.gen_framebuffers(1)[0];
+        let framebuffer = if framebuffer == 0 {
+            None
+        } else {
+            Some(unsafe { WebGLFramebufferId::new(framebuffer) })
+        };
+        chan.send(framebuffer).unwrap();
+    }
+
+
+    fn create_renderbuffer(gl: &gl::Gl, chan: MsgSender<Option<WebGLRenderbufferId>>) {
+        let renderbuffer = gl.gen_renderbuffers(1)[0];
+        let renderbuffer = if renderbuffer == 0 {
+            None
+        } else {
+            Some(unsafe { WebGLRenderbufferId::new(renderbuffer) })
+        };
+        chan.send(renderbuffer).unwrap();
+    }
+
+    fn create_texture(gl: &gl::Gl, chan: MsgSender<Option<WebGLTextureId>>) {
+        let texture = gl.gen_textures(1)[0];
+        let texture = if texture == 0 {
+            None
+        } else {
+            Some(unsafe { WebGLTextureId::new(texture) })
+        };
+        chan.send(texture).unwrap();
+    }
+
+
+    fn create_program(gl: &gl::Gl, chan: MsgSender<Option<WebGLProgramId>>) {
+        let program = gl.create_program();
+        let program = if program == 0 {
+            None
+        } else {
+            Some(unsafe { WebGLProgramId::new(program) })
+        };
+        chan.send(program).unwrap();
+    }
+
+    fn create_shader(gl: &gl::Gl, shader_type: u32, chan: MsgSender<Option<WebGLShaderId>>) {
+        let shader = gl.create_shader(shader_type);
+        let shader = if shader == 0 {
+            None
+        } else {
+            Some(unsafe { WebGLShaderId::new(shader) })
+        };
+        chan.send(shader).unwrap();
+    }
+
+    fn create_vertex_array(gl: &gl::Gl, chan: MsgSender<Option<WebGLVertexArrayId>>) {
+        let vao = gl.gen_vertex_arrays(1)[0];
+        let vao = if vao == 0 {
+            None
+        } else {
+            Some(unsafe { WebGLVertexArrayId::new(vao) })
+        };
+        chan.send(vao).unwrap();
+    }
+
+    #[inline]
+    fn bind_framebuffer<Native: NativeGLContextMethods>(gl: &gl::Gl,
+                                                        target: u32,
+                                                        request: WebGLFramebufferBindingRequest,
+                                                        ctx: &GLContext<Native>) {
+        let id = match request {
+            WebGLFramebufferBindingRequest::Explicit(id) => id.get(),
+            WebGLFramebufferBindingRequest::Default =>
+                ctx.borrow_draw_buffer().unwrap().get_framebuffer(),
+        };
+
+        gl.bind_framebuffer(target, id);
+    }
+
+
+    #[inline]
+    fn compile_shader(gl: &gl::Gl, shader_id: WebGLShaderId, source: String) {
+        gl.shader_source(shader_id.get(), &[source.as_bytes()]);
+        gl.compile_shader(shader_id.get());
+    }
+
+    fn fence_and_wait_sync(gl: &gl::Gl) {
+        // Call FenceSync and ClientWaitSync to ensure that textures are ready.
+        let sync = gl.fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0);
+        // SYNC_FLUSH_COMMANDS_BIT is used to automatically generate a glFlush before blocking on the sync object.
+        gl.wait_sync(sync, gl::SYNC_FLUSH_COMMANDS_BIT, gl::TIMEOUT_IGNORED);
+        // Release GLsync object
+        gl.delete_sync(sync);
+    }
+}
deleted file mode 100644
--- a/gfx/webrender_traits/Cargo.toml
+++ /dev/null
@@ -1,30 +0,0 @@
-[package]
-name = "webrender_traits"
-version = "0.43.0"
-authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
-license = "MPL-2.0"
-repository = "https://github.com/servo/webrender"
-
-[features]
-nightly = ["euclid/unstable", "serde/unstable"]
-ipc = ["ipc-channel"]
-webgl = ["offscreen_gl_context"]
-
-[dependencies]
-app_units = "0.5"
-bincode = "0.8"
-byteorder = "1.0"
-euclid = "0.15"
-gleam = "0.4.5"
-heapsize = ">= 0.3.6, < 0.5"
-ipc-channel = {version = "0.8", optional = true}
-offscreen_gl_context = {version = "0.11", features = ["serde"], optional = true}
-serde = { version = "1.0", features = ["rc", "derive"] }
-time = "0.1"
-
-[target.'cfg(target_os = "macos")'.dependencies]
-core-foundation = "0.3"
-core-graphics = "0.8"
-
-[target.'cfg(target_os = "windows")'.dependencies]
-dwrote = "0.4"
deleted file mode 100644
--- a/gfx/webrender_traits/src/api.rs
+++ /dev/null
@@ -1,601 +0,0 @@
-/* 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 channel::{self, MsgSender, Payload, PayloadSenderHelperMethods, PayloadSender};
-#[cfg(feature = "webgl")]
-use offscreen_gl_context::{GLContextAttributes, GLLimits};
-use std::cell::Cell;
-use std::fmt;
-use std::marker::PhantomData;
-use {BuiltDisplayList, BuiltDisplayListDescriptor, ClipId, ColorF, DeviceIntPoint, DeviceIntSize};
-use {DeviceUintRect, DeviceUintSize, FontKey, GlyphDimensions, GlyphKey};
-use {ImageData, ImageDescriptor, ImageKey, LayoutPoint, LayoutVector2D, LayoutSize, LayoutTransform};
-use {NativeFontHandle, WorldPoint};
-#[cfg(feature = "webgl")]
-use {WebGLCommand, WebGLContextId};
-
-pub type TileSize = u16;
-
-#[derive(Clone, Deserialize, Serialize)]
-pub enum ApiMsg {
-    AddRawFont(FontKey, Vec<u8>, u32),
-    AddNativeFont(FontKey, NativeFontHandle),
-    DeleteFont(FontKey),
-    /// Gets the glyph dimensions
-    GetGlyphDimensions(Vec<GlyphKey>, MsgSender<Vec<Option<GlyphDimensions>>>),
-    /// Adds an image from the resource cache.
-    AddImage(ImageKey, ImageDescriptor, ImageData, Option<TileSize>),
-    /// Updates the the resource cache with the new image data.
-    UpdateImage(ImageKey, ImageDescriptor, ImageData, Option<DeviceUintRect>),
-    /// Drops an image from the resource cache.
-    DeleteImage(ImageKey),
-    CloneApi(MsgSender<IdNamespace>),
-    /// Supplies a new frame to WebRender.
-    ///
-    /// After receiving this message, WebRender will read the display list from the payload channel.
-    // TODO: We should consider using named members to avoid confusion.
-    SetDisplayList(Option<ColorF>,
-                   Epoch,
-                   PipelineId,
-                   LayoutSize, // viewport_size
-                   LayoutSize, // content size
-                   BuiltDisplayListDescriptor,
-                   bool),
-    SetPageZoom(ZoomFactor),
-    SetPinchZoom(ZoomFactor),
-    SetPan(DeviceIntPoint),
-    SetRootPipeline(PipelineId),
-    SetWindowParameters(DeviceUintSize, DeviceUintRect),
-    Scroll(ScrollLocation, WorldPoint, ScrollEventPhase),
-    ScrollNodeWithId(LayoutPoint, ClipId, ScrollClamping),
-    TickScrollingBounce,
-    TranslatePointToLayerSpace(WorldPoint, MsgSender<(LayoutPoint, PipelineId)>),
-    GetScrollNodeState(MsgSender<Vec<ScrollLayerState>>),
-    RequestWebGLContext(DeviceIntSize, GLContextAttributes, MsgSender<Result<(WebGLContextId, GLLimits), String>>),
-    ResizeWebGLContext(WebGLContextId, DeviceIntSize),
-    WebGLCommand(WebGLContextId, WebGLCommand),
-    GenerateFrame(Option<DynamicProperties>),
-    // WebVR commands that must be called in the WebGL render thread.
-    VRCompositorCommand(WebGLContextId, VRCompositorCommand),
-    /// An opaque handle that must be passed to the render notifier. It is used by Gecko
-    /// to forward gecko-specific messages to the render thread preserving the ordering
-    /// within the other messages.
-    ExternalEvent(ExternalEvent),
-    ShutDown,
-}
-
-impl fmt::Debug for ApiMsg {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        f.write_str(match *self {
-            ApiMsg::AddRawFont(..) => "ApiMsg::AddRawFont",
-            ApiMsg::AddNativeFont(..) => "ApiMsg::AddNativeFont",
-            ApiMsg::DeleteFont(..) => "ApiMsg::DeleteFont",
-            ApiMsg::GetGlyphDimensions(..) => "ApiMsg::GetGlyphDimensions",
-            ApiMsg::AddImage(..) => "ApiMsg::AddImage",
-            ApiMsg::UpdateImage(..) => "ApiMsg::UpdateImage",
-            ApiMsg::DeleteImage(..) => "ApiMsg::DeleteImage",
-            ApiMsg::CloneApi(..) => "ApiMsg::CloneApi",
-            ApiMsg::SetDisplayList(..) => "ApiMsg::SetDisplayList",
-            ApiMsg::SetRootPipeline(..) => "ApiMsg::SetRootPipeline",
-            ApiMsg::Scroll(..) => "ApiMsg::Scroll",
-            ApiMsg::ScrollNodeWithId(..) => "ApiMsg::ScrollNodeWithId",
-            ApiMsg::TickScrollingBounce => "ApiMsg::TickScrollingBounce",
-            ApiMsg::TranslatePointToLayerSpace(..) => "ApiMsg::TranslatePointToLayerSpace",
-            ApiMsg::GetScrollNodeState(..) => "ApiMsg::GetScrollNodeState",
-            ApiMsg::RequestWebGLContext(..) => "ApiMsg::RequestWebGLContext",
-            ApiMsg::ResizeWebGLContext(..) => "ApiMsg::ResizeWebGLContext",
-            ApiMsg::WebGLCommand(..) => "ApiMsg::WebGLCommand",
-            ApiMsg::GenerateFrame(..) => "ApiMsg::GenerateFrame",
-            ApiMsg::VRCompositorCommand(..) => "ApiMsg::VRCompositorCommand",
-            ApiMsg::ExternalEvent(..) => "ApiMsg::ExternalEvent",
-            ApiMsg::ShutDown => "ApiMsg::ShutDown",
-            ApiMsg::SetPageZoom(..) => "ApiMsg::SetPageZoom",
-            ApiMsg::SetPinchZoom(..) => "ApiMsg::SetPinchZoom",
-            ApiMsg::SetPan(..) => "ApiMsg::SetPan",
-            ApiMsg::SetWindowParameters(..) => "ApiMsg::SetWindowParameters",
-        })
-    }
-}
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
-pub struct Epoch(pub u32);
-
-#[cfg(not(feature = "webgl"))]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
-pub struct WebGLContextId(pub usize);
-
-#[cfg(not(feature = "webgl"))]
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct GLContextAttributes([u8; 0]);
-
-#[cfg(not(feature = "webgl"))]
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct GLLimits([u8; 0]);
-
-#[cfg(not(feature = "webgl"))]
-#[derive(Clone, Deserialize, Serialize)]
-pub enum WebGLCommand {
-    Flush,
-}
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub struct PipelineId(pub u32, pub u32);
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
-pub struct IdNamespace(pub u32);
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
-pub struct ResourceId(pub u32);
-
-/// An opaque pointer-sized value.
-#[repr(C)]
-#[derive(Clone, Deserialize, Serialize)]
-pub struct ExternalEvent {
-    raw: usize,
-}
-
-unsafe impl Send for ExternalEvent {}
-
-impl ExternalEvent {
-    pub fn from_raw(raw: usize) -> Self { ExternalEvent { raw: raw } }
-    /// Consumes self to make it obvious that the event should be forwarded only once.
-    pub fn unwrap(self) -> usize { self.raw }
-}
-
-#[derive(Clone, Deserialize, Serialize)]
-pub enum ScrollClamping {
-    ToContentBounds,
-    NoClamping,
-}
-
-#[derive(Clone, Deserialize, Serialize)]
-pub struct RenderApiSender {
-    api_sender: MsgSender<ApiMsg>,
-    payload_sender: PayloadSender,
-}
-
-impl RenderApiSender {
-    pub fn new(api_sender: MsgSender<ApiMsg>,
-               payload_sender: PayloadSender)
-               -> RenderApiSender {
-        RenderApiSender {
-            api_sender: api_sender,
-            payload_sender: payload_sender,
-        }
-    }
-
-    pub fn create_api(&self) -> RenderApi {
-        let RenderApiSender {
-            ref api_sender,
-            ref payload_sender
-        } = *self;
-        let (sync_tx, sync_rx) = channel::msg_channel().unwrap();
-        let msg = ApiMsg::CloneApi(sync_tx);
-        api_sender.send(msg).unwrap();
-        RenderApi {
-            api_sender: api_sender.clone(),
-            payload_sender: payload_sender.clone(),
-            id_namespace: sync_rx.recv().unwrap(),
-            next_id: Cell::new(ResourceId(0)),
-        }
-    }
-}
-
-pub struct RenderApi {
-    pub api_sender: MsgSender<ApiMsg>,
-    pub payload_sender: PayloadSender,
-    pub id_namespace: IdNamespace,
-    pub next_id: Cell<ResourceId>,
-}
-
-impl RenderApi {
-    pub fn clone_sender(&self) -> RenderApiSender {
-        RenderApiSender::new(self.api_sender.clone(), self.payload_sender.clone())
-    }
-
-    pub fn generate_font_key(&self) -> FontKey {
-        let new_id = self.next_unique_id();
-        FontKey::new(new_id.0, new_id.1)
-    }
-
-    pub fn add_raw_font(&self, key: FontKey, bytes: Vec<u8>, index: u32) {
-        let msg = ApiMsg::AddRawFont(key, bytes, index);
-        self.api_sender.send(msg).unwrap();
-    }
-
-    pub fn add_native_font(&self, key: FontKey, native_font_handle: NativeFontHandle) {
-        let msg = ApiMsg::AddNativeFont(key, native_font_handle);
-        self.api_sender.send(msg).unwrap();
-    }
-
-    pub fn delete_font(&self, key: FontKey) {
-        let msg = ApiMsg::DeleteFont(key);
-        self.api_sender.send(msg).unwrap();
-    }
-
-    /// Gets the dimensions for the supplied glyph keys
-    ///
-    /// Note: Internally, the internal texture cache doesn't store
-    /// 'empty' textures (height or width = 0)
-    /// This means that glyph dimensions e.g. for spaces (' ') will mostly be None.
-    pub fn get_glyph_dimensions(&self, glyph_keys: Vec<GlyphKey>)
-                                -> Vec<Option<GlyphDimensions>> {
-        let (tx, rx) = channel::msg_channel().unwrap();
-        let msg = ApiMsg::GetGlyphDimensions(glyph_keys, tx);
-        self.api_sender.send(msg).unwrap();
-        rx.recv().unwrap()
-    }
-
-    /// Creates an `ImageKey`.
-    pub fn generate_image_key(&self) -> ImageKey {
-        let new_id = self.next_unique_id();
-        ImageKey::new(new_id.0, new_id.1)
-    }
-
-    /// Adds an image identified by the `ImageKey`.
-    pub fn add_image(&self,
-                     key: ImageKey,
-                     descriptor: ImageDescriptor,
-                     data: ImageData,
-                     tiling: Option<TileSize>) {
-        let msg = ApiMsg::AddImage(key, descriptor, data, tiling);
-        self.api_sender.send(msg).unwrap();
-    }
-
-    /// Updates a specific image.
-    ///
-    /// Currently doesn't support changing dimensions or format by updating.
-    // TODO: Support changing dimensions (and format) during image update?
-    pub fn update_image(&self,
-                        key: ImageKey,
-                        descriptor: ImageDescriptor,
-                        data: ImageData,
-                        dirty_rect: Option<DeviceUintRect>) {
-        let msg = ApiMsg::UpdateImage(key, descriptor, data, dirty_rect);
-        self.api_sender.send(msg).unwrap();
-    }
-
-    /// Deletes the specific image.
-    pub fn delete_image(&self, key: ImageKey) {
-        let msg = ApiMsg::DeleteImage(key);
-        self.api_sender.send(msg).unwrap();
-    }
-
-    /// Sets the root pipeline.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use webrender_traits::{PipelineId, RenderApiSender};
-    /// # fn example(sender: RenderApiSender) {
-    /// let api = sender.create_api();
-    /// // ...
-    /// let pipeline_id = PipelineId(0, 0);
-    /// api.set_root_pipeline(pipeline_id);
-    /// # }
-    /// ```
-    pub fn set_root_pipeline(&self, pipeline_id: PipelineId) {
-        let msg = ApiMsg::SetRootPipeline(pipeline_id);
-        self.api_sender.send(msg).unwrap();
-    }
-
-    /// Supplies a new frame to WebRender.
-    ///
-    /// Non-blocking, it notifies a worker process which processes the display list.
-    /// When it's done and a RenderNotifier has been set in `webrender::renderer::Renderer`,
-    /// [new_frame_ready()][notifier] gets called.
-    ///
-    /// Note: Scrolling doesn't require an own Frame.
-    ///
-    /// Arguments:
-    ///
-    /// * `background_color`: The background color of this pipeline.
-    /// * `epoch`: The unique Frame ID, monotonically increasing.
-    /// * `viewport_size`: The size of the viewport for this frame.
-    /// * `pipeline_id`: The ID of the pipeline that is supplying this display list.
-    /// * `content_size`: The total screen space size of this display list's display items.
-    /// * `display_list`: The root Display list used in this frame.
-    /// * `preserve_frame_state`: If a previous frame exists which matches this pipeline
-    ///                           id, this setting determines if frame state (such as scrolling
-    ///                           position) should be preserved for this new display list.
-    ///
-    /// [notifier]: trait.RenderNotifier.html#tymethod.new_frame_ready
-    pub fn set_display_list(&self,
-                            background_color: Option<ColorF>,
-                            epoch: Epoch,
-                            viewport_size: LayoutSize,
-                            (pipeline_id, content_size, display_list): (PipelineId, LayoutSize, BuiltDisplayList),
-                            preserve_frame_state: bool) {
-        let (display_list_data, display_list_descriptor) = display_list.into_data();
-        let msg = ApiMsg::SetDisplayList(background_color,
-                                         epoch,
-                                         pipeline_id,
-                                         viewport_size,
-                                         content_size,
-                                         display_list_descriptor,
-                                         preserve_frame_state);
-        self.api_sender.send(msg).unwrap();
-
-        self.payload_sender.send_payload(Payload {
-            epoch: epoch,
-            pipeline_id: pipeline_id,
-            display_list_data: display_list_data,
-        }).unwrap();
-    }
-
-    /// Scrolls the scrolling layer under the `cursor`
-    ///
-    /// WebRender looks for the layer closest to the user
-    /// which has `ScrollPolicy::Scrollable` set.
-    pub fn scroll(&self, scroll_location: ScrollLocation, cursor: WorldPoint, phase: ScrollEventPhase) {
-        let msg = ApiMsg::Scroll(scroll_location, cursor, phase);
-        self.api_sender.send(msg).unwrap();
-    }
-
-    pub fn scroll_node_with_id(&self, origin: LayoutPoint, id: ClipId, clamp: ScrollClamping) {
-        let msg = ApiMsg::ScrollNodeWithId(origin, id, clamp);
-        self.api_sender.send(msg).unwrap();
-    }
-
-    pub fn set_page_zoom(&self, page_zoom: ZoomFactor) {
-        let msg = ApiMsg::SetPageZoom(page_zoom);
-        self.api_sender.send(msg).unwrap();
-    }
-
-    pub fn set_pinch_zoom(&self, pinch_zoom: ZoomFactor) {
-        let msg = ApiMsg::SetPinchZoom(pinch_zoom);
-        self.api_sender.send(msg).unwrap();
-    }
-
-    pub fn set_pan(&self, pan: DeviceIntPoint) {
-        let msg = ApiMsg::SetPan(pan);
-        self.api_sender.send(msg).unwrap();
-    }
-
-    pub fn set_window_parameters(&self,
-                                 window_size: DeviceUintSize,
-                                 inner_rect: DeviceUintRect) {
-        let msg = ApiMsg::SetWindowParameters(window_size, inner_rect);
-        self.api_sender.send(msg).unwrap();
-    }
-
-    pub fn tick_scrolling_bounce_animations(&self) {
-        let msg = ApiMsg::TickScrollingBounce;
-        self.api_sender.send(msg).unwrap();
-    }
-
-    /// Translates a point from viewport coordinates to layer space
-    pub fn translate_point_to_layer_space(&self, point: &WorldPoint)
-                                          -> (LayoutPoint, PipelineId) {
-        let (tx, rx) = channel::msg_channel().unwrap();
-        let msg = ApiMsg::TranslatePointToLayerSpace(*point, tx);
-        self.api_sender.send(msg).unwrap();
-        rx.recv().unwrap()
-    }
-
-    pub fn get_scroll_node_state(&self) -> Vec<ScrollLayerState> {
-        let (tx, rx) = channel::msg_channel().unwrap();
-        let msg = ApiMsg::GetScrollNodeState(tx);
-        self.api_sender.send(msg).unwrap();
-        rx.recv().unwrap()
-    }
-
-    pub fn request_webgl_context(&self, size: &DeviceIntSize, attributes: GLContextAttributes)
-                                 -> Result<(WebGLContextId, GLLimits), String> {
-        let (tx, rx) = channel::msg_channel().unwrap();
-        let msg = ApiMsg::RequestWebGLContext(*size, attributes, tx);
-        self.api_sender.send(msg).unwrap();
-        rx.recv().unwrap()
-    }
-
-    pub fn resize_webgl_context(&self, context_id: WebGLContextId, size: &DeviceIntSize) {
-        let msg = ApiMsg::ResizeWebGLContext(context_id, *size);
-        self.api_sender.send(msg).unwrap();
-    }
-
-    pub fn send_webgl_command(&self, context_id: WebGLContextId, command: WebGLCommand) {
-        let msg = ApiMsg::WebGLCommand(context_id, command);
-        self.api_sender.send(msg).unwrap();
-    }
-
-    /// Generate a new frame. Optionally, supply a list of animated
-    /// property bindings that should be used to resolve bindings
-    /// in the current display list.
-    pub fn generate_frame(&self, property_bindings: Option<DynamicProperties>) {
-        let msg = ApiMsg::GenerateFrame(property_bindings);
-        self.api_sender.send(msg).unwrap();
-    }
-
-    pub fn send_vr_compositor_command(&self, context_id: WebGLContextId, command: VRCompositorCommand) {
-        let msg = ApiMsg::VRCompositorCommand(context_id, command);
-        self.api_sender.send(msg).unwrap();
-    }
-
-    pub fn send_external_event(&self, evt: ExternalEvent) {
-        let msg = ApiMsg::ExternalEvent(evt);
-        self.api_sender.send(msg).unwrap();
-    }
-
-    pub fn shut_down(&self) {
-        self.api_sender.send(ApiMsg::ShutDown).unwrap();
-    }
-
-    /// Create a new unique key that can be used for
-    /// animated property bindings.
-    pub fn generate_property_binding_key<T: Copy>(&self) -> PropertyBindingKey<T> {
-        let new_id = self.next_unique_id();
-        PropertyBindingKey {
-            id: PropertyBindingId {
-                namespace: new_id.0,
-                uid: new_id.1,
-            },
-            _phantom: PhantomData,
-        }
-    }
-
-    #[inline]
-    fn next_unique_id(&self) -> (u32, u32) {
-        let IdNamespace(namespace) = self.id_namespace;
-        let ResourceId(id) = self.next_id.get();
-        self.next_id.set(ResourceId(id + 1));
-        (namespace, id)
-    }
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub enum ScrollEventPhase {
-    /// The user started scrolling.
-    Start,
-    /// The user performed a scroll. The Boolean flag indicates whether the user's fingers are
-    /// down, if a touchpad is in use. (If false, the event is a touchpad fling.)
-    Move(bool),
-    /// The user ended scrolling.
-    End,
-}
-
-#[derive(Clone, Deserialize, Serialize)]
-pub struct ScrollLayerState {
-    pub id: ClipId,
-    pub scroll_offset: LayoutVector2D,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
-pub enum ScrollLocation {
-    /// Scroll by a certain amount.
-    Delta(LayoutVector2D),
-    /// Scroll to very top of element.
-    Start,
-    /// Scroll to very bottom of element.
-    End
-}
-
-/// Represents a zoom factor.
-#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
-pub struct ZoomFactor(f32);
-
-impl ZoomFactor {
-    /// Construct a new zoom factor.
-    pub fn new(scale: f32) -> ZoomFactor {
-        ZoomFactor(scale)
-    }
-
-    /// Get the zoom factor as an untyped float.
-    pub fn get(&self) -> f32 {
-        self.0
-    }
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, Eq, Hash)]
-pub struct PropertyBindingId {
-    namespace: u32,
-    uid: u32,
-}
-
-impl PropertyBindingId {
-    pub fn new(value: u64) -> Self {
-        PropertyBindingId {
-            namespace: (value>>32) as u32,
-            uid: value as u32,
-        }
-    }
-}
-
-/// A unique key that is used for connecting animated property
-/// values to bindings in the display list.
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct PropertyBindingKey<T> {
-    pub id: PropertyBindingId,
-    _phantom: PhantomData<T>,
-}
-
-/// Construct a property value from a given key and value.
-impl<T: Copy> PropertyBindingKey<T> {
-    pub fn with(&self, value: T) -> PropertyValue<T> {
-        PropertyValue {
-            key: *self,
-            value: value,
-        }
-    }
-}
-
-impl<T> PropertyBindingKey<T> {
-    pub fn new(value: u64) -> Self {
-        PropertyBindingKey {
-            id: PropertyBindingId::new(value),
-            _phantom: PhantomData,
-        }
-    }
-}
-
-/// A binding property can either be a specific value
-/// (the normal, non-animated case) or point to a binding location
-/// to fetch the current value from.
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub enum PropertyBinding<T> {
-    Value(T),
-    Binding(PropertyBindingKey<T>),
-}
-
-impl<T> From<T> for PropertyBinding<T> {
-    fn from(value: T) -> PropertyBinding<T> {
-        PropertyBinding::Value(value)
-    }
-}
-
-impl<T> From<PropertyBindingKey<T>> for PropertyBinding<T> {
-    fn from(key: PropertyBindingKey<T>) -> PropertyBinding<T> {
-        PropertyBinding::Binding(key)
-    }
-}
-
-/// The current value of an animated property. This is
-/// supplied by the calling code.
-#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
-pub struct PropertyValue<T> {
-    pub key: PropertyBindingKey<T>,
-    pub value: T,
-}
-
-/// When using `generate_frame()`, a list of `PropertyValue` structures
-/// can optionally be supplied to provide the current value of any
-/// animated properties.
-#[derive(Clone, Deserialize, Serialize, Debug)]
-pub struct DynamicProperties {
-    pub transforms: Vec<PropertyValue<LayoutTransform>>,
-    pub floats: Vec<PropertyValue<f32>>,
-}
-
-pub type VRCompositorId = u64;
-
-// WebVR commands that must be called in the WebGL render thread.
-#[derive(Clone, Deserialize, Serialize)]
-pub enum VRCompositorCommand {
-    Create(VRCompositorId),
-    SyncPoses(VRCompositorId, f64, f64, MsgSender<Result<Vec<u8>,()>>),
-    SubmitFrame(VRCompositorId, [f32; 4], [f32; 4]),
-    Release(VRCompositorId)
-}
-
-// Trait object that handles WebVR commands.
-// Receives the texture id and size associated to the WebGLContext.
-pub trait VRCompositorHandler: Send {
-    fn handle(&mut self, command: VRCompositorCommand, texture: Option<(u32, DeviceIntSize)>);
-}
-
-pub trait RenderNotifier: Send {
-    fn new_frame_ready(&mut self);
-    fn new_scroll_frame_ready(&mut self, composite_needed: bool);
-    fn external_event(&mut self, _evt: ExternalEvent) { unimplemented!() }
-    fn shut_down(&mut self) {}
-}
-
-/// Trait to allow dispatching functions to a specific thread or event loop.
-pub trait RenderDispatcher: Send {
-    fn dispatch(&self, Box<Fn() + Send>);
-}
deleted file mode 100644
--- a/gfx/webrender_traits/src/channel.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-/* 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 byteorder::{LittleEndian, WriteBytesExt, ReadBytesExt};
-use std::io::{Cursor, Read};
-use api::{Epoch, PipelineId};
-use std::mem;
-
-#[derive(Clone)]
-pub struct Payload {
-    /// An epoch used to get the proper payload for a pipeline id frame request.
-    ///
-    /// TODO(emilio): Is this still relevant? We send the messages for the same
-    /// pipeline in order, so we shouldn't need it. Seems like this was only
-    /// wallpapering (in most cases) the underlying problem in #991.
-    pub epoch: Epoch,
-    /// A pipeline id to key the payload with, along with the epoch.
-    pub pipeline_id: PipelineId,
-    pub display_list_data: Vec<u8>,
-}
-
-impl Payload {
-    /// Convert the payload to a raw byte vector, in order for it to be
-    /// efficiently shared via shmem, for example.
-    ///
-    /// TODO(emilio, #1049): Consider moving the IPC boundary to the
-    /// constellation in Servo and remove this complexity from WR.
-    pub fn to_data(&self) -> Vec<u8> {
-        let mut data = Vec::with_capacity(mem::size_of::<u32>() +
-                                          2 * mem::size_of::<u32>() +
-                                          mem::size_of::<u64>() +
-                                          self.display_list_data.len());
-        data.write_u32::<LittleEndian>(self.epoch.0).unwrap();
-        data.write_u32::<LittleEndian>(self.pipeline_id.0).unwrap();
-        data.write_u32::<LittleEndian>(self.pipeline_id.1).unwrap();
-        data.write_u64::<LittleEndian>(self.display_list_data.len() as u64).unwrap();
-        data.extend_from_slice(&self.display_list_data);
-        data
-    }
-
-    /// Deserializes the given payload from a raw byte vector.
-    pub fn from_data(data: &[u8]) -> Payload {
-        let mut payload_reader = Cursor::new(data);
-        let epoch = Epoch(payload_reader.read_u32::<LittleEndian>().unwrap());
-        let pipeline_id = PipelineId(payload_reader.read_u32::<LittleEndian>().unwrap(),
-                                     payload_reader.read_u32::<LittleEndian>().unwrap());
-
-        let dl_size = payload_reader.read_u64::<LittleEndian>().unwrap() as usize;
-        let mut built_display_list_data = vec![0; dl_size];
-        payload_reader.read_exact(&mut built_display_list_data[..]).unwrap();
-
-        assert_eq!(payload_reader.position(), data.len() as u64);
-
-        Payload {
-            epoch: epoch,
-            pipeline_id: pipeline_id,
-            display_list_data: built_display_list_data,
-        }
-    }
-}
-
-
-/// A helper to handle the interface difference between `IpcBytesSender`
-/// and `Sender<Vec<u8>>`.
-pub trait PayloadSenderHelperMethods {
-    fn send_payload(&self, data: Payload) -> Result<(), Error>;
-}
-
-pub trait PayloadReceiverHelperMethods {
-    fn recv_payload(&self) -> Result<Payload, Error>;
-}
-
-#[cfg(not(feature = "ipc"))]
-include!("channel_mpsc.rs");
-
-#[cfg(feature = "ipc")]
-include!("channel_ipc.rs");
deleted file mode 100644
--- a/gfx/webrender_traits/src/color.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-/* 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/. */
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct ColorF {
-    pub r: f32,
-    pub g: f32,
-    pub b: f32,
-    pub a: f32,
-}
-known_heap_size!(0, ColorF);
-
-impl ColorF {
-    pub fn premultiplied(&self) -> ColorF {
-        ColorF {
-            r: self.r * self.a,
-            g: self.g * self.a,
-            b: self.b * self.a,
-            a: self.a,
-        }
-    }
-}
-
-#[repr(C)]
-#[derive(Clone, Copy, Hash, Eq, Debug, Deserialize, PartialEq, PartialOrd, Ord, Serialize)]
-pub struct ColorU {
-    pub r: u8,
-    pub g: u8,
-    pub b: u8,
-    pub a: u8,
-}
-
-impl From<ColorF> for ColorU {
-    fn from(color: ColorF) -> ColorU {
-        ColorU {
-            r: ColorU::round_to_int(color.r),
-            g: ColorU::round_to_int(color.g),
-            b: ColorU::round_to_int(color.b),
-            a: ColorU::round_to_int(color.a),
-        }
-    }
-}
-
-impl Into<ColorF> for ColorU {
-    fn into(self) -> ColorF {
-        ColorF {
-            r: self.r as f32 / 255.0,
-            g: self.g as f32 / 255.0,
-            b: self.b as f32 / 255.0,
-            a: self.a as f32 / 255.0,
-        }
-    }
-}
-
-impl ColorU {
-    fn round_to_int(x: f32) -> u8 {
-        debug_assert!((0.0 <= x) && (x <= 1.0));
-        let f = (255.0 * x) + 0.5;
-        let val = f.floor();
-        debug_assert!(val <= 255.0);
-        val as u8
-    }
-
-    pub fn new(r: u8, g: u8, b: u8, a: u8) -> ColorU {
-        ColorU {
-            r: r,
-            g: g,
-            b: b,
-            a: a,
-        }
-    }
-}
-
deleted file mode 100644
--- a/gfx/webrender_traits/src/display_item.rs
+++ /dev/null
@@ -1,654 +0,0 @@
-/* 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::SideOffsets2D;
-use {ColorF, FontKey, ImageKey, ItemRange, PipelineId, WebGLContextId};
-use {LayoutPoint, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D};
-use {PropertyBinding};
-
-// NOTE: some of these structs have an "IMPLICIT" comment.
-// This indicates that the BuiltDisplayList will have serialized
-// a list of values nearby that this item consumes. The traversal
-// iterator should handle finding these.
-
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub struct ClipAndScrollInfo {
-    pub scroll_node_id: ClipId,
-    pub clip_node_id: Option<ClipId>,
-}
-
-impl ClipAndScrollInfo {
-    pub fn simple(node_id: ClipId) -> ClipAndScrollInfo {
-        ClipAndScrollInfo {
-            scroll_node_id: node_id,
-            clip_node_id: None,
-        }
-    }
-
-    pub fn new(scroll_node_id: ClipId, clip_node_id: ClipId) -> ClipAndScrollInfo {
-        ClipAndScrollInfo {
-            scroll_node_id: scroll_node_id,
-            clip_node_id: Some(clip_node_id),
-        }
-    }
-
-    pub fn clip_node_id(&self) -> ClipId {
-        self.clip_node_id.unwrap_or(self.scroll_node_id)
-    }
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct DisplayItem {
-    pub item: SpecificDisplayItem,
-    pub rect: LayoutRect,
-    pub clip_and_scroll: ClipAndScrollInfo,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub enum SpecificDisplayItem {
-    Clip(ClipDisplayItem),
-    Rectangle(RectangleDisplayItem),
-    Text(TextDisplayItem),
-    Image(ImageDisplayItem),
-    YuvImage(YuvImageDisplayItem),
-    WebGL(WebGLDisplayItem),
-    Border(BorderDisplayItem),
-    BoxShadow(BoxShadowDisplayItem),
-    Gradient(GradientDisplayItem),
-    RadialGradient(RadialGradientDisplayItem),
-    Iframe(IframeDisplayItem),
-    PushStackingContext(PushStackingContextDisplayItem),
-    PopStackingContext,
-    SetGradientStops,
-    SetClipRegion(ClipRegion),
-    PushNestedDisplayList,
-    PopNestedDisplayList,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct ClipDisplayItem {
-    pub id: ClipId,
-    pub parent_id: ClipId,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct RectangleDisplayItem {
-    pub color: ColorF,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct TextDisplayItem {
-    pub font_key: FontKey,
-    pub size: Au,
-    pub color: ColorF,
-    pub blur_radius: f32,
-    pub glyph_options: Option<GlyphOptions>,
-} // IMPLICIT: glyphs: Vec<GlyphInstance>
-
-#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
-pub struct GlyphOptions {
-    // These are currently only used on windows for dwrite fonts.
-    pub use_embedded_bitmap: bool,
-    pub force_gdi_rendering: bool,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct WebGLDisplayItem {
-    pub context_id: WebGLContextId,
-}
-
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct NormalBorder {
-    pub left: BorderSide,
-    pub right: BorderSide,
-    pub top: BorderSide,
-    pub bottom: BorderSide,
-    pub radius: BorderRadius,
-}
-
-#[repr(u32)]
-#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
-pub enum RepeatMode {
-    Stretch,
-    Repeat,
-    Round,
-    Space,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct NinePatchDescriptor {
-    pub width: u32,
-    pub height: u32,
-    pub slice: SideOffsets2D<u32>,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct ImageBorder {
-    pub image_key: ImageKey,
-    pub patch: NinePatchDescriptor,
-    pub outset: SideOffsets2D<f32>,
-    pub repeat_horizontal: RepeatMode,
-    pub repeat_vertical: RepeatMode,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct GradientBorder {
-    pub gradient: Gradient,
-    pub outset: SideOffsets2D<f32>,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct RadialGradientBorder {
-    pub gradient: RadialGradient,
-    pub outset: SideOffsets2D<f32>,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub enum BorderDetails {
-    Normal(NormalBorder),
-    Image(ImageBorder),
-    Gradient(GradientBorder),
-    RadialGradient(RadialGradientBorder),
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct BorderDisplayItem {
-    pub widths: BorderWidths,
-    pub details: BorderDetails,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct BorderRadius {
-    pub top_left: LayoutSize,
-    pub top_right: LayoutSize,
-    pub bottom_left: LayoutSize,
-    pub bottom_right: LayoutSize,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct BorderWidths {
-    pub left: f32,
-    pub top: f32,
-    pub right: f32,
-    pub bottom: f32,
-}
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct BorderSide {
-    pub color: ColorF,
-    pub style: BorderStyle,
-}
-
-#[repr(u32)]
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub enum BorderStyle {
-    None    = 0,
-    Solid   = 1,
-    Double  = 2,
-    Dotted  = 3,
-    Dashed  = 4,
-    Hidden  = 5,
-    Groove  = 6,
-    Ridge   = 7,
-    Inset   = 8,
-    Outset  = 9,
-}
-
-#[repr(u32)]
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub enum BoxShadowClipMode {
-    None    = 0,
-    Outset  = 1,
-    Inset   = 2,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct BoxShadowDisplayItem {
-    pub box_bounds: LayoutRect,
-    pub offset: LayoutVector2D,
-    pub color: ColorF,
-    pub blur_radius: f32,
-    pub spread_radius: f32,
-    pub border_radius: f32,
-    pub clip_mode: BoxShadowClipMode,
-}
-
-#[repr(u32)]
-#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Ord, PartialOrd)]
-pub enum ExtendMode {
-    Clamp,
-    Repeat,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct Gradient {
-    pub start_point: LayoutPoint,
-    pub end_point: LayoutPoint,
-    pub extend_mode: ExtendMode,
-} // IMPLICIT: stops: Vec<GradientStop>
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct GradientDisplayItem {
-    pub gradient: Gradient,
-    pub tile_size: LayoutSize,
-    pub tile_spacing: LayoutSize,
-}
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct GradientStop {
-    pub offset: f32,
-    pub color: ColorF,
-}
-known_heap_size!(0, GradientStop);
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct RadialGradient {
-    pub start_center: LayoutPoint,
-    pub start_radius: f32,
-    pub end_center: LayoutPoint,
-    pub end_radius: f32,
-    pub ratio_xy: f32,
-    pub extend_mode: ExtendMode,
-} // IMPLICIT stops: Vec<GradientStop>
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct RadialGradientDisplayItem {
-    pub gradient: RadialGradient,
-    pub tile_size: LayoutSize,
-    pub tile_spacing: LayoutSize,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct PushStackingContextDisplayItem {
-    pub stacking_context: StackingContext,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct StackingContext {
-    pub scroll_policy: ScrollPolicy,
-    pub transform: Option<PropertyBinding<LayoutTransform>>,
-    pub transform_style: TransformStyle,
-    pub perspective: Option<LayoutTransform>,
-    pub mix_blend_mode: MixBlendMode,
-} // IMPLICIT: filters: Vec<FilterOp>
-
-#[repr(u32)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
-pub enum ScrollPolicy {
-    Scrollable  = 0,
-    Fixed       = 1,
-}
-
-known_heap_size!(0, ScrollPolicy);
-
-#[repr(u32)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub enum TransformStyle {
-    Flat        = 0,
-    Preserve3D  = 1,
-}
-
-#[repr(u32)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub enum MixBlendMode {
-    Normal      = 0,
-    Multiply    = 1,
-    Screen      = 2,
-    Overlay     = 3,
-    Darken      = 4,
-    Lighten     = 5,
-    ColorDodge  = 6,
-    ColorBurn   = 7,
-    HardLight   = 8,
-    SoftLight   = 9,
-    Difference  = 10,
-    Exclusion   = 11,
-    Hue         = 12,
-    Saturation  = 13,
-    Color       = 14,
-    Luminosity  = 15,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
-pub enum FilterOp {
-    Blur(Au),
-    Brightness(f32),
-    Contrast(f32),
-    Grayscale(f32),
-    HueRotate(f32),
-    Invert(f32),
-    Opacity(PropertyBinding<f32>),
-    Saturate(f32),
-    Sepia(f32),
-}
-
-impl FilterOp {
-    pub fn is_noop(&self) -> bool {
-        match *self {
-            FilterOp::Blur(length) if length == Au(0) => true,
-            FilterOp::Brightness(amount) if amount == 1.0 => true,
-            FilterOp::Contrast(amount) if amount == 1.0 => true,
-            FilterOp::Grayscale(amount) if amount == 0.0 => true,
-            FilterOp::HueRotate(amount) if amount == 0.0 => true,
-            FilterOp::Invert(amount) if amount == 0.0 => true,
-            FilterOp::Opacity(amount) if amount == PropertyBinding::Value(1.0) => true,
-            FilterOp::Saturate(amount) if amount == 1.0 => true,
-            FilterOp::Sepia(amount) if amount == 0.0 => true,
-            _ => false,
-        }
-    }
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct IframeDisplayItem {
-    pub pipeline_id: PipelineId,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct ImageDisplayItem {
-    pub image_key: ImageKey,
-    pub stretch_size: LayoutSize,
-    pub tile_spacing: LayoutSize,
-    pub image_rendering: ImageRendering,
-}
-
-#[repr(u32)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub enum ImageRendering {
-    Auto        = 0,
-    CrispEdges  = 1,
-    Pixelated   = 2,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct YuvImageDisplayItem {
-    pub yuv_data: YuvData,
-    pub color_space: YuvColorSpace,
-    pub image_rendering: ImageRendering
-}
-
-#[repr(u32)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub enum YuvColorSpace {
-    Rec601 = 0,
-    Rec709 = 1,
-}
-pub const YUV_COLOR_SPACES: [YuvColorSpace; 2] = [YuvColorSpace::Rec601, YuvColorSpace::Rec709];
-
-impl YuvColorSpace {
-    pub fn get_feature_string(&self) -> &'static str {
-        match *self {
-            YuvColorSpace::Rec601 => "YUV_REC601",
-            YuvColorSpace::Rec709 => "YUV_REC709",
-        }
-    }
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub enum YuvData {
-    NV12(ImageKey, ImageKey),   // (Y channel, CbCr interleaved channel)
-    PlanarYCbCr(ImageKey, ImageKey, ImageKey),  // (Y channel, Cb channel, Cr Channel)
-    InterleavedYCbCr(ImageKey), // (YCbCr interleaved channel)
-}
-
-impl YuvData {
-    pub fn get_format(&self) -> YuvFormat {
-        match *self {
-            YuvData::NV12(..) => YuvFormat::NV12,
-            YuvData::PlanarYCbCr(..) => YuvFormat::PlanarYCbCr,
-            YuvData::InterleavedYCbCr(..) => YuvFormat::InterleavedYCbCr,
-        }
-    }
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub enum YuvFormat {
-    NV12 = 0,
-    PlanarYCbCr = 1,
-    InterleavedYCbCr = 2,
-}
-pub const YUV_FORMATS: [YuvFormat; 3] = [YuvFormat::NV12, YuvFormat::PlanarYCbCr, YuvFormat::InterleavedYCbCr];
-
-impl YuvFormat {
-    pub fn get_plane_num(&self) -> usize {
-        match *self {
-            YuvFormat::NV12 => 2,
-            YuvFormat::PlanarYCbCr => 3,
-            YuvFormat::InterleavedYCbCr => 1,
-        }
-    }
-
-    pub fn get_feature_string(&self) -> &'static str {
-        match *self {
-            YuvFormat::NV12 => "NV12",
-            YuvFormat::PlanarYCbCr => "",
-            YuvFormat::InterleavedYCbCr => "INTERLEAVED_Y_CB_CR"
-        }
-    }
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct ImageMask {
-    pub image: ImageKey,
-    pub rect: LayoutRect,
-    pub repeat: bool,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct ClipRegion {
-    pub main: LayoutRect,
-    pub image_mask: Option<ImageMask>,
-    #[serde(default, skip_serializing, skip_deserializing)]
-    pub complex_clips: ItemRange<ComplexClipRegion>,
-    #[serde(default, skip_serializing, skip_deserializing)]
-    pub complex_clip_count: usize,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct ComplexClipRegion {
-    /// The boundaries of the rectangle.
-    pub rect: LayoutRect,
-    /// Border radii of this rectangle.
-    pub radii: BorderRadius,
-}
-
-impl BorderRadius {
-    pub fn zero() -> BorderRadius {
-        BorderRadius {
-            top_left: LayoutSize::new(0.0, 0.0),
-            top_right: LayoutSize::new(0.0, 0.0),
-            bottom_left: LayoutSize::new(0.0, 0.0),
-            bottom_right: LayoutSize::new(0.0, 0.0),
-        }
-    }
-
-    pub fn uniform(radius: f32) -> BorderRadius {
-        BorderRadius {
-            top_left: LayoutSize::new(radius, radius),
-            top_right: LayoutSize::new(radius, radius),
-            bottom_left: LayoutSize::new(radius, radius),
-            bottom_right: LayoutSize::new(radius, radius),
-        }
-    }
-
-    pub fn uniform_size(radius: LayoutSize) -> BorderRadius {
-        BorderRadius {
-            top_left: radius,
-            top_right: radius,
-            bottom_left: radius,
-            bottom_right: radius,
-        }
-    }
-
-    pub fn is_uniform(&self) -> Option<f32> {
-        match self.is_uniform_size() {
-            Some(radius) if radius.width == radius.height => Some(radius.width),
-            _ => None
-        }
-    }
-
-    pub fn is_uniform_size(&self) -> Option<LayoutSize> {
-        let uniform_radius = self.top_left;
-        if self.top_right == uniform_radius &&
-           self.bottom_left == uniform_radius &&
-           self.bottom_right == uniform_radius {
-            Some(uniform_radius)
-        } else {
-            None
-        }
-    }
-
-    pub fn is_zero(&self) -> bool {
-        if let Some(radius) = self.is_uniform() {
-            radius == 0.0
-        } else {
-            false
-        }
-    }
-}
-
-impl ClipRegion {
-    pub fn new(rect: &LayoutRect,
-               image_mask: Option<ImageMask>)
-               -> ClipRegion {
-        ClipRegion {
-            main: *rect,
-            image_mask: image_mask,
-            complex_clips: ItemRange::default(),
-            complex_clip_count: 0,
-        }
-    }
-
-    pub fn simple(rect: &LayoutRect) -> ClipRegion {
-        ClipRegion {
-            main: *rect,
-            image_mask: None,
-            complex_clips: ItemRange::default(),
-            complex_clip_count: 0,
-        }
-    }
-
-    pub fn empty() -> ClipRegion {
-        ClipRegion {
-            main: LayoutRect::zero(),
-            image_mask: None,
-            complex_clips: ItemRange::default(),
-            complex_clip_count: 0,
-        }
-    }
-
-    pub fn is_complex(&self) -> bool {
-        self.complex_clip_count != 0 || self.image_mask.is_some()
-    }
-}
-
-impl ColorF {
-    pub fn new(r: f32, g: f32, b: f32, a: f32) -> ColorF {
-        ColorF {
-            r: r,
-            g: g,
-            b: b,
-            a: a,
-        }
-    }
-
-    pub fn scale_rgb(&self, scale: f32) -> ColorF {
-        ColorF {
-            r: self.r * scale,
-            g: self.g * scale,
-            b: self.b * scale,
-            a: self.a,
-        }
-    }
-
-    pub fn to_array(&self) -> [f32; 4] {
-        [self.r, self.g, self.b, self.a]
-    }
-}
-
-impl ComplexClipRegion {
-    /// Create a new complex clip region.
-    pub fn new(rect: LayoutRect, radii: BorderRadius) -> ComplexClipRegion {
-        ComplexClipRegion {
-            rect: rect,
-            radii: radii,
-        }
-    }
-}
-
-pub type NestingIndex = u64;
-
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub enum ClipId {
-    Clip(u64, NestingIndex, PipelineId),
-    ClipExternalId(u64, PipelineId),
-    DynamicallyAddedNode(u64, PipelineId),
-}
-
-impl ClipId {
-    pub fn root_scroll_node(pipeline_id: PipelineId) -> ClipId {
-        ClipId::Clip(0, 0, pipeline_id)
-    }
-
-    pub fn root_reference_frame(pipeline_id: PipelineId) -> ClipId {
-        ClipId::DynamicallyAddedNode(0, pipeline_id)
-    }
-
-    pub fn new(id: u64, pipeline_id: PipelineId) -> ClipId {
-        // We do this because it is very easy to create accidentally create something that
-        // seems like a root scroll node, but isn't one.
-        if id == 0 {
-            return ClipId::root_scroll_node(pipeline_id);
-        }
-
-        ClipId::ClipExternalId(id, pipeline_id)
-    }
-
-    pub fn pipeline_id(&self) -> PipelineId {
-        match *self {
-            ClipId::Clip(_, _, pipeline_id) |
-            ClipId::ClipExternalId(_, pipeline_id) |
-            ClipId::DynamicallyAddedNode(_, pipeline_id) => pipeline_id,
-        }
-    }
-
-    pub fn external_id(&self) -> Option<u64> {
-        match *self {
-            ClipId::ClipExternalId(id, _) => Some(id),
-            _ => None,
-        }
-    }
-
-    pub fn is_root_scroll_node(&self) -> bool {
-        match *self {
-            ClipId::Clip(0, 0, _)  => true,
-            _ => false,
-        }
-    }
-
-    pub fn is_nested(&self) -> bool {
-        match *self {
-            ClipId::Clip(_, nesting_level, _) => nesting_level != 0,
-            _ => false,
-        }
-    }
-}
-
-macro_rules! define_empty_heap_size_of {
-    ($name:ident) => {
-        impl ::heapsize::HeapSizeOf for $name {
-            fn heap_size_of_children(&self) -> usize { 0 }
-        }
-    }
-}
-
-define_empty_heap_size_of!(ClipId);
-define_empty_heap_size_of!(RepeatMode);
-define_empty_heap_size_of!(ImageKey);
-define_empty_heap_size_of!(MixBlendMode);
-define_empty_heap_size_of!(TransformStyle);
deleted file mode 100644
--- a/gfx/webrender_traits/src/display_list.rs
+++ /dev/null
@@ -1,957 +0,0 @@
-/* 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 bincode;
-use serde::{Deserialize, Serialize, Serializer};
-use serde::ser::{SerializeSeq, SerializeMap};
-use time::precise_time_ns;
-use {BorderDetails, BorderDisplayItem, BorderWidths, BoxShadowClipMode, BoxShadowDisplayItem};
-use {ClipAndScrollInfo, ClipDisplayItem, ClipId, ClipRegion, ColorF, ComplexClipRegion};
-use {DisplayItem, ExtendMode, FilterOp, FontKey, GlyphInstance, GlyphOptions, Gradient};
-use {GradientDisplayItem, GradientStop, IframeDisplayItem, ImageDisplayItem, ImageKey, ImageMask};
-use {ImageRendering, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform, MixBlendMode};
-use {PipelineId, PropertyBinding, PushStackingContextDisplayItem, RadialGradient};
-use {RadialGradientDisplayItem, RectangleDisplayItem, ScrollPolicy, SpecificDisplayItem};
-use {StackingContext, TextDisplayItem, TransformStyle, WebGLContextId, WebGLDisplayItem};
-use {YuvColorSpace, YuvData, YuvImageDisplayItem, LayoutVector2D};
-use std::marker::PhantomData;
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub struct ItemRange<T> {
-    start: usize,
-    length: usize,
-    _boo: PhantomData<T>,
-}
-
-impl<T> Default for ItemRange<T> {
-    fn default() -> Self {
-        ItemRange { start: 0, length: 0, _boo: PhantomData }
-    }
-}
-
-impl<T> ItemRange<T> {
-    pub fn is_empty(&self) -> bool {
-        // Nothing more than space for a length (0).
-        self.length <= ::std::mem::size_of::<u64>()
-    }
-}
-
-/// A display list.
-#[derive(Clone, Default)]
-pub struct BuiltDisplayList {
-    /// Serde encoded bytes. Mostly DisplayItems, but some mixed in slices.
-    data: Vec<u8>,
-    descriptor: BuiltDisplayListDescriptor,
-}
-
-/// Describes the memory layout of a display list.
-///
-/// A display list consists of some number of display list items, followed by a number of display
-/// items.
-#[repr(C)]
-#[derive(Copy, Clone, Default, Deserialize, Serialize)]
-pub struct BuiltDisplayListDescriptor {
-    /// The first IPC time stamp: before any work has been done
-    builder_start_time: u64,
-    /// The second IPC time stamp: after serialization
-    builder_finish_time: u64,
-}
-
-pub struct BuiltDisplayListIter<'a> {
-    list: &'a BuiltDisplayList,
-    data: &'a [u8],
-    cur_item: DisplayItem,
-    cur_stops: ItemRange<GradientStop>,
-    cur_glyphs: ItemRange<GlyphInstance>,
-    cur_filters: ItemRange<FilterOp>,
-    cur_clip: ClipRegion,
-    peeking: Peek,
-}
-
-pub struct DisplayItemRef<'a: 'b, 'b> {
-    iter: &'b BuiltDisplayListIter<'a>,
-}
-
-#[derive(PartialEq)]
-enum Peek {
-    StartPeeking,
-    IsPeeking,
-    NotPeeking,
-}
-
-#[derive(Clone)]
-pub struct AuxIter<'a, T> {
-    data: &'a [u8],
-    size: usize,
-    _boo: PhantomData<T>,
-}
-
-impl BuiltDisplayListDescriptor {
-}
-
-impl BuiltDisplayList {
-    pub fn from_data(data: Vec<u8>, descriptor: BuiltDisplayListDescriptor) -> BuiltDisplayList {
-        BuiltDisplayList {
-            data: data,
-            descriptor: descriptor,
-        }
-    }
-
-    pub fn into_data(self) -> (Vec<u8>, BuiltDisplayListDescriptor) {
-        (self.data, self.descriptor)
-    }
-
-    pub fn data(&self) -> &[u8] {
-        &self.data[..]
-    }
-
-    pub fn descriptor(&self) -> &BuiltDisplayListDescriptor {
-        &self.descriptor
-    }
-
-    pub fn times(&self) -> (u64, u64) {
-      (self.descriptor.builder_start_time, self.descriptor.builder_finish_time)
-    }
-
-    pub fn iter(&self) -> BuiltDisplayListIter {
-        BuiltDisplayListIter::new(self)
-    }
-
-    pub fn get<'de, T: Deserialize<'de>>(&self, range: ItemRange<T>) -> AuxIter<T> {
-        AuxIter::new(&self.data[range.start .. range.start + range.length])
-    }
-}
-
-impl<'a> BuiltDisplayListIter<'a> {
-    pub fn new(list: &'a BuiltDisplayList) -> Self {
-        BuiltDisplayListIter {
-            list: list,
-            data: &list.data,
-            // Dummy data, will be overwritten by `next`
-            cur_item: DisplayItem {
-                item: SpecificDisplayItem::PopStackingContext,
-                rect: LayoutRect::zero(),
-                clip_and_scroll: ClipAndScrollInfo::simple(ClipId::new(0, PipelineId(0, 0))),
-            },
-            cur_stops: ItemRange::default(),
-            cur_glyphs: ItemRange::default(),
-            cur_filters: ItemRange::default(),
-            cur_clip: ClipRegion::empty(),
-            peeking: Peek::NotPeeking,
-        }
-    }
-
-    pub fn display_list(&self) -> &'a BuiltDisplayList {
-        self.list
-    }
-
-    pub fn next<'b>(&'b mut self) -> Option<DisplayItemRef<'a, 'b>> {
-        use SpecificDisplayItem::*;
-
-        match self.peeking {
-            Peek::IsPeeking => {
-                self.peeking = Peek::NotPeeking;
-                return Some(self.as_ref())
-            }
-            Peek::StartPeeking => {
-                self.peeking = Peek::IsPeeking;
-            }
-            Peek::NotPeeking => { /* do nothing */ }
-        }
-
-        // Don't let these bleed into another item
-        self.cur_stops = ItemRange::default();
-        self.cur_clip = ClipRegion::empty();
-
-        loop {
-            if self.data.len() == 0 {
-                return None
-            }
-
-            self.cur_item = bincode::deserialize_from(&mut self.data, bincode::Infinite)
-                                    .expect("MEH: malicious process?");
-
-            match self.cur_item.item {
-                SetClipRegion(clip) => {
-                    self.cur_clip = clip;
-                    let (clip_range, clip_count) = self.skip_slice::<ComplexClipRegion>();
-                    self.cur_clip.complex_clip_count = clip_count;
-                    self.cur_clip.complex_clips = clip_range;
-
-                    // This is a dummy item, skip over it
-                    continue;
-                }
-                SetGradientStops => {
-                    self.cur_stops = self.skip_slice::<GradientStop>().0;
-
-                    // This is a dummy item, skip over it
-                    continue;
-                }
-                Text(_) => {
-                    self.cur_glyphs = self.skip_slice::<GlyphInstance>().0;
-                }
-                PushStackingContext(_) => {
-                    self.cur_filters = self.skip_slice::<FilterOp>().0;
-                }
-                _ => { /* do nothing */ }
-            }
-
-            break;
-        }
-
-        Some(self.as_ref())
-    }
-
-    /// Returns the byte-range the slice occupied, and the number of elements
-    /// in the slice.
-    fn skip_slice<T: for<'de> Deserialize<'de>>(&mut self) -> (ItemRange<T>, usize) {
-        let base = self.list.data.as_ptr() as usize;
-        let start = self.data.as_ptr() as usize;
-
-        // Read through the values (this is a bit of a hack to reuse logic)
-        let mut iter = AuxIter::<T>::new(self.data);
-        let count = iter.len();
-        for _ in &mut iter {}
-        let end = iter.data.as_ptr() as usize;
-
-        let range = ItemRange { start: start - base, length: end - start, _boo: PhantomData };
-
-        // Adjust data pointer to skip read values
-        self.data = &self.data[range.length..];
-        (range, count)
-    }
-
-    pub fn as_ref<'b>(&'b self) -> DisplayItemRef<'a, 'b> {
-        DisplayItemRef { iter: self }
-    }
-
-    pub fn starting_stacking_context(&mut self)
-        -> Option<(StackingContext, LayoutRect, ItemRange<FilterOp>)> {
-
-        self.next().and_then(|item| match *item.item() {
-            SpecificDisplayItem::PushStackingContext(ref specific_item) => {
-                Some((specific_item.stacking_context, item.rect(), item.filters()))
-            },
-            _ => None,
-        })
-    }
-
-    pub fn skip_current_stacking_context(&mut self) {
-        let mut depth = 0;
-        while let Some(item) = self.next() {
-            match *item.item() {
-                SpecificDisplayItem::PushStackingContext(..) => depth += 1,
-                SpecificDisplayItem::PopStackingContext if depth == 0 => return,
-                SpecificDisplayItem::PopStackingContext => depth -= 1,
-                _ => {}
-            }
-            debug_assert!(depth >= 0);
-        }
-    }
-
-    pub fn current_stacking_context_empty(&mut self) -> bool {
-        match self.peek() {
-            Some(item) => *item.item() == SpecificDisplayItem::PopStackingContext,
-            None => true,
-        }
-    }
-
-    pub fn peek<'b>(&'b mut self) -> Option<DisplayItemRef<'a, 'b>> {
-        if self.peeking == Peek::NotPeeking {
-            self.peeking = Peek::StartPeeking;
-            self.next()
-        } else {
-            Some(self.as_ref())
-        }
-    }
-}
-
-// Some of these might just become ItemRanges
-impl<'a, 'b> DisplayItemRef<'a, 'b> {
-    pub fn display_item(&self) -> &DisplayItem {
-        &self.iter.cur_item
-    }
-
-    pub fn rect(&self) -> LayoutRect {
-        self.iter.cur_item.rect
-    }
-
-    pub fn clip_and_scroll(&self) -> ClipAndScrollInfo {
-        self.iter.cur_item.clip_and_scroll
-    }
-
-    pub fn item(&self) -> &SpecificDisplayItem {
-        &self.iter.cur_item.item
-    }
-
-    pub fn clip_region(&self) -> &ClipRegion {
-        &self.iter.cur_clip
-    }
-
-    pub fn gradient_stops(&self) -> ItemRange<GradientStop> {
-        self.iter.cur_stops
-    }
-
-    pub fn glyphs(&self) -> ItemRange<GlyphInstance> {
-        self.iter.cur_glyphs
-    }
-
-    pub fn filters(&self) -> ItemRange<FilterOp> {
-        self.iter.cur_filters
-    }
-
-    pub fn display_list(&self) -> &BuiltDisplayList {
-        self.iter.display_list()
-    }
-
-    // Creates a new iterator where this element's iterator
-    // is, to hack around borrowck.
-    pub fn sub_iter(&self) -> BuiltDisplayListIter<'a> {
-        BuiltDisplayListIter {
-            list: self.iter.list,
-            data: self.iter.data,
-            // Dummy data, will be overwritten by `next`
-            cur_item: DisplayItem {
-                item: SpecificDisplayItem::PopStackingContext,
-                rect: LayoutRect::zero(),
-                clip_and_scroll: ClipAndScrollInfo::simple(ClipId::new(0, PipelineId(0, 0))),
-            },
-            cur_stops: ItemRange::default(),
-            cur_glyphs: ItemRange::default(),
-            cur_filters: ItemRange::default(),
-            cur_clip: ClipRegion::empty(),
-            peeking: Peek::NotPeeking,
-        }
-    }
-}
-
-impl<'de, 'a, T: Deserialize<'de>> AuxIter<'a, T> {
-    pub fn new(mut data: &'a [u8]) -> Self {
-
-        let size: usize = if data.len() == 0 {
-            0   // Accept empty ItemRanges pointing anywhere
-        } else {
-            bincode::deserialize_from(&mut data, bincode::Infinite)
-                                  .expect("MEH: malicious input?")
-        };
-
-        AuxIter {
-            data: data,
-            size: size,
-            _boo: PhantomData,
-        }
-    }
-}
-
-impl<'a, T: for<'de> Deserialize<'de>> Iterator for AuxIter<'a, T> {
-    type Item = T;
-
-    fn next(&mut self) -> Option<T> {
-        if self.size == 0 {
-            None
-        } else {
-            self.size -= 1;
-            Some(bincode::deserialize_from(&mut self.data, bincode::Infinite)
-                         .expect("MEH: malicious input?"))
-        }
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        (self.size, Some(self.size))
-    }
-}
-
-impl<'a, T: for<'de> Deserialize<'de>> ::std::iter::ExactSizeIterator for AuxIter<'a, T> { }
-
-
-// This is purely for the JSON writer in wrench
-impl Serialize for BuiltDisplayList {
-    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
-        let mut seq = serializer.serialize_seq(None)?;
-        let mut traversal = self.iter();
-        while let Some(item) = traversal.next() {
-            seq.serialize_element(&item)?
-        }
-        seq.end()
-    }
-}
-
-impl<'a, 'b> Serialize for DisplayItemRef<'a, 'b> {
-    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
-        let mut map = serializer.serialize_map(None)?;
-
-        map.serialize_entry("item", self.display_item())?;
-
-        match *self.item() {
-            SpecificDisplayItem::Text(_) => {
-                map.serialize_entry("glyphs",
-                    &self.iter.list.get(self.glyphs()).collect::<Vec<_>>())?;
-            }
-            SpecificDisplayItem::PushStackingContext(_) => {
-                map.serialize_entry("filters",
-                    &self.iter.list.get(self.filters()).collect::<Vec<_>>())?;
-            }
-            _ => { }
-        }
-
-        let clip_region = self.clip_region();
-        let gradient_stops = self.gradient_stops();
-
-        map.serialize_entry("clip_region", &clip_region)?;
-        if !clip_region.complex_clips.is_empty() {
-            map.serialize_entry("complex_clips",
-                &self.iter.list.get(clip_region.complex_clips).collect::<Vec<_>>())?;
-        }
-
-        if !gradient_stops.is_empty() {
-            map.serialize_entry("gradient_stops",
-                &self.iter.list.get(gradient_stops).collect::<Vec<_>>())?;
-        }
-
-        map.end()
-    }
-}
-
-#[derive(Clone)]
-pub struct DisplayListBuilder {
-    pub data: Vec<u8>,
-    pub pipeline_id: PipelineId,
-    clip_stack: Vec<ClipAndScrollInfo>,
-    next_clip_id: u64,
-    builder_start_time: u64,
-
-    /// The size of the content of this display list. This is used to allow scrolling
-    /// outside the bounds of the display list items themselves.
-    content_size: LayoutSize,
-}
-
-impl DisplayListBuilder {
-    pub fn new(pipeline_id: PipelineId, content_size: LayoutSize) -> DisplayListBuilder {
-        Self::with_capacity(pipeline_id, content_size, 0)
-    }
-
-    pub fn with_capacity(pipeline_id: PipelineId,
-                         content_size: LayoutSize,
-                         capacity: usize) -> DisplayListBuilder {
-        let start_time = precise_time_ns();
-
-        // We start at 1 here, because the root scroll id is always 0.
-        const FIRST_CLIP_ID : u64 = 1;
-
-        DisplayListBuilder {
-            data: Vec::with_capacity(capacity),
-            pipeline_id: pipeline_id,
-            clip_stack: vec![ClipAndScrollInfo::simple(ClipId::root_scroll_node(pipeline_id))],
-            next_clip_id: FIRST_CLIP_ID,
-            builder_start_time: start_time,
-            content_size: content_size,
-        }
-    }
-
-    pub fn print_display_list(&mut self) {
-        let mut temp = BuiltDisplayList::default();
-        ::std::mem::swap(&mut temp.data, &mut self.data);
-
-        {
-            let mut iter = BuiltDisplayListIter::new(&temp);
-            while let Some(item) = iter.next() {
-                println!("{:?}", item.display_item());
-            }
-        }
-
-        self.data = temp.data;
-    }
-
-    fn push_item(&mut self, item: SpecificDisplayItem, rect: LayoutRect) {
-        bincode::serialize_into(&mut self.data, &DisplayItem {
-            item: item,
-            rect: rect,
-            clip_and_scroll: *self.clip_stack.last().unwrap(),
-        }, bincode::Infinite).unwrap();
-    }
-
-    fn push_new_empty_item(&mut self, item: SpecificDisplayItem) {
-        bincode::serialize_into(&mut self.data, &DisplayItem {
-            item: item,
-            rect: LayoutRect::zero(),
-            clip_and_scroll: *self.clip_stack.last().unwrap(),
-        }, bincode::Infinite).unwrap();
-    }
-
-    fn push_iter<I>(&mut self, iter: I)
-    where I: IntoIterator,
-          I::IntoIter: ExactSizeIterator,
-          I::Item: Serialize,
-    {
-        let iter = iter.into_iter();
-        let len = iter.len();
-        let mut count = 0;
-
-        bincode::serialize_into(&mut self.data, &len, bincode::Infinite).unwrap();
-        for elem in iter {
-            count += 1;
-            bincode::serialize_into(&mut self.data, &elem, bincode::Infinite).unwrap();
-        }
-
-        debug_assert_eq!(len, count);
-    }
-
-    pub fn push_rect(&mut self,
-                     rect: LayoutRect,
-                     _token: ClipRegionToken,
-                     color: ColorF) {
-        let item = SpecificDisplayItem::Rectangle(RectangleDisplayItem {
-            color: color,
-        });
-
-        self.push_item(item, rect);
-    }
-
-    pub fn push_image(&mut self,
-                      rect: LayoutRect,
-                      _token: ClipRegionToken,
-                      stretch_size: LayoutSize,
-                      tile_spacing: LayoutSize,
-                      image_rendering: ImageRendering,
-                      key: ImageKey) {
-        let item = SpecificDisplayItem::Image(ImageDisplayItem {
-            image_key: key,
-            stretch_size: stretch_size,
-            tile_spacing: tile_spacing,
-            image_rendering: image_rendering,
-        });
-
-        self.push_item(item, rect);
-    }
-
-    /// Push a yuv image. All planar data in yuv image should use the same buffer type.
-    pub fn push_yuv_image(&mut self,
-                          rect: LayoutRect,
-                          _token: ClipRegionToken,
-                          yuv_data: YuvData,
-                          color_space: YuvColorSpace,
-                          image_rendering: ImageRendering) {
-        let item = SpecificDisplayItem::YuvImage(YuvImageDisplayItem {
-            yuv_data: yuv_data,
-            color_space: color_space,
-            image_rendering: image_rendering,
-        });
-        self.push_item(item, rect);
-    }
-
-    pub fn push_webgl_canvas(&mut self,
-                             rect: LayoutRect,
-                             _token: ClipRegionToken,
-                             context_id: WebGLContextId) {
-        let item = SpecificDisplayItem::WebGL(WebGLDisplayItem {
-            context_id: context_id,
-        });
-        self.push_item(item, rect);
-    }
-
-    pub fn push_text(&mut self,
-                     rect: LayoutRect,
-                     _token: ClipRegionToken,
-                     glyphs: &[GlyphInstance],
-                     font_key: FontKey,
-                     color: ColorF,
-                     size: Au,
-                     blur_radius: f32,
-                     glyph_options: Option<GlyphOptions>) {
-        // Sanity check - anything with glyphs bigger than this
-        // is probably going to consume too much memory to render
-        // efficiently anyway. This is specifically to work around
-        // the font_advance.html reftest, which creates a very large
-        // font as a crash test - the rendering is also ignored
-        // by the azure renderer.
-        if size < Au::from_px(4096) {
-            let item = SpecificDisplayItem::Text(TextDisplayItem {
-                color: color,
-                font_key: font_key,
-                size: size,
-                blur_radius: blur_radius,
-                glyph_options: glyph_options,
-            });
-
-            self.push_item(item, rect);
-            self.push_iter(glyphs);
-        }
-    }
-
-    // Gradients can be defined with stops outside the range of [0, 1]
-    // when this happens the gradient needs to be normalized by adjusting
-    // the gradient stops and gradient line into an equivalent gradient
-    // with stops in the range [0, 1]. this is done by moving the beginning
-    // of the gradient line to where stop[0] and the end of the gradient line
-    // to stop[n-1]. this function adjusts the stops in place, and returns
-    // the amount to adjust the gradient line start and stop
-    fn normalize_stops(stops: &mut Vec<GradientStop>,
-                       extend_mode: ExtendMode) -> (f32, f32) {
-        assert!(stops.len() >= 2);
-
-        let first = *stops.first().unwrap();
-        let last = *stops.last().unwrap();
-
-        assert!(first.offset <= last.offset);
-
-        let stops_origin = first.offset;
-        let stops_delta = last.offset - first.offset;
-
-        if stops_delta > 0.000001 {
-            for stop in stops {
-                stop.offset = (stop.offset - stops_origin) / stops_delta;
-            }
-
-            (first.offset, last.offset)
-        } else {
-            // We have a degenerate gradient and can't accurately transform the stops
-            // what happens here depends on the repeat behavior, but in any case
-            // we reconstruct the gradient stops to something simpler and equivalent
-            stops.clear();
-
-            match extend_mode {
-                ExtendMode::Clamp => {
-                    // This gradient is two colors split at the offset of the stops,
-                    // so create a gradient with two colors split at 0.5 and adjust
-                    // the gradient line so 0.5 is at the offset of the stops
-                    stops.push(GradientStop {
-                        color: first.color,
-                        offset: 0.0,
-                    });
-                    stops.push(GradientStop {
-                        color: first.color,
-                        offset: 0.5,
-                    });
-                    stops.push(GradientStop {
-                        color: last.color,
-                        offset: 0.5,
-                    });
-                    stops.push(GradientStop {
-                        color: last.color,
-                        offset: 1.0,
-                    });
-
-                    let offset = last.offset;
-
-                    (offset - 0.5, offset + 0.5)
-                }
-                ExtendMode::Repeat => {
-                    // A repeating gradient with stops that are all in the same
-                    // position should just display the last color. I believe the
-                    // spec says that it should be the average color of the gradient,
-                    // but this matches what Gecko and Blink does
-                    stops.push(GradientStop {
-                        color: last.color,
-                        offset: 0.0,
-                    });
-                    stops.push(GradientStop {
-                        color: last.color,
-                        offset: 1.0,
-                    });
-
-                    (0.0, 1.0)
-                }
-            }
-        }
-    }
-
-    // NOTE: gradients must be pushed in the order they're created
-    // because create_gradient stores the stops in anticipation
-    pub fn create_gradient(&mut self,
-                           start_point: LayoutPoint,
-                           end_point: LayoutPoint,
-                           mut stops: Vec<GradientStop>,
-                           extend_mode: ExtendMode) -> Gradient {
-        let (start_offset,
-             end_offset) = DisplayListBuilder::normalize_stops(&mut stops, extend_mode);
-
-        let start_to_end = end_point - start_point;
-
-        self.push_stops(&stops);
-
-        Gradient {
-            start_point: start_point + start_to_end * start_offset,
-            end_point: start_point + start_to_end * end_offset,
-            extend_mode: extend_mode,
-        }
-    }
-
-    // NOTE: gradients must be pushed in the order they're created
-    // because create_gradient stores the stops in anticipation
-    pub fn create_radial_gradient(&mut self,
-                                  center: LayoutPoint,
-                                  radius: LayoutSize,
-                                  mut stops: Vec<GradientStop>,
-                                  extend_mode: ExtendMode) -> RadialGradient {
-        if radius.width <= 0.0 || radius.height <= 0.0 {
-            // The shader cannot handle a non positive radius. So
-            // reuse the stops vector and construct an equivalent
-            // gradient.
-            let last_color = stops.last().unwrap().color;
-
-            let stops = [
-                GradientStop {
-                    offset: 0.0,
-                    color: last_color,
-                },
-                GradientStop {
-                    offset: 1.0,
-                    color: last_color,
-                },
-            ];
-
-            self.push_stops(&stops);
-
-            return RadialGradient {
-                start_center: center,
-                start_radius: 0.0,
-                end_center: center,
-                end_radius: 1.0,
-                ratio_xy: 1.0,
-                extend_mode: extend_mode,
-            };
-        }
-
-        let (start_offset,
-             end_offset) = DisplayListBuilder::normalize_stops(&mut stops, extend_mode);
-
-        self.push_stops(&stops);
-
-        RadialGradient {
-            start_center: center,
-            start_radius: radius.width * start_offset,
-            end_center: center,
-            end_radius: radius.width * end_offset,
-            ratio_xy: radius.width / radius.height,
-            extend_mode: extend_mode,
-        }
-    }
-
-    // NOTE: gradients must be pushed in the order they're created
-    // because create_gradient stores the stops in anticipation
-    pub fn create_complex_radial_gradient(&mut self,
-                                          start_center: LayoutPoint,
-                                          start_radius: f32,
-                                          end_center: LayoutPoint,
-                                          end_radius: f32,
-                                          ratio_xy: f32,
-                                          stops: Vec<GradientStop>,
-                                          extend_mode: ExtendMode) -> RadialGradient {
-
-        self.push_stops(&stops);
-
-        RadialGradient {
-            start_center: start_center,
-            start_radius: start_radius,
-            end_center: end_center,
-            end_radius: end_radius,
-            ratio_xy: ratio_xy,
-            extend_mode: extend_mode,
-        }
-    }
-
-    pub fn push_border(&mut self,
-                       rect: LayoutRect,
-                       _token: ClipRegionToken,
-                       widths: BorderWidths,
-                       details: BorderDetails) {
-        let item = SpecificDisplayItem::Border(BorderDisplayItem {
-            details: details,
-            widths: widths,
-        });
-
-        self.push_item(item, rect);
-    }
-
-    pub fn push_box_shadow(&mut self,
-                           rect: LayoutRect,
-                           _token: ClipRegionToken,
-                           box_bounds: LayoutRect,
-                           offset: LayoutVector2D,
-                           color: ColorF,
-                           blur_radius: f32,
-                           spread_radius: f32,
-                           border_radius: f32,
-                           clip_mode: BoxShadowClipMode) {
-        let item = SpecificDisplayItem::BoxShadow(BoxShadowDisplayItem {
-            box_bounds: box_bounds,
-            offset: offset,
-            color: color,
-            blur_radius: blur_radius,
-            spread_radius: spread_radius,
-            border_radius: border_radius,
-            clip_mode: clip_mode,
-        });
-
-        self.push_item(item, rect);
-    }
-
-    pub fn push_gradient(&mut self,
-                         rect: LayoutRect,
-                         _token: ClipRegionToken,
-                         gradient: Gradient,
-                         tile_size: LayoutSize,
-                         tile_spacing: LayoutSize) {
-        let item = SpecificDisplayItem::Gradient(GradientDisplayItem {
-            gradient: gradient,
-            tile_size: tile_size,
-            tile_spacing: tile_spacing,
-        });
-
-        self.push_item(item, rect);
-    }
-
-    pub fn push_radial_gradient(&mut self,
-                                rect: LayoutRect,
-                                _token: ClipRegionToken,
-                                gradient: RadialGradient,
-                                tile_size: LayoutSize,
-                                tile_spacing: LayoutSize) {
-        let item = SpecificDisplayItem::RadialGradient(RadialGradientDisplayItem {
-            gradient: gradient,
-            tile_size: tile_size,
-            tile_spacing: tile_spacing,
-        });
-
-        self.push_item(item, rect);
-    }
-
-    pub fn push_stacking_context(&mut self,
-                                 scroll_policy: ScrollPolicy,
-                                 bounds: LayoutRect,
-                                 transform: Option<PropertyBinding<LayoutTransform>>,
-                                 transform_style: TransformStyle,
-                                 perspective: Option<LayoutTransform>,
-                                 mix_blend_mode: MixBlendMode,
-                                 filters: Vec<FilterOp>) {
-        let item = SpecificDisplayItem::PushStackingContext(PushStackingContextDisplayItem {
-            stacking_context: StackingContext {
-                scroll_policy: scroll_policy,
-                transform: transform,
-                transform_style: transform_style,
-                perspective: perspective,
-                mix_blend_mode: mix_blend_mode,
-            }
-        });
-
-        self.push_item(item, bounds);
-        self.push_iter(&filters);
-    }
-
-    pub fn pop_stacking_context(&mut self) {
-        self.push_new_empty_item(SpecificDisplayItem::PopStackingContext);
-    }
-
-    pub fn push_stops(&mut self, stops: &[GradientStop]) {
-        if stops.is_empty() {
-            return
-        }
-        self.push_new_empty_item(SpecificDisplayItem::SetGradientStops);
-        self.push_iter(stops);
-    }
-
-    pub fn define_clip(&mut self,
-                       content_rect: LayoutRect,
-                       _token: ClipRegionToken,
-                       id: Option<ClipId>)
-                       -> ClipId {
-        let id = match id {
-            Some(id) => id,
-            None => {
-                self.next_clip_id += 1;
-                ClipId::Clip(self.next_clip_id - 1, 0, self.pipeline_id)
-            }
-        };
-
-        let item = SpecificDisplayItem::Clip(ClipDisplayItem {
-            id: id,
-            parent_id: self.clip_stack.last().unwrap().scroll_node_id,
-        });
-
-        self.push_item(item, content_rect);
-        id
-    }
-
-    pub fn push_clip_node(&mut self,
-                          content_rect: LayoutRect,
-                          token: ClipRegionToken,
-                          id: Option<ClipId>){
-        let id = self.define_clip(content_rect, token, id);
-        self.clip_stack.push(ClipAndScrollInfo::simple(id));
-    }
-
-    pub fn push_clip_id(&mut self, id: ClipId) {
-        self.clip_stack.push(ClipAndScrollInfo::simple(id));
-    }
-
-    pub fn push_clip_and_scroll_info(&mut self, info: ClipAndScrollInfo) {
-        self.clip_stack.push(info);
-    }
-
-    pub fn pop_clip_id(&mut self) {
-        self.clip_stack.pop();
-        assert!(self.clip_stack.len() > 0);
-    }
-
-    pub fn pop_clip_node(&mut self) {
-        self.pop_clip_id();
-    }
-
-    pub fn push_iframe(&mut self, rect: LayoutRect, _token: ClipRegionToken, pipeline_id: PipelineId) {
-        let item = SpecificDisplayItem::Iframe(IframeDisplayItem { pipeline_id: pipeline_id });
-        self.push_item(item, rect);
-    }
-
-    // Don't use this function. It will go away.
-    //
-    // We're using this method as a hack in Gecko to retain parts sub-parts of display
-    // lists so that we can regenerate them without building Gecko display items. WebRender
-    // will replace references to the root scroll frame id with the current scroll frame
-    // id.
-    pub fn push_nested_display_list(&mut self, built_display_list: &BuiltDisplayList) {
-        self.push_clip_region(&LayoutRect::zero(), vec![], None);
-        self.push_new_empty_item(SpecificDisplayItem::PushNestedDisplayList);
-
-        self.data.extend_from_slice(&built_display_list.data);
-
-        self.push_clip_region(&LayoutRect::zero(), vec![], None);
-        self.push_new_empty_item(SpecificDisplayItem::PopNestedDisplayList);
-    }
-
-    pub fn push_clip_region<I>(&mut self,
-                            rect: &LayoutRect,
-                            complex: I,
-                            image_mask: Option<ImageMask>)
-                            -> ClipRegionToken
-    where I: IntoIterator<Item = ComplexClipRegion>,
-          I::IntoIter: ExactSizeIterator,
-    {
-        self.push_new_empty_item(SpecificDisplayItem::SetClipRegion(
-            ClipRegion::new(rect, image_mask),
-        ));
-        self.push_iter(complex);
-
-        ClipRegionToken { _unforgeable: () }
-    }
-
-    pub fn finalize(self) -> (PipelineId, LayoutSize, BuiltDisplayList) {
-        let end_time = precise_time_ns();
-
-        (self.pipeline_id,
-         self.content_size,
-         BuiltDisplayList {
-            descriptor: BuiltDisplayListDescriptor {
-                builder_start_time: self.builder_start_time,
-                builder_finish_time: end_time,
-            },
-            data: self.data,
-         })
-    }
-}
-
-/// Verification that push_clip_region was called before
-/// pushing an item that requires it.
-pub struct ClipRegionToken { _unforgeable: () }
deleted file mode 100644
--- a/gfx/webrender_traits/src/font.rs
+++ /dev/null
@@ -1,195 +0,0 @@
-/* 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 {ColorU, ColorF, LayoutPoint};
-use std::sync::Arc;
-
-#[cfg(target_os = "macos")] use core_foundation::string::CFString;
-#[cfg(target_os = "macos")] use core_graphics::font::CGFont;
-#[cfg(target_os = "macos")] use serde::de::{self, Deserialize, Deserializer};
-#[cfg(target_os = "macos")] use serde::ser::{Serialize, Serializer};
-#[cfg(target_os = "windows")] use dwrote::FontDescriptor;
-
-
-#[cfg(target_os = "macos")]
-#[derive(Clone)]
-pub struct NativeFontHandle(pub CGFont);
-
-#[cfg(target_os = "macos")]
-impl Serialize for NativeFontHandle {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
-        let postscript_name = self.0.postscript_name().to_string();
-        postscript_name.serialize(serializer)
-    }
-}
-
-#[cfg(target_os = "macos")]
-impl<'de> Deserialize<'de> for NativeFontHandle {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
-        let postscript_name: String = try!(Deserialize::deserialize(deserializer));
-
-        match CGFont::from_name(&CFString::new(&*postscript_name)) {
-            Ok(font) => Ok(NativeFontHandle(font)),
-            _ => Err(de::Error::custom("Couldn't find a font with that PostScript name!")),
-        }
-    }
-}
-
-/// Native fonts are not used on Linux; all fonts are raw.
-#[cfg(not(any(target_os = "macos", target_os = "windows")))]
-#[cfg_attr(not(any(target_os = "macos", target_os = "windows")), derive(Clone, Serialize, Deserialize))]
-pub struct NativeFontHandle;
-
-#[cfg(target_os = "windows")]
-pub type NativeFontHandle = FontDescriptor;
-
-#[repr(C)]
-#[derive(Copy, Clone, Deserialize, Serialize, Debug)]
-pub struct GlyphDimensions {
-    pub left: i32,
-    pub top: i32,
-    pub width: u32,
-    pub height: u32,
-}
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Ord, PartialOrd)]
-pub struct FontKey(pub u32, pub u32);
-
-impl FontKey {
-    pub fn new(key0: u32, key1: u32) -> FontKey {
-        FontKey(key0, key1)
-    }
-}
-
-
-#[derive(Clone)]
-pub enum FontTemplate {
-    Raw(Arc<Vec<u8>>, u32),
-    Native(NativeFontHandle),
-}
-
-#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Ord, PartialOrd)]
-pub enum FontRenderMode {
-    Mono,
-    Alpha,
-    Subpixel,
-}
-
-const FIXED16_SHIFT: i32 = 16;
-
-// This matches the behaviour of SkScalarToFixed
-fn f32_truncate_to_fixed16(x: f32) -> i32 {
-    let fixed1 = (1 << FIXED16_SHIFT) as f32;
-    (x * fixed1) as i32
-}
-
-impl FontRenderMode {
-    // Skia quantizes subpixel offets into 1/4 increments.
-    // Given the absolute position, return the quantized increment
-    fn subpixel_quantize_offset(&self, pos: f32) -> SubpixelOffset {
-        if *self != FontRenderMode::Subpixel {
-            return SubpixelOffset::Zero;
-        }
-
-        const SUBPIXEL_BITS: i32 = 2;
-        const SUBPIXEL_FIXED16_MASK: i32 = ((1 << SUBPIXEL_BITS) - 1) << (FIXED16_SHIFT - SUBPIXEL_BITS);
-
-        const SUBPIXEL_ROUNDING: f32 = 0.5 / (1 << SUBPIXEL_BITS) as f32;
-        let pos = pos + SUBPIXEL_ROUNDING;
-        let fraction = (f32_truncate_to_fixed16(pos) & SUBPIXEL_FIXED16_MASK) >> (FIXED16_SHIFT - SUBPIXEL_BITS);
-
-        match fraction {
-            0 => SubpixelOffset::Zero,
-            1 => SubpixelOffset::Quarter,
-            2 => SubpixelOffset::Half,
-            3 => SubpixelOffset::ThreeQuarters,
-            _ => panic!("Should only be given the fractional part"),
-        }
-    }
-}
-
-#[repr(u8)]
-#[derive(Hash, Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
-pub enum SubpixelOffset {
-    Zero            = 0,
-    Quarter         = 1,
-    Half            = 2,
-    ThreeQuarters   = 3,
-}
-
-impl Into<f64> for SubpixelOffset {
-    fn into(self) -> f64 {
-        match self {
-            SubpixelOffset::Zero => 0.0,
-            SubpixelOffset::Quarter => 0.25,
-            SubpixelOffset::Half => 0.5,
-            SubpixelOffset::ThreeQuarters => 0.75,
-        }
-    }
-}
-
-#[derive(Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)]
-pub struct SubpixelPoint {
-    pub x: SubpixelOffset,
-    pub y: SubpixelOffset,
-}
-
-impl SubpixelPoint {
-    pub fn new(point: LayoutPoint,
-               render_mode: FontRenderMode) -> SubpixelPoint {
-        SubpixelPoint {
-            x: render_mode.subpixel_quantize_offset(point.x),
-            y: render_mode.subpixel_quantize_offset(point.y),
-        }
-    }
-
-    pub fn to_f64(&self) -> (f64, f64) {
-        (self.x.into(), self.y.into())
-    }
-
-    pub fn set_offset(&mut self, point: LayoutPoint, render_mode: FontRenderMode) {
-        self.x = render_mode.subpixel_quantize_offset(point.x);
-        self.y = render_mode.subpixel_quantize_offset(point.y);
-    }
-}
-
-#[derive(Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)]
-pub struct GlyphKey {
-    pub font_key: FontKey,
-    // The font size is in *device* pixels, not logical pixels.
-    // It is stored as an Au since we need sub-pixel sizes, but
-    // can't store as a f32 due to use of this type as a hash key.
-    // TODO(gw): Perhaps consider having LogicalAu and DeviceAu
-    //           or something similar to that.
-    pub size: Au,
-    pub index: u32,
-    pub color: ColorU,
-    pub subpixel_point: SubpixelPoint,
-}
-
-impl GlyphKey {
-    pub fn new(font_key: FontKey,
-               size: Au,
-               color: ColorF,
-               index: u32,
-               point: LayoutPoint,
-               render_mode: FontRenderMode) -> GlyphKey {
-        GlyphKey {
-            font_key: font_key,
-            size: size,
-            color: ColorU::from(color),
-            index: index,
-            subpixel_point: SubpixelPoint::new(point, render_mode),
-        }
-    }
-}
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct GlyphInstance {
-    pub index: u32,
-    pub point: LayoutPoint,
-}
deleted file mode 100644
--- a/gfx/webrender_traits/src/image.rs
+++ /dev/null
@@ -1,193 +0,0 @@
-/* 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 std::sync::Arc;
-use {DeviceUintRect, DevicePoint};
-use {TileOffset, TileSize};
-use font::{FontKey, FontTemplate};
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub struct ImageKey(pub u32, pub u32);
-
-impl ImageKey {
-    pub fn new(key0: u32, key1: u32) -> ImageKey {
-        ImageKey(key0, key1)
-    }
-}
-
-/// An arbitrary identifier for an external image provided by the
-/// application. It must be a unique identifier for each external
-/// image.
-#[repr(C)]
-#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
-pub struct ExternalImageId(pub u64);
-
-#[repr(u32)]
-#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
-pub enum ExternalImageType {
-    Texture2DHandle,        // gl TEXTURE_2D handle
-    TextureRectHandle,      // gl TEXTURE_RECT handle
-    TextureExternalHandle,  // gl TEXTURE_EXTERNAL handle
-    ExternalBuffer,
-}
-
-#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
-pub struct ExternalImageData {
-    pub id: ExternalImageId,
-    pub channel_index: u8,
-    pub image_type: ExternalImageType,
-}
-
-#[repr(u32)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub enum ImageFormat {
-    Invalid  = 0,
-    A8       = 1,
-    RGB8     = 2,
-    BGRA8    = 3,
-    RGBAF32  = 4,
-    RG8      = 5,
-}
-
-impl ImageFormat {
-    pub fn bytes_per_pixel(self) -> Option<u32> {
-        match self {
-            ImageFormat::A8 => Some(1),
-            ImageFormat::RGB8 => Some(3),
-            ImageFormat::BGRA8 => Some(4),
-            ImageFormat::RGBAF32 => Some(16),
-            ImageFormat::RG8 => Some(2),
-            ImageFormat::Invalid => None,
-        }
-    }
-}
-
-#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
-pub struct ImageDescriptor {
-    pub format: ImageFormat,
-    pub width: u32,
-    pub height: u32,
-    pub stride: Option<u32>,
-    pub offset: u32,
-    pub is_opaque: bool,
-}
-
-impl ImageDescriptor {
-    pub fn new(width: u32, height: u32, format: ImageFormat, is_opaque: bool) -> Self {
-        ImageDescriptor {
-            width: width,
-            height: height,
-            format: format,
-            stride: None,
-            offset: 0,
-            is_opaque: is_opaque,
-        }
-    }
-
-    pub fn compute_stride(&self) -> u32 {
-        self.stride.unwrap_or(self.width * self.format.bytes_per_pixel().unwrap())
-    }
-}
-
-#[derive(Clone, Serialize, Deserialize)]
-pub enum ImageData {
-    Raw(Arc<Vec<u8>>),
-    Blob(BlobImageData),
-    External(ExternalImageData),
-}
-
-impl ImageData {
-    pub fn new(bytes: Vec<u8>) -> ImageData {
-        ImageData::Raw(Arc::new(bytes))
-    }
-
-    pub fn new_shared(bytes: Arc<Vec<u8>>) -> ImageData {
-        ImageData::Raw(bytes)
-    }
-
-    pub fn new_blob_image(commands: Vec<u8>) -> ImageData {
-        ImageData::Blob(commands)
-    }
-
-    #[inline]
-    pub fn is_blob(&self) -> bool {
-        match self {
-            &ImageData::Blob(_) => true,
-            _ => false,
-        }
-    }
-
-    #[inline]
-    pub fn uses_texture_cache(&self) -> bool {
-        match self {
-            &ImageData::External(ext_data) => {
-                match ext_data.image_type {
-                    ExternalImageType::Texture2DHandle => false,
-                    ExternalImageType::TextureRectHandle => false,
-                    ExternalImageType::TextureExternalHandle => false,
-                    ExternalImageType::ExternalBuffer => true,
-                }
-            }
-            &ImageData::Blob(_) => true,
-            &ImageData::Raw(_) => true,
-        }
-    }
-}
-
-pub trait BlobImageResources {
-    fn get_font_data(&self, key: FontKey) -> &FontTemplate;
-    fn get_image(&self, key: ImageKey) -> Option<(&ImageData, &ImageDescriptor)>;
-}
-
-pub trait BlobImageRenderer: Send {
-    fn add(&mut self, key: ImageKey, data: BlobImageData, tiling: Option<TileSize>);
-
-    fn update(&mut self, key: ImageKey, data: BlobImageData);
-
-    fn delete(&mut self, key: ImageKey);
-
-    fn request(&mut self,
-               services: &BlobImageResources,
-               key: BlobImageRequest,
-               descriptor: &BlobImageDescriptor,
-               dirty_rect: Option<DeviceUintRect>);
-
-    fn resolve(&mut self, key: BlobImageRequest) -> BlobImageResult;
-
-    fn delete_font(&mut self, key: FontKey);
-}
-
-pub type BlobImageData = Vec<u8>;
-
-pub type BlobImageResult = Result<RasterizedBlobImage, BlobImageError>;
-
-#[repr(C)]
-#[derive(Copy, Clone, Debug)]
-pub struct BlobImageDescriptor {
-    pub width: u32,
-    pub height: u32,
-    pub offset: DevicePoint,
-    pub format: ImageFormat,
-}
-
-pub struct RasterizedBlobImage {
-    pub width: u32,
-    pub height: u32,
-    pub data: Vec<u8>,
-}
-
-#[derive(Clone, Debug)]
-pub enum BlobImageError {
-    Oom,
-    InvalidKey,
-    InvalidData,
-    Other(String),
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
-pub struct BlobImageRequest {
-    pub key: ImageKey,
-    pub tile: Option<TileOffset>,
-}
deleted file mode 100644
--- a/gfx/webrender_traits/src/units.rs
+++ /dev/null
@@ -1,110 +0,0 @@
-/* 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/. */
-
-//! A collection of coordinate spaces and their corresponding Point, Size and Rect types.
-//!
-//! Physical pixels take into account the device pixel ratio and their dimensions tend
-//! to correspond to the allocated size of resources in memory, while logical pixels
-//! don't have the device pixel ratio applied which means they are agnostic to the usage
-//! of hidpi screens and the like.
-//!
-//! The terms "layer" and "stacking context" can be used interchangeably
-//! in the context of coordinate systems.
-
-use euclid::{Length, TypedTransform3D, TypedRect, TypedSize2D};
-use euclid::{TypedPoint2D, TypedPoint3D, TypedVector2D, TypedVector3D};
-
-/// Geometry in the coordinate system of the render target (screen or intermediate
-/// surface) in physical pixels.
-#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
-pub struct DevicePixel;
-
-pub type DeviceIntRect = TypedRect<i32, DevicePixel>;
-pub type DeviceIntPoint = TypedPoint2D<i32, DevicePixel>;
-pub type DeviceIntSize = TypedSize2D<i32, DevicePixel>;
-pub type DeviceIntLength = Length<i32, DevicePixel>;
-
-pub type DeviceUintRect = TypedRect<u32, DevicePixel>;
-pub type DeviceUintPoint = TypedPoint2D<u32, DevicePixel>;
-pub type DeviceUintSize = TypedSize2D<u32, DevicePixel>;
-
-pub type DeviceRect = TypedRect<f32, DevicePixel>;
-pub type DevicePoint = TypedPoint2D<f32, DevicePixel>;
-pub type DeviceVector2D = TypedVector2D<f32, DevicePixel>;
-pub type DeviceSize = TypedSize2D<f32, DevicePixel>;
-
-/// Geometry in a stacking context's local coordinate space (logical pixels).
-///
-/// For now layout pixels are equivalent to layer pixels, but it may change.
-pub type LayoutPixel = LayerPixel;
-
-pub type LayoutRect = LayerRect;
-pub type LayoutPoint = LayerPoint;
-pub type LayoutVector2D = LayerVector2D;
-pub type LayoutVector3D = LayerVector3D;
-pub type LayoutSize = LayerSize;
-
-/// Geometry in a layer's local coordinate space (logical pixels).
-#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
-pub struct LayerPixel;
-
-pub type LayerRect = TypedRect<f32, LayerPixel>;
-pub type LayerPoint = TypedPoint2D<f32, LayerPixel>;
-pub type LayerPoint3D = TypedPoint3D<f32, LayerPixel>;
-pub type LayerVector2D = TypedVector2D<f32, LayerPixel>;
-pub type LayerVector3D = TypedVector3D<f32, LayerPixel>;
-pub type LayerSize = TypedSize2D<f32, LayerPixel>;
-
-/// Geometry in a layer's scrollable parent coordinate space (logical pixels).
-///
-/// Some layers are scrollable while some are not. There is a distinction between
-/// a layer's parent layer and a layer's scrollable parent layer (its closest parent
-/// that is scrollable, but not necessarily its immediate parent). Most of the internal
-/// transforms are expressed in terms of the scrollable parent and not the immediate
-/// parent.
-#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
-pub struct ScrollLayerPixel;
-
-pub type ScrollLayerRect = TypedRect<f32, ScrollLayerPixel>;
-pub type ScrollLayerPoint = TypedPoint2D<f32, ScrollLayerPixel>;
-pub type ScrollLayerVector2D = TypedVector2D<f32, ScrollLayerPixel>;
-pub type ScrollLayerSize = TypedSize2D<f32, ScrollLayerPixel>;
-
-/// Geometry in the document's coordinate space (logical pixels).
-#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
-pub struct WorldPixel;
-
-pub type WorldRect = TypedRect<f32, WorldPixel>;
-pub type WorldPoint = TypedPoint2D<f32, WorldPixel>;
-pub type WorldSize = TypedSize2D<f32, WorldPixel>;
-pub type WorldPoint3D = TypedPoint3D<f32, WorldPixel>;
-pub type WorldVector2D = TypedVector2D<f32, WorldPixel>;
-pub type WorldVector3D = TypedVector3D<f32, WorldPixel>;
-
-/// Offset in number of tiles.
-#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
-pub struct Tiles;
-pub type TileOffset = TypedPoint2D<u16, Tiles>;
-
-pub type LayoutTransform = TypedTransform3D<f32, LayoutPixel, LayoutPixel>;
-pub type LayerTransform = TypedTransform3D<f32, LayerPixel, LayerPixel>;
-pub type LayerToScrollTransform = TypedTransform3D<f32, LayerPixel, ScrollLayerPixel>;
-pub type ScrollToLayerTransform = TypedTransform3D<f32, ScrollLayerPixel, LayerPixel>;
-pub type LayerToWorldTransform = TypedTransform3D<f32, LayerPixel, WorldPixel>;
-pub type WorldToLayerTransform = TypedTransform3D<f32, WorldPixel, LayerPixel>;
-pub type ScrollToWorldTransform = TypedTransform3D<f32, ScrollLayerPixel, WorldPixel>;
-
-
-pub fn device_length(value: f32, device_pixel_ratio: f32) -> DeviceIntLength {
-    DeviceIntLength::new((value * device_pixel_ratio).round() as i32)
-}
-
-pub fn as_scroll_parent_rect(rect: &LayerRect) -> ScrollLayerRect {
-    ScrollLayerRect::from_untyped(&rect.to_untyped())
-}
-
-pub fn as_scroll_parent_vector(vector: &LayerVector2D) -> ScrollLayerVector2D {
-    ScrollLayerVector2D::from_untyped(&vector.to_untyped())
-}
-
deleted file mode 100644
--- a/gfx/webrender_traits/src/webgl.rs
+++ /dev/null
@@ -1,1043 +0,0 @@
-/* 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 channel::MsgSender;
-use gleam::gl;
-
-#[cfg(feature = "nightly")]
-use core::nonzero::NonZero;
-
-use offscreen_gl_context::{GLContext, NativeGLContextMethods, GLContextAttributes};
-use std::fmt;
-
-#[derive(Clone, Deserialize, Serialize)]
-pub enum WebGLCommand {
-    GetContextAttributes(MsgSender<GLContextAttributes>),
-    ActiveTexture(u32),
-    BlendColor(f32, f32, f32, f32),
-    BlendEquation(u32),
-    BlendEquationSeparate(u32, u32),
-    BlendFunc(u32, u32),
-    BlendFuncSeparate(u32, u32, u32, u32),
-    AttachShader(WebGLProgramId, WebGLShaderId),
-    DetachShader(WebGLProgramId, WebGLShaderId),
-    BindAttribLocation(WebGLProgramId, u32, String),
-    BufferData(u32, Vec<u8>, u32),
-    BufferSubData(u32, isize, Vec<u8>),
-    Clear(u32),
-    ClearColor(f32, f32, f32, f32),
-    ClearDepth(f64),
-    ClearStencil(i32),
-    ColorMask(bool, bool, bool, bool),
-    CullFace(u32),
-    FrontFace(u32),
-    DepthFunc(u32),
-    DepthMask(bool),
-    DepthRange(f64, f64),
-    Enable(u32),
-    Disable(u32),
-    CompileShader(WebGLShaderId, String),
-    CopyTexImage2D(u32, i32, u32, i32, i32, i32, i32, i32),
-    CopyTexSubImage2D(u32, i32, i32, i32, i32, i32, i32, i32),
-    CreateBuffer(MsgSender<Option<WebGLBufferId>>),
-    CreateFramebuffer(MsgSender<Option<WebGLFramebufferId>>),
-    CreateRenderbuffer(MsgSender<Option<WebGLRenderbufferId>>),
-    CreateTexture(MsgSender<Option<WebGLTextureId>>),
-    CreateProgram(MsgSender<Option<WebGLProgramId>>),
-    CreateShader(u32, MsgSender<Option<WebGLShaderId>>),
-    DeleteBuffer(WebGLBufferId),
-    DeleteFramebuffer(WebGLFramebufferId),
-    DeleteRenderbuffer(WebGLRenderbufferId),
-    DeleteTexture(WebGLTextureId),
-    DeleteProgram(WebGLProgramId),
-    DeleteShader(WebGLShaderId),
-    BindBuffer(u32, Option<WebGLBufferId>),
-    BindFramebuffer(u32, WebGLFramebufferBindingRequest),
-    BindRenderbuffer(u32, Option<WebGLRenderbufferId>),
-    BindTexture(u32, Option<WebGLTextureId>),
-    DisableVertexAttribArray(u32),
-    DrawArrays(u32, i32, i32),
-    DrawElements(u32, i32, u32, i64),
-    EnableVertexAttribArray(u32),
-    FramebufferRenderbuffer(u32, u32, u32, Option<WebGLRenderbufferId>),
-    FramebufferTexture2D(u32, u32, u32, Option<WebGLTextureId>, i32),
-    GetBufferParameter(u32, u32, MsgSender<WebGLResult<WebGLParameter>>),
-    GetExtensions(MsgSender<String>),
-    GetParameter(u32, MsgSender<WebGLResult<WebGLParameter>>),
-    GetProgramParameter(WebGLProgramId, u32, MsgSender<WebGLResult<WebGLParameter>>),
-    GetShaderParameter(WebGLShaderId, u32, MsgSender<WebGLResult<WebGLParameter>>),
-    GetShaderPrecisionFormat(u32, u32, MsgSender<WebGLResult<(i32, i32, i32)>>),
-    GetActiveAttrib(WebGLProgramId, u32, MsgSender<WebGLResult<(i32, u32, String)>>),
-    GetActiveUniform(WebGLProgramId, u32, MsgSender<WebGLResult<(i32, u32, String)>>),
-    GetAttribLocation(WebGLProgramId, String, MsgSender<Option<i32>>),
-    GetUniformLocation(WebGLProgramId, String, MsgSender<Option<i32>>),
-    GetVertexAttrib(u32, u32, MsgSender<WebGLResult<WebGLParameter>>),
-    GetVertexAttribOffset(u32, u32, MsgSender<WebGLResult<isize>>),
-    GetShaderInfoLog(WebGLShaderId, MsgSender<String>),
-    GetProgramInfoLog(WebGLProgramId, MsgSender<String>),
-    PolygonOffset(f32, f32),
-    RenderbufferStorage(u32, u32, i32, i32),
-    ReadPixels(i32, i32, i32, i32, u32, u32, MsgSender<Vec<u8>>),
-    SampleCoverage(f32, bool),
-    Scissor(i32, i32, i32, i32),
-    StencilFunc(u32, i32, u32),
-    StencilFuncSeparate(u32, u32, i32, u32),
-    StencilMask(u32),
-    StencilMaskSeparate(u32, u32),
-    StencilOp(u32, u32, u32),
-    StencilOpSeparate(u32, u32, u32, u32),
-    Hint(u32, u32),
-    IsEnabled(u32, MsgSender<bool>),
-    LineWidth(f32),
-    PixelStorei(u32, i32),
-    LinkProgram(WebGLProgramId),
-    Uniform1f(i32, f32),
-    Uniform1fv(i32, Vec<f32>),
-    Uniform1i(i32, i32),
-    Uniform1iv(i32, Vec<i32>),
-    Uniform2f(i32, f32, f32),
-    Uniform2fv(i32, Vec<f32>),
-    Uniform2i(i32, i32, i32),
-    Uniform2iv(i32, Vec<i32>),
-    Uniform3f(i32, f32, f32, f32),
-    Uniform3fv(i32, Vec<f32>),
-    Uniform3i(i32, i32, i32, i32),
-    Uniform3iv(i32, Vec<i32>),
-    Uniform4f(i32, f32, f32, f32, f32),
-    Uniform4fv(i32, Vec<f32>),
-    Uniform4i(i32, i32, i32, i32, i32),
-    Uniform4iv(i32, Vec<i32>),
-    UniformMatrix2fv(i32, bool, Vec<f32>),
-    UniformMatrix3fv(i32, bool, Vec<f32>),
-    UniformMatrix4fv(i32, bool, Vec<f32>),
-    UseProgram(WebGLProgramId),
-    ValidateProgram(WebGLProgramId),
-    VertexAttrib(u32, f32, f32, f32, f32),
-    VertexAttribPointer(u32, i32, u32, bool, i32, u32),
-    VertexAttribPointer2f(u32, i32, bool, i32, u32),
-    Viewport(i32, i32, i32, i32),
-    TexImage2D(u32, i32, i32, i32, i32, u32, u32, Vec<u8>),
-    TexParameteri(u32, u32, i32),
-    TexParameterf(u32, u32, f32),
-    TexSubImage2D(u32, i32, i32, i32, i32, i32, u32, u32, Vec<u8>),
-    DrawingBufferWidth(MsgSender<i32>),
-    DrawingBufferHeight(MsgSender<i32>),
-    Finish(MsgSender<()>),
-    Flush,
-    GenerateMipmap(u32),
-    CreateVertexArray(MsgSender<Option<WebGLVertexArrayId>>),
-    DeleteVertexArray(WebGLVertexArrayId),
-    BindVertexArray(Option<WebGLVertexArrayId>),
-}
-
-#[cfg(feature = "nightly")]
-macro_rules! define_resource_id_struct {
-    ($name:ident) => {
-        #[derive(Clone, Copy, Eq, Hash, PartialEq)]
-        pub struct $name(NonZero<u32>);
-
-        impl $name {
-            #[inline]
-            unsafe fn new(id: u32) -> Self {
-                $name(NonZero::new(id))
-            }
-
-            #[inline]
-            fn get(self) -> u32 {
-                *self.0
-            }
-        }
-
-    };
-}
-
-#[cfg(not(feature = "nightly"))]
-macro_rules! define_resource_id_struct {
-    ($name:ident) => {
-        #[derive(Clone, Copy, Eq, Hash, PartialEq)]
-        pub struct $name(u32);
-
-        impl $name {
-            #[inline]
-            unsafe fn new(id: u32) -> Self {
-                $name(id)
-            }
-
-            #[inline]
-            fn get(self) -> u32 {
-                self.0
-            }
-        }
-    };
-}
-
-macro_rules! define_resource_id {
-    ($name:ident) => {
-        define_resource_id_struct!($name);
-
-        impl<'de> ::serde::Deserialize<'de> for $name {
-            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-                where D: ::serde::Deserializer<'de>
-            {
-                let id = try!(u32::deserialize(deserializer));
-                if id == 0 {
-                    Err(::serde::de::Error::custom("expected a non-zero value"))
-                } else {
-                    Ok(unsafe { $name::new(id) })
-                }
-            }
-        }
-
-        impl ::serde::Serialize for $name {
-            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-                where S: ::serde::Serializer
-            {
-                self.get().serialize(serializer)
-            }
-        }
-
-        impl ::std::fmt::Debug for $name {
-            fn fmt(&self, fmt: &mut ::std::fmt::Formatter)
-                  -> Result<(), ::std::fmt::Error> {
-                fmt.debug_tuple(stringify!($name))
-                   .field(&self.get())
-                   .finish()
-            }
-        }
-
-        impl ::std::fmt::Display for $name {
-            fn fmt(&self, fmt: &mut ::std::fmt::Formatter)
-                  -> Result<(), ::std::fmt::Error> {
-                write!(fmt, "{}", self.get())
-            }
-        }
-
-        impl ::heapsize::HeapSizeOf for $name {
-            fn heap_size_of_children(&self) -> usize { 0 }
-        }
-    }
-}
-
-define_resource_id!(WebGLBufferId);
-define_resource_id!(WebGLFramebufferId);
-define_resource_id!(WebGLRenderbufferId);
-define_resource_id!(WebGLTextureId);
-define_resource_id!(WebGLProgramId);
-define_resource_id!(WebGLShaderId);
-define_resource_id!(WebGLVertexArrayId);
-
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
-pub struct WebGLContextId(pub usize);
-
-impl ::heapsize::HeapSizeOf for WebGLContextId {
-    fn heap_size_of_children(&self) -> usize { 0 }
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub enum WebGLError {
-    InvalidEnum,
-    InvalidFramebufferOperation,
-    InvalidOperation,
-    InvalidValue,
-    OutOfMemory,
-    ContextLost,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub enum WebGLFramebufferBindingRequest {
-    Explicit(WebGLFramebufferId),
-    Default,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub enum WebGLParameter {
-    Int(i32),
-    Bool(bool),
-    String(String),
-    Float(f32),
-    FloatArray(Vec<f32>),
-    Invalid,
-}
-
-pub type WebGLResult<T> = Result<T, WebGLError>;
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub enum WebGLShaderParameter {
-    Int(i32),
-    Bool(bool),
-    Invalid,
-}
-
-impl fmt::Debug for WebGLCommand {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        use WebGLCommand::*;
-        let name = match *self {
-            GetContextAttributes(..) => "GetContextAttributes",
-            ActiveTexture(..) => "ActiveTexture",
-            BlendColor(..) => "BlendColor",
-            BlendEquation(..) => "BlendEquation",
-            BlendEquationSeparate(..) => "BlendEquationSeparate",
-            BlendFunc(..) => "BlendFunc",
-            BlendFuncSeparate(..) => "BlendFuncSeparate",
-            AttachShader(..) => "AttachShader",
-            DetachShader(..) => "DetachShader",
-            BindAttribLocation(..) => "BindAttribLocation",
-            BufferData(..) => "BufferData",
-            BufferSubData(..) => "BufferSubData",
-            Clear(..) => "Clear",
-            ClearColor(..) => "ClearColor",
-            ClearDepth(..) => "ClearDepth",
-            ClearStencil(..) => "ClearStencil",
-            ColorMask(..) => "ColorMask",
-            CopyTexImage2D(..) => "CopyTexImage2D",
-            CopyTexSubImage2D(..) => "CopyTexSubImage2D",
-            CullFace(..) => "CullFace",
-            FrontFace(..) => "FrontFace",
-            DepthFunc(..) => "DepthFunc",
-            DepthMask(..) => "DepthMask",
-            DepthRange(..) => "DepthRange",
-            Enable(..) => "Enable",
-            Disable(..) => "Disable",
-            CompileShader(..) => "CompileShader",
-            CreateBuffer(..) => "CreateBuffer",
-            CreateFramebuffer(..) => "CreateFramebuffer",
-            CreateRenderbuffer(..) => "CreateRenderbuffer",
-            CreateTexture(..) => "CreateTexture",
-            CreateProgram(..) => "CreateProgram",
-            CreateShader(..) => "CreateShader",
-            DeleteBuffer(..) => "DeleteBuffer",
-            DeleteFramebuffer(..) => "DeleteFramebuffer",
-            DeleteRenderbuffer(..) => "DeleteRenderBuffer",
-            DeleteTexture(..) => "DeleteTexture",
-            DeleteProgram(..) => "DeleteProgram",
-            DeleteShader(..) => "DeleteShader",
-            BindBuffer(..) => "BindBuffer",
-            BindFramebuffer(..) => "BindFramebuffer",
-            BindRenderbuffer(..) => "BindRenderbuffer",
-            BindTexture(..) => "BindTexture",
-            DisableVertexAttribArray(..) => "DisableVertexAttribArray",
-            DrawArrays(..) => "DrawArrays",
-            DrawElements(..) => "DrawElements",
-            EnableVertexAttribArray(..) => "EnableVertexAttribArray",
-            FramebufferRenderbuffer(..) => "FramebufferRenderbuffer",
-            FramebufferTexture2D(..) => "FramebufferTexture2D",
-            GetBufferParameter(..) => "GetBufferParameter",
-            GetExtensions(..) => "GetExtensions",
-            GetParameter(..) => "GetParameter",
-            GetProgramParameter(..) => "GetProgramParameter",
-            GetShaderParameter(..) => "GetShaderParameter",
-            GetShaderPrecisionFormat(..) => "GetShaderPrecisionFormat",
-            GetActiveAttrib(..) => "GetActiveAttrib",
-            GetActiveUniform(..) => "GetActiveUniform",
-            GetAttribLocation(..) => "GetAttribLocation",
-            GetUniformLocation(..) => "GetUniformLocation",
-            GetShaderInfoLog(..) => "GetShaderInfoLog",
-            GetProgramInfoLog(..) => "GetProgramInfoLog",
-            GetVertexAttrib(..) => "GetVertexAttrib",
-            GetVertexAttribOffset(..) => "GetVertexAttribOffset",
-            PolygonOffset(..) => "PolygonOffset",
-            ReadPixels(..) => "ReadPixels",
-            RenderbufferStorage(..) => "RenderbufferStorage",
-            SampleCoverage(..) => "SampleCoverage",
-            Scissor(..) => "Scissor",
-            StencilFunc(..) => "StencilFunc",
-            StencilFuncSeparate(..) => "StencilFuncSeparate",
-            StencilMask(..) => "StencilMask",
-            StencilMaskSeparate(..) => "StencilMaskSeparate",
-            StencilOp(..) => "StencilOp",
-            StencilOpSeparate(..) => "StencilOpSeparate",
-            Hint(..) => "Hint",
-            IsEnabled(..) => "IsEnabled",
-            LineWidth(..) => "LineWidth",
-            PixelStorei(..) => "PixelStorei",
-            LinkProgram(..) => "LinkProgram",
-            Uniform1f(..) => "Uniform1f",
-            Uniform1fv(..) => "Uniform1fv",
-            Uniform1i(..) => "Uniform1i",
-            Uniform1iv(..) => "Uniform1iv",
-            Uniform2f(..) => "Uniform2f",
-            Uniform2fv(..) => "Uniform2fv",
-            Uniform2i(..) => "Uniform2i",
-            Uniform2iv(..) => "Uniform2iv",
-            Uniform3f(..) => "Uniform3f",
-            Uniform3fv(..) => "Uniform3fv",
-            Uniform3i(..) => "Uniform3i",
-            Uniform3iv(..) => "Uniform3iv",
-            Uniform4f(..) => "Uniform4f",
-            Uniform4fv(..) => "Uniform4fv",
-            Uniform4i(..) => "Uniform4i",
-            Uniform4iv(..) => "Uniform4iv",
-            UniformMatrix2fv(..) => "UniformMatrix2fv",
-            UniformMatrix3fv(..) => "UniformMatrix3fv",
-            UniformMatrix4fv(..) => "UniformMatrix4fv",
-            UseProgram(..) => "UseProgram",
-            ValidateProgram(..) => "ValidateProgram",
-            VertexAttrib(..) => "VertexAttrib",
-            VertexAttribPointer2f(..) => "VertexAttribPointer2f",
-            VertexAttribPointer(..) => "VertexAttribPointer",
-            Viewport(..) => "Viewport",
-            TexImage2D(..) => "TexImage2D",
-            TexParameteri(..) => "TexParameteri",
-            TexParameterf(..) => "TexParameterf",
-            TexSubImage2D(..) => "TexSubImage2D",
-            DrawingBufferWidth(..) => "DrawingBufferWidth",
-            DrawingBufferHeight(..) => "DrawingBufferHeight",
-            Finish(..) => "Finish",
-            Flush => "Flush",
-            GenerateMipmap(..) => "GenerateMipmap",
-            CreateVertexArray(..) => "CreateVertexArray",
-            DeleteVertexArray(..) => "DeleteVertexArray",
-            BindVertexArray(..) => "BindVertexArray"
-        };
-
-        write!(f, "CanvasWebGLMsg::{}(..)", name)
-    }
-}
-
-impl WebGLCommand {
-    /// NOTE: This method consumes the command
-    pub fn apply<Native: NativeGLContextMethods>(self, ctx: &GLContext<Native>) {
-        match self {
-            WebGLCommand::GetContextAttributes(sender) =>
-                sender.send(*ctx.borrow_attributes()).unwrap(),
-            WebGLCommand::ActiveTexture(target) =>
-                ctx.gl().active_texture(target),
-            WebGLCommand::AttachShader(program_id, shader_id) =>
-                ctx.gl().attach_shader(program_id.get(), shader_id.get()),
-            WebGLCommand::DetachShader(program_id, shader_id) =>
-                ctx.gl().detach_shader(program_id.get(), shader_id.get()),
-            WebGLCommand::BindAttribLocation(program_id, index, name) =>
-                ctx.gl().bind_attrib_location(program_id.get(), index, &name),
-            WebGLCommand::BlendColor(r, g, b, a) =>
-                ctx.gl().blend_color(r, g, b, a),
-            WebGLCommand::BlendEquation(mode) =>
-                ctx.gl().blend_equation(mode),
-            WebGLCommand::BlendEquationSeparate(mode_rgb, mode_alpha) =>
-                ctx.gl().blend_equation_separate(mode_rgb, mode_alpha),
-            WebGLCommand::BlendFunc(src, dest) =>
-                ctx.gl().blend_func(src, dest),
-            WebGLCommand::BlendFuncSeparate(src_rgb, dest_rgb, src_alpha, dest_alpha) =>
-                ctx.gl().blend_func_separate(src_rgb, dest_rgb, src_alpha, dest_alpha),
-            WebGLCommand::BufferData(buffer_type, data, usage) =>
-                gl::buffer_data(ctx.gl(), buffer_type, &data, usage),
-            WebGLCommand::BufferSubData(buffer_type, offset, data) =>
-                gl::buffer_sub_data(ctx.gl(), buffer_type, offset, &data),
-            WebGLCommand::Clear(mask) =>
-                ctx.gl().clear(mask),
-            WebGLCommand::ClearColor(r, g, b, a) =>
-                ctx.gl().clear_color(r, g, b, a),
-            WebGLCommand::ClearDepth(depth) =>
-                ctx.gl().clear_depth(depth),
-            WebGLCommand::ClearStencil(stencil) =>
-                ctx.gl().clear_stencil(stencil),
-            WebGLCommand::ColorMask(r, g, b, a) =>
-                ctx.gl().color_mask(r, g, b, a),
-            WebGLCommand::CopyTexImage2D(target, level, internal_format, x, y, width, height, border) =>
-                ctx.gl().copy_tex_image_2d(target, level, internal_format, x, y, width, height, border),
-            WebGLCommand::CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height) =>
-                ctx.gl().copy_tex_sub_image_2d(target, level, xoffset, yoffset, x, y, width, height),
-            WebGLCommand::CullFace(mode) =>
-                ctx.gl().cull_face(mode),
-            WebGLCommand::DepthFunc(func) =>
-                ctx.gl().depth_func(func),
-            WebGLCommand::DepthMask(flag) =>
-                ctx.gl().depth_mask(flag),
-            WebGLCommand::DepthRange(near, far) =>
-                ctx.gl().depth_range(near, far),
-            WebGLCommand::Disable(cap) =>
-                ctx.gl().disable(cap),
-            WebGLCommand::Enable(cap) =>
-                ctx.gl().enable(cap),
-            WebGLCommand::FramebufferRenderbuffer(target, attachment, renderbuffertarget, rb) =>
-                ctx.gl().framebuffer_renderbuffer(target, attachment, renderbuffertarget, rb.map_or(0, WebGLRenderbufferId::get)),
-            WebGLCommand::FramebufferTexture2D(target, attachment, textarget, texture, level) =>
-                ctx.gl().framebuffer_texture_2d(target, attachment, textarget, texture.map_or(0, WebGLTextureId::get), level),
-            WebGLCommand::FrontFace(mode) =>
-                ctx.gl().front_face(mode),
-            WebGLCommand::DisableVertexAttribArray(attrib_id) =>
-                ctx.gl().disable_vertex_attrib_array(attrib_id),
-            WebGLCommand::DrawArrays(mode, first, count) =>
-                ctx.gl().draw_arrays(mode, first, count),
-            WebGLCommand::DrawElements(mode, count, type_, offset) =>
-                ctx.gl().draw_elements(mode, count, type_, offset as u32),
-            WebGLCommand::EnableVertexAttribArray(attrib_id) =>
-                ctx.gl().enable_vertex_attrib_array(attrib_id),
-            WebGLCommand::Hint(name, val) =>
-                ctx.gl().hint(name, val),
-            WebGLCommand::IsEnabled(cap, chan) =>
-                chan.send(ctx.gl().is_enabled(cap) != 0).unwrap(),
-            WebGLCommand::LineWidth(width) =>
-                ctx.gl().line_width(width),
-            WebGLCommand::PixelStorei(name, val) =>
-                ctx.gl().pixel_store_i(name, val),
-            WebGLCommand::PolygonOffset(factor, units) =>
-                ctx.gl().polygon_offset(factor, units),
-            WebGLCommand::ReadPixels(x, y, width, height, format, pixel_type, chan) =>
-                Self::read_pixels(ctx.gl(), x, y, width, height, format, pixel_type, chan),
-            WebGLCommand::RenderbufferStorage(target, format, width, height) =>
-                ctx.gl().renderbuffer_storage(target, format, width, height),
-            WebGLCommand::SampleCoverage(value, invert) =>
-                ctx.gl().sample_coverage(value, invert),
-            WebGLCommand::Scissor(x, y, width, height) =>
-                ctx.gl().scissor(x, y, width, height),
-            WebGLCommand::StencilFunc(func, ref_, mask) =>
-                ctx.gl().stencil_func(func, ref_, mask),
-            WebGLCommand::StencilFuncSeparate(face, func, ref_, mask) =>
-                ctx.gl().stencil_func_separate(face, func, ref_, mask),
-            WebGLCommand::StencilMask(mask) =>
-                ctx.gl().stencil_mask(mask),
-            WebGLCommand::StencilMaskSeparate(face, mask) =>
-                ctx.gl().stencil_mask_separate(face, mask),
-            WebGLCommand::StencilOp(fail, zfail, zpass) =>
-                ctx.gl().stencil_op(fail, zfail, zpass),
-            WebGLCommand::StencilOpSeparate(face, fail, zfail, zpass) =>
-                ctx.gl().stencil_op_separate(face, fail, zfail, zpass),
-            WebGLCommand::GetActiveAttrib(program_id, index, chan) =>
-                Self::active_attrib(ctx.gl(), program_id, index, chan),
-            WebGLCommand::GetActiveUniform(program_id, index, chan) =>
-                Self::active_uniform(ctx.gl(), program_id, index, chan),
-            WebGLCommand::GetAttribLocation(program_id, name, chan) =>
-                Self::attrib_location(ctx.gl(), program_id, name, chan),
-            WebGLCommand::GetVertexAttrib(index, pname, chan) =>
-                Self::vertex_attrib(ctx.gl(), index, pname, chan),
-            WebGLCommand::GetVertexAttribOffset(index, pname, chan) =>
-                Self::vertex_attrib_offset(ctx.gl(), index, pname, chan),
-            WebGLCommand::GetBufferParameter(target, param_id, chan) =>
-                Self::buffer_parameter(ctx.gl(), target, param_id, chan),
-            WebGLCommand::GetParameter(param_id, chan) =>
-                Self::parameter(ctx.gl(), param_id, chan),
-            WebGLCommand::GetProgramParameter(program_id, param_id, chan) =>
-                Self::program_parameter(ctx.gl(), program_id, param_id, chan),
-            WebGLCommand::GetShaderParameter(shader_id, param_id, chan) =>
-                Self::shader_parameter(ctx.gl(), shader_id, param_id, chan),
-            WebGLCommand::GetShaderPrecisionFormat(shader_type, precision_type, chan) =>
-                Self::shader_precision_format(ctx.gl(), shader_type, precision_type, chan),
-            WebGLCommand::GetExtensions(chan) =>
-                Self::get_extensions(ctx.gl(), chan),
-            WebGLCommand::GetUniformLocation(program_id, name, chan) =>
-                Self::uniform_location(ctx.gl(), program_id, name, chan),
-            WebGLCommand::GetShaderInfoLog(shader_id, chan) =>
-                Self::shader_info_log(ctx.gl(), shader_id, chan),
-            WebGLCommand::GetProgramInfoLog(program_id, chan) =>
-                Self::program_info_log(ctx.gl(), program_id, chan),
-            WebGLCommand::CompileShader(shader_id, source) =>
-                Self::compile_shader(ctx.gl(), shader_id, source),
-            WebGLCommand::CreateBuffer(chan) =>
-                Self::create_buffer(ctx.gl(), chan),
-            WebGLCommand::CreateFramebuffer(chan) =>
-                Self::create_framebuffer(ctx.gl(), chan),
-            WebGLCommand::CreateRenderbuffer(chan) =>
-                Self::create_renderbuffer(ctx.gl(), chan),
-            WebGLCommand::CreateTexture(chan) =>
-                Self::create_texture(ctx.gl(), chan),
-            WebGLCommand::CreateProgram(chan) =>
-                Self::create_program(ctx.gl(), chan),
-            WebGLCommand::CreateShader(shader_type, chan) =>
-                Self::create_shader(ctx.gl(), shader_type, chan),
-            WebGLCommand::DeleteBuffer(id) =>
-                ctx.gl().delete_buffers(&[id.get()]),
-            WebGLCommand::DeleteFramebuffer(id) =>
-                ctx.gl().delete_framebuffers(&[id.get()]),
-            WebGLCommand::DeleteRenderbuffer(id) =>
-                ctx.gl().delete_renderbuffers(&[id.get()]),
-            WebGLCommand::DeleteTexture(id) =>
-                ctx.gl().delete_textures(&[id.get()]),
-            WebGLCommand::DeleteProgram(id) =>
-                ctx.gl().delete_program(id.get()),
-            WebGLCommand::DeleteShader(id) =>
-                ctx.gl().delete_shader(id.get()),
-            WebGLCommand::BindBuffer(target, id) =>
-                ctx.gl().bind_buffer(target, id.map_or(0, WebGLBufferId::get)),
-            WebGLCommand::BindFramebuffer(target, request) =>
-                Self::bind_framebuffer(ctx.gl(), target, request, ctx),
-            WebGLCommand::BindRenderbuffer(target, id) =>
-                ctx.gl().bind_renderbuffer(target, id.map_or(0, WebGLRenderbufferId::get)),
-            WebGLCommand::BindTexture(target, id) =>
-                ctx.gl().bind_texture(target, id.map_or(0, WebGLTextureId::get)),
-            WebGLCommand::LinkProgram(program_id) =>
-                ctx.gl().link_program(program_id.get()),
-            WebGLCommand::Uniform1f(uniform_id, v) =>
-                ctx.gl().uniform_1f(uniform_id, v),
-            WebGLCommand::Uniform1fv(uniform_id, v) =>
-                ctx.gl().uniform_1fv(uniform_id, &v),
-            WebGLCommand::Uniform1i(uniform_id, v) =>
-                ctx.gl().uniform_1i(uniform_id, v),
-            WebGLCommand::Uniform1iv(uniform_id, v) =>
-                ctx.gl().uniform_1iv(uniform_id, &v),
-            WebGLCommand::Uniform2f(uniform_id, x, y) =>
-                ctx.gl().uniform_2f(uniform_id, x, y),
-            WebGLCommand::Uniform2fv(uniform_id, v) =>
-                ctx.gl().uniform_2fv(uniform_id, &v),
-            WebGLCommand::Uniform2i(uniform_id, x, y) =>
-                ctx.gl().uniform_2i(uniform_id, x, y),
-            WebGLCommand::Uniform2iv(uniform_id, v) =>
-                ctx.gl().uniform_2iv(uniform_id, &v),
-            WebGLCommand::Uniform3f(uniform_id, x, y, z) =>
-                ctx.gl().uniform_3f(uniform_id, x, y, z),
-            WebGLCommand::Uniform3fv(uniform_id, v) =>
-                ctx.gl().uniform_3fv(uniform_id, &v),
-            WebGLCommand::Uniform3i(uniform_id, x, y, z) =>
-                ctx.gl().uniform_3i(uniform_id, x, y, z),
-            WebGLCommand::Uniform3iv(uniform_id, v) =>
-                ctx.gl().uniform_3iv(uniform_id, &v),
-            WebGLCommand::Uniform4f(uniform_id, x, y, z, w) =>
-                ctx.gl().uniform_4f(uniform_id, x, y, z, w),
-            WebGLCommand::Uniform4fv(uniform_id, v) =>
-                ctx.gl().uniform_4fv(uniform_id, &v),
-            WebGLCommand::Uniform4i(uniform_id, x, y, z, w) =>
-                ctx.gl().uniform_4i(uniform_id, x, y, z, w),
-            WebGLCommand::Uniform4iv(uniform_id, v) =>
-                ctx.gl().uniform_4iv(uniform_id, &v),
-            WebGLCommand::UniformMatrix2fv(uniform_id, transpose,  v) =>
-                ctx.gl().uniform_matrix_2fv(uniform_id, transpose, &v),
-            WebGLCommand::UniformMatrix3fv(uniform_id, transpose,  v) =>
-                ctx.gl().uniform_matrix_3fv(uniform_id, transpose, &v),
-            WebGLCommand::UniformMatrix4fv(uniform_id, transpose,  v) =>
-                ctx.gl().uniform_matrix_4fv(uniform_id, transpose, &v),
-            WebGLCommand::UseProgram(program_id) =>
-                ctx.gl().use_program(program_id.get()),
-            WebGLCommand::ValidateProgram(program_id) =>
-                ctx.gl().validate_program(program_id.get()),
-            WebGLCommand::VertexAttrib(attrib_id, x, y, z, w) =>
-                ctx.gl().vertex_attrib_4f(attrib_id, x, y, z, w),
-            WebGLCommand::VertexAttribPointer2f(attrib_id, size, normalized, stride, offset) =>
-                ctx.gl().vertex_attrib_pointer_f32(attrib_id, size, normalized, stride, offset),
-            WebGLCommand::VertexAttribPointer(attrib_id, size, data_type, normalized, stride, offset) =>
-                ctx.gl().vertex_attrib_pointer(attrib_id, size, data_type, normalized, stride, offset),
-            WebGLCommand::Viewport(x, y, width, height) =>
-                ctx.gl().viewport(x, y, width, height),
-            WebGLCommand::TexImage2D(target, level, internal, width, height, format, data_type, data) =>
-                ctx.gl().tex_image_2d(target, level, internal, width, height, /*border*/0, format, data_type, Some(&data)),
-            WebGLCommand::TexParameteri(target, name, value) =>
-                ctx.gl().tex_parameter_i(target, name, value),
-            WebGLCommand::TexParameterf(target, name, value) =>
-                ctx.gl().tex_parameter_f(target, name, value),
-            WebGLCommand::TexSubImage2D(target, level, xoffset, yoffset, x, y, width, height, data) =>
-                ctx.gl().tex_sub_image_2d(target, level, xoffset, yoffset, x, y, width, height, &data),
-            WebGLCommand::DrawingBufferWidth(sender) =>
-                sender.send(ctx.borrow_draw_buffer().unwrap().size().width).unwrap(),
-            WebGLCommand::DrawingBufferHeight(sender) =>
-                sender.send(ctx.borrow_draw_buffer().unwrap().size().height).unwrap(),
-            WebGLCommand::Finish(sender) =>
-                Self::finish(ctx.gl(), sender),
-            WebGLCommand::Flush =>
-                ctx.gl().flush(),
-            WebGLCommand::GenerateMipmap(target) =>
-                ctx.gl().generate_mipmap(target),
-            WebGLCommand::CreateVertexArray(chan) =>
-                Self::create_vertex_array(ctx.gl(), chan),
-            WebGLCommand::DeleteVertexArray(id) =>
-                ctx.gl().delete_vertex_arrays(&[id.get()]),
-            WebGLCommand::BindVertexArray(id) =>
-                ctx.gl().bind_vertex_array(id.map_or(0, WebGLVertexArrayId::get)),
-        }
-
-        // FIXME: Use debug_assertions once tests are run with them
-        let error = ctx.gl().get_error();
-        assert!(error == gl::NO_ERROR, "Unexpected WebGL error: 0x{:x} ({})", error, error);
-    }
-
-    fn read_pixels(gl: &gl::Gl, x: i32, y: i32, width: i32, height: i32, format: u32, pixel_type: u32,
-                   chan: MsgSender<Vec<u8>>) {
-      let result = gl.read_pixels(x, y, width, height, format, pixel_type);
-      chan.send(result).unwrap()
-    }
-
-    fn active_attrib(gl: &gl::Gl,
-                     program_id: WebGLProgramId,
-                     index: u32,
-                     chan: MsgSender<WebGLResult<(i32, u32, String)>>) {
-        let result = if index >= gl.get_program_iv(program_id.get(), gl::ACTIVE_ATTRIBUTES) as u32 {
-            Err(WebGLError::InvalidValue)
-        } else {
-            Ok(gl.get_active_attrib(program_id.get(), index))
-        };
-        chan.send(result).unwrap();
-    }
-
-    fn active_uniform(gl: &gl::Gl,
-                      program_id: WebGLProgramId,
-                      index: u32,
-                      chan: MsgSender<WebGLResult<(i32, u32, String)>>) {
-        let result = if index >= gl.get_program_iv(program_id.get(), gl::ACTIVE_UNIFORMS) as u32 {
-            Err(WebGLError::InvalidValue)
-        } else {
-            Ok(gl.get_active_uniform(program_id.get(), index))
-        };
-        chan.send(result).unwrap();
-    }
-
-    fn attrib_location(gl: &gl::Gl,
-                       program_id: WebGLProgramId,
-                       name: String,
-                       chan: MsgSender<Option<i32>> ) {
-        let attrib_location = gl.get_attrib_location(program_id.get(), &name);
-
-        let attrib_location = if attrib_location == -1 {
-            None
-        } else {
-            Some(attrib_location)
-        };
-
-        chan.send(attrib_location).unwrap();
-    }
-
-    fn parameter(gl: &gl::Gl,
-                 param_id: u32,
-                 chan: MsgSender<WebGLResult<WebGLParameter>>) {
-        let result = match param_id {
-            gl::ACTIVE_TEXTURE |
-            gl::ALPHA_BITS |
-            gl::BLEND_DST_ALPHA |
-            gl::BLEND_DST_RGB |
-            gl::BLEND_EQUATION_ALPHA |
-            gl::BLEND_EQUATION_RGB |
-            gl::BLEND_SRC_ALPHA |
-            gl::BLEND_SRC_RGB |
-            gl::BLUE_BITS |
-            gl::CULL_FACE_MODE |
-            gl::DEPTH_BITS |
-            gl::DEPTH_FUNC |
-            gl::FRONT_FACE |
-            //gl::GENERATE_MIPMAP_HINT |
-            gl::GREEN_BITS |
-            //gl::IMPLEMENTATION_COLOR_READ_FORMAT |
-            //gl::IMPLEMENTATION_COLOR_READ_TYPE |
-            gl::MAX_COMBINED_TEXTURE_IMAGE_UNITS |
-            gl::MAX_CUBE_MAP_TEXTURE_SIZE |
-            //gl::MAX_FRAGMENT_UNIFORM_VECTORS |
-            gl::MAX_RENDERBUFFER_SIZE |
-            gl::MAX_TEXTURE_IMAGE_UNITS |
-            gl::MAX_TEXTURE_SIZE |
-            //gl::MAX_VARYING_VECTORS |
-            gl::MAX_VERTEX_ATTRIBS |
-            gl::MAX_VERTEX_TEXTURE_IMAGE_UNITS |
-            //gl::MAX_VERTEX_UNIFORM_VECTORS |
-            gl::PACK_ALIGNMENT |
-            gl::RED_BITS |
-            gl::SAMPLE_BUFFERS |
-            gl::SAMPLES |
-            gl::STENCIL_BACK_FAIL |
-            gl::STENCIL_BACK_FUNC |
-            gl::STENCIL_BACK_PASS_DEPTH_FAIL |
-            gl::STENCIL_BACK_PASS_DEPTH_PASS |
-            gl::STENCIL_BACK_REF |
-            gl::STENCIL_BACK_VALUE_MASK |
-            gl::STENCIL_BACK_WRITEMASK |
-            gl::STENCIL_BITS |
-            gl::STENCIL_CLEAR_VALUE |
-            gl::STENCIL_FAIL |
-            gl::STENCIL_FUNC |
-            gl::STENCIL_PASS_DEPTH_FAIL |
-            gl::STENCIL_PASS_DEPTH_PASS |
-            gl::STENCIL_REF |
-            gl::STENCIL_VALUE_MASK |
-            gl::STENCIL_WRITEMASK |
-            gl::SUBPIXEL_BITS |
-            gl::UNPACK_ALIGNMENT =>
-            //gl::UNPACK_COLORSPACE_CONVERSION_WEBGL =>
-                Ok(WebGLParameter::Int(gl.get_integer_v(param_id))),
-
-            gl::BLEND |
-            gl::CULL_FACE |
-            gl::DEPTH_TEST |
-            gl::DEPTH_WRITEMASK |
-            gl::DITHER |
-            gl::POLYGON_OFFSET_FILL |
-            gl::SAMPLE_COVERAGE_INVERT |
-            gl::STENCIL_TEST =>
-            //gl::UNPACK_FLIP_Y_WEBGL |
-            //gl::UNPACK_PREMULTIPLY_ALPHA_WEBGL =>
-                Ok(WebGLParameter::Bool(gl.get_boolean_v(param_id) != 0)),
-
-            gl::DEPTH_CLEAR_VALUE |
-            gl::LINE_WIDTH |
-            gl::POLYGON_OFFSET_FACTOR |
-            gl::POLYGON_OFFSET_UNITS |
-            gl::SAMPLE_COVERAGE_VALUE =>
-                Ok(WebGLParameter::Float(gl.get_float_v(param_id))),
-
-            gl::VERSION => Ok(WebGLParameter::String("WebGL 1.0".to_owned())),
-            gl::RENDERER |
-            gl::VENDOR => Ok(WebGLParameter::String("Mozilla/Servo".to_owned())),
-            gl::SHADING_LANGUAGE_VERSION => Ok(WebGLParameter::String("WebGL GLSL ES 1.0".to_owned())),
-
-            // TODO(zbarsky, emilio): Implement support for the following valid parameters
-            // Float32Array
-            gl::ALIASED_LINE_WIDTH_RANGE |
-            //gl::ALIASED_POINT_SIZE_RANGE |
-            //gl::BLEND_COLOR |
-            gl::COLOR_CLEAR_VALUE |
-            gl::DEPTH_RANGE |
-
-            // WebGLBuffer
-            gl::ARRAY_BUFFER_BINDING |
-            gl::ELEMENT_ARRAY_BUFFER_BINDING |
-
-            // WebGLFrameBuffer
-            gl::FRAMEBUFFER_BINDING |
-
-            // WebGLRenderBuffer
-            gl::RENDERBUFFER_BINDING |
-
-            // WebGLProgram
-            gl::CURRENT_PROGRAM |
-
-            // WebGLTexture
-            gl::TEXTURE_BINDING_2D |
-            gl::TEXTURE_BINDING_CUBE_MAP |
-
-            // sequence<GlBoolean>
-            gl::COLOR_WRITEMASK |
-
-            // Uint32Array
-            gl::COMPRESSED_TEXTURE_FORMATS |
-
-            // Int32Array
-            gl::MAX_VIEWPORT_DIMS |
-            gl::SCISSOR_BOX |
-            gl::VIEWPORT => Err(WebGLError::InvalidEnum),
-
-            // Invalid parameters
-            _ => Err(WebGLError::InvalidEnum)
-        };
-
-        chan.send(result).unwrap();
-    }
-
-    fn finish(gl: &gl::Gl, chan: MsgSender<()>) {
-        gl.finish();
-        chan.send(()).unwrap();
-    }
-
-    fn vertex_attrib(gl: &gl::Gl,
-                     index: u32,
-                     pname: u32,
-                     chan: MsgSender<WebGLResult<WebGLParameter>>) {
-        let result = if index >= gl.get_integer_v(gl::MAX_VERTEX_ATTRIBS) as u32 {
-            Err(WebGLError::InvalidValue)
-        } else {
-            match pname {
-                gl::VERTEX_ATTRIB_ARRAY_ENABLED |
-                gl::VERTEX_ATTRIB_ARRAY_NORMALIZED =>
-                    Ok(WebGLParameter::Bool(gl.get_vertex_attrib_iv(index, pname) != 0)),
-                gl::VERTEX_ATTRIB_ARRAY_SIZE |
-                gl::VERTEX_ATTRIB_ARRAY_STRIDE |
-                gl::VERTEX_ATTRIB_ARRAY_TYPE =>
-                    Ok(WebGLParameter::Int(gl.get_vertex_attrib_iv(index, pname))),
-                gl::CURRENT_VERTEX_ATTRIB =>
-                    Ok(WebGLParameter::FloatArray(gl.get_vertex_attrib_fv(index, pname))),
-                // gl::VERTEX_ATTRIB_ARRAY_BUFFER_BINDING should return WebGLBuffer
-                _ => Err(WebGLError::InvalidEnum),
-            }
-        };
-
-        chan.send(result).unwrap();
-    }
-
-    fn vertex_attrib_offset(gl: &gl::Gl,
-                            index: u32,
-                            pname: u32,
-                            chan: MsgSender<WebGLResult<isize>>) {
-        let result = match pname {
-                gl::VERTEX_ATTRIB_ARRAY_POINTER => Ok(gl.get_vertex_attrib_pointer_v(index, pname)),
-                _ => Err(WebGLError::InvalidEnum),
-        };
-
-        chan.send(result).unwrap();
-    }
-
-    fn buffer_parameter(gl: &gl::Gl,
-                        target: u32,
-                        param_id: u32,
-                        chan: MsgSender<WebGLResult<WebGLParameter>>) {
-        let result = match param_id {
-            gl::BUFFER_SIZE |
-            gl::BUFFER_USAGE =>
-                Ok(WebGLParameter::Int(gl.get_buffer_parameter_iv(target, param_id))),
-            _ => Err(WebGLError::InvalidEnum),
-        };
-
-        chan.send(result).unwrap();
-    }
-
-    fn program_parameter(gl: &gl::Gl,
-                         program_id: WebGLProgramId,
-                         param_id: u32,
-                         chan: MsgSender<WebGLResult<WebGLParameter>>) {
-        let result = match param_id {
-            gl::DELETE_STATUS |
-            gl::LINK_STATUS |
-            gl::VALIDATE_STATUS =>
-                Ok(WebGLParameter::Bool(gl.get_program_iv(program_id.get(), param_id) != 0)),
-            gl::ATTACHED_SHADERS |
-            gl::ACTIVE_ATTRIBUTES |
-            gl::ACTIVE_UNIFORMS =>
-                Ok(WebGLParameter::Int(gl.get_program_iv(program_id.get(), param_id))),
-            _ => Err(WebGLError::InvalidEnum),
-        };
-
-        chan.send(result).unwrap();
-    }
-
-    fn shader_parameter(gl: &gl::Gl,
-                        shader_id: WebGLShaderId,
-                        param_id: u32,
-                        chan: MsgSender<WebGLResult<WebGLParameter>>) {
-        let result = match param_id {
-            gl::SHADER_TYPE =>
-                Ok(WebGLParameter::Int(gl.get_shader_iv(shader_id.get(), param_id))),
-            gl::DELETE_STATUS |
-            gl::COMPILE_STATUS =>
-                Ok(WebGLParameter::Bool(gl.get_shader_iv(shader_id.get(), param_id) != 0)),
-            _ => Err(WebGLError::InvalidEnum),
-        };
-
-        chan.send(result).unwrap();
-    }
-
-    fn shader_precision_format(gl: &gl::Gl,
-                               shader_type: u32,
-                               precision_type: u32,
-                               chan: MsgSender<WebGLResult<(i32, i32, i32)>>) {
-       
-        let result = match precision_type {
-            gl::LOW_FLOAT |
-            gl::MEDIUM_FLOAT |
-            gl::HIGH_FLOAT |
-            gl::LOW_INT |
-            gl::MEDIUM_INT |
-            gl::HIGH_INT => {
-                Ok(gl.get_shader_precision_format(shader_type, precision_type))
-            },
-            _=> {
-                Err(WebGLError::InvalidEnum)
-            }
-        };
-
-        chan.send(result).unwrap();
-    }
-
-    fn get_extensions(gl: &gl::Gl, chan: MsgSender<String>) {
-        chan.send(gl.get_string(gl::EXTENSIONS)).unwrap();
-    }
-
-    fn uniform_location(gl: &gl::Gl,
-                        program_id: WebGLProgramId,
-                        name: String,
-                        chan: MsgSender<Option<i32>>) {
-        let location = gl.get_uniform_location(program_id.get(), &name);
-        let location = if location == -1 {
-            None
-        } else {
-            Some(location)
-        };
-
-        chan.send(location).unwrap();
-    }
-
-
-    fn shader_info_log(gl: &gl::Gl, shader_id: WebGLShaderId, chan: MsgSender<String>) {
-        let log = gl.get_shader_info_log(shader_id.get());
-        chan.send(log).unwrap();
-    }
-
-    fn program_info_log(gl: &gl::Gl, program_id: WebGLProgramId, chan: MsgSender<String>) {
-        let log = gl.get_program_info_log(program_id.get());
-        chan.send(log).unwrap();
-    }
-
-    fn create_buffer(gl: &gl::Gl, chan: MsgSender<Option<WebGLBufferId>>) {
-        let buffer = gl.gen_buffers(1)[0];
-        let buffer = if buffer == 0 {
-            None
-        } else {
-            Some(unsafe { WebGLBufferId::new(buffer) })
-        };
-        chan.send(buffer).unwrap();
-    }
-
-    fn create_framebuffer(gl: &gl::Gl, chan: MsgSender<Option<WebGLFramebufferId>>) {
-        let framebuffer = gl.gen_framebuffers(1)[0];
-        let framebuffer = if framebuffer == 0 {
-            None
-        } else {
-            Some(unsafe { WebGLFramebufferId::new(framebuffer) })
-        };
-        chan.send(framebuffer).unwrap();
-    }
-
-
-    fn create_renderbuffer(gl: &gl::Gl, chan: MsgSender<Option<WebGLRenderbufferId>>) {
-        let renderbuffer = gl.gen_renderbuffers(1)[0];
-        let renderbuffer = if renderbuffer == 0 {
-            None
-        } else {
-            Some(unsafe { WebGLRenderbufferId::new(renderbuffer) })
-        };
-        chan.send(renderbuffer).unwrap();
-    }
-
-    fn create_texture(gl: &gl::Gl, chan: MsgSender<Option<WebGLTextureId>>) {
-        let texture = gl.gen_textures(1)[0];
-        let texture = if texture == 0 {
-            None
-        } else {
-            Some(unsafe { WebGLTextureId::new(texture) })
-        };
-        chan.send(texture).unwrap();
-    }
-
-
-    fn create_program(gl: &gl::Gl, chan: MsgSender<Option<WebGLProgramId>>) {
-        let program = gl.create_program();
-        let program = if program == 0 {
-            None
-        } else {
-            Some(unsafe { WebGLProgramId::new(program) })
-        };
-        chan.send(program).unwrap();
-    }
-
-    fn create_shader(gl: &gl::Gl, shader_type: u32, chan: MsgSender<Option<WebGLShaderId>>) {
-        let shader = gl.create_shader(shader_type);
-        let shader = if shader == 0 {
-            None
-        } else {
-            Some(unsafe { WebGLShaderId::new(shader) })
-        };
-        chan.send(shader).unwrap();
-    }
-
-    fn create_vertex_array(gl: &gl::Gl, chan: MsgSender<Option<WebGLVertexArrayId>>) {
-        let vao = gl.gen_vertex_arrays(1)[0];
-        let vao = if vao == 0 {
-            None
-        } else {
-            Some(unsafe { WebGLVertexArrayId::new(vao) })
-        };
-        chan.send(vao).unwrap();
-    }
-
-    #[inline]
-    fn bind_framebuffer<Native: NativeGLContextMethods>(gl: &gl::Gl,
-                                                        target: u32,
-                                                        request: WebGLFramebufferBindingRequest,
-                                                        ctx: &GLContext<Native>) {
-        let id = match request {
-            WebGLFramebufferBindingRequest::Explicit(id) => id.get(),
-            WebGLFramebufferBindingRequest::Default =>
-                ctx.borrow_draw_buffer().unwrap().get_framebuffer(),
-        };
-
-        gl.bind_framebuffer(target, id);
-    }
-
-
-    #[inline]
-    fn compile_shader(gl: &gl::Gl, shader_id: WebGLShaderId, source: String) {
-        gl.shader_source(shader_id.get(), &[source.as_bytes()]);
-        gl.compile_shader(shader_id.get());
-    }
-}