Bug 1494898. Update webrender to commit d7a6d081384ce0da9dd359b0cf4b9f758aab1b67
authorJeff Muizelaar <jmuizelaar@mozilla.com>
Sat, 29 Sep 2018 14:19:27 -0400
changeset 438871 ab5269bd3c218e44e98c31edee99cdd185d0beda
parent 438870 0fcb85b4c2f2580df3de46af1c10b260dd156a44
child 438872 85ee60997e57b500bff9f2588837d3ee6df01f82
push id108428
push userjmuizelaar@mozilla.com
push dateSat, 29 Sep 2018 18:19:45 +0000
treeherdermozilla-inbound@f1dcdcc1674b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1494898
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1494898. Update webrender to commit d7a6d081384ce0da9dd359b0cf4b9f758aab1b67
gfx/webrender/src/clip.rs
gfx/webrender/src/display_list_flattener.rs
gfx/webrender/src/frame_builder.rs
gfx/webrender/src/image.rs
gfx/webrender/src/picture.rs
gfx/webrender/src/prim_store.rs
gfx/webrender/src/render_backend.rs
gfx/webrender/src/renderer.rs
gfx/webrender/src/resource_cache.rs
gfx/webrender/src/scene_builder.rs
gfx/webrender/src/tiling.rs
gfx/webrender_api/src/api.rs
gfx/webrender_api/src/units.rs
gfx/webrender_bindings/revision.txt
gfx/wrench/src/rawtest.rs
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -417,30 +417,30 @@ impl ClipStore {
     pub fn get_instance_from_range(
         &self,
         node_range: &ClipNodeRange,
         index: u32,
     ) -> &ClipNodeInstance {
         &self.clip_node_instances[(node_range.first + index) as usize]
     }
 
-    // Notify the clip store that a new rasterization root has been created.
+    // Notify the clip store that a new surface has been created.
     // This means any clips from an earlier root should be collected rather
     // than applied on the primitive itself.
-    pub fn push_raster_root(
+    pub fn push_surface(
         &mut self,
-        raster_spatial_node_index: SpatialNodeIndex,
+        spatial_node_index: SpatialNodeIndex,
     ) {
         self.clip_node_collectors.push(
-            ClipNodeCollector::new(raster_spatial_node_index),
+            ClipNodeCollector::new(spatial_node_index),
         );
     }
 
-    // Mark the end of a rasterization root.
-    pub fn pop_raster_root(
+    // Mark the end of a rendering surface.
+    pub fn pop_surface(
         &mut self,
     ) -> ClipNodeCollector {
         self.clip_node_collectors.pop().unwrap()
     }
 
     // The main interface other code uses. Given a local primitive, positioning
     // information, and a clip chain id, build an optimized clip chain instance.
     pub fn build_clip_chain_instance(
@@ -469,17 +469,17 @@ impl ClipStore {
 
         // for each clip chain node
         while current_clip_chain_id != ClipChainId::NONE {
             let clip_chain_node = &self.clip_chain_nodes[current_clip_chain_id.0 as usize];
 
             // Check if any clip node index should actually be
             // handled during compositing of a rasterization root.
             match self.clip_node_collectors.iter_mut().find(|c| {
-                clip_chain_node.spatial_node_index < c.raster_root
+                clip_chain_node.spatial_node_index < c.spatial_node_index
             }) {
                 Some(collector) => {
                     collector.insert(current_clip_chain_id);
                 }
                 None => {
                     if !add_clip_node_to_current_chain(
                         clip_chain_node.handle,
                         clip_chain_node.spatial_node_index,
@@ -1181,26 +1181,26 @@ pub fn project_inner_rect(
         WorldSize::new(xs[2] - xs[1], ys[2] - ys[1]),
     ))
 }
 
 // Collects a list of unique clips to be applied to a rasterization
 // root at the end of primitive preparation.
 #[derive(Debug)]
 pub struct ClipNodeCollector {
-    raster_root: SpatialNodeIndex,
+    spatial_node_index: SpatialNodeIndex,
     clips: FastHashSet<ClipChainId>,
 }
 
 impl ClipNodeCollector {
     pub fn new(
-        raster_root: SpatialNodeIndex,
+        spatial_node_index: SpatialNodeIndex,
     ) -> Self {
         ClipNodeCollector {
-            raster_root,
+            spatial_node_index,
             clips: FastHashSet::default(),
         }
     }
 
     pub fn insert(
         &mut self,
         clip_chain_id: ClipChainId,
     ) {
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -8,17 +8,17 @@ use api::{ClipId, ColorF, ComplexClipReg
 use api::{DevicePixelScale, DeviceUintRect, DisplayItemRef, ExtendMode, ExternalScrollId};
 use api::{FilterOp, FontInstanceKey, GlyphInstance, GlyphOptions, RasterSpace, GradientStop};
 use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, LayoutPoint, ColorDepth};
 use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D};
 use api::{LineOrientation, LineStyle, LocalClip, NinePatchBorderSource, PipelineId};
 use api::{PropertyBinding, ReferenceFrame, RepeatMode, ScrollFrameDisplayItem, ScrollSensitivity};
 use api::{Shadow, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect};
 use api::{ClipMode, TransformStyle, YuvColorSpace, YuvData};
-use clip::{ClipDataInterner, ClipChainId, ClipRegion, ClipItemKey, ClipStore};
+use clip::{ClipChainId, ClipRegion, ClipItemKey, ClipStore};
 use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex};
 use euclid::vec2;
 use frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig};
 use glyph_rasterizer::FontInstance;
 use gpu_cache::GpuCacheHandle;
 use gpu_types::BrushFlags;
 use hit_test::{HitTestingItem, HitTestingRun};
 use image::simplify_repeated_primitive;
@@ -26,28 +26,22 @@ use internal_types::{FastHashMap, FastHa
 use picture::{PictureCompositeMode, PictureIdGenerator, PicturePrimitive};
 use prim_store::{BrushKind, BrushPrimitive, BrushSegmentDescriptor};
 use prim_store::{EdgeAaSegmentMask, ImageSource, PrimitiveOpacity};
 use prim_store::{BorderSource, BrushSegment, BrushSegmentVec, PrimitiveContainer, PrimitiveIndex, PrimitiveStore};
 use prim_store::{OpacityBinding, ScrollNodeAndClipChain, TextRunPrimitive};
 use render_backend::{DocumentView};
 use resource_cache::{FontInstanceMap, ImageRequest};
 use scene::{Scene, ScenePipeline, StackingContextHelpers};
+use scene_builder::DocumentResources;
 use spatial_node::{SpatialNodeType, StickyFrameInfo};
 use std::{f32, mem};
-use tiling::{CompositeOps, ScrollbarPrimitive};
+use tiling::{CompositeOps};
 use util::{MaxRect, RectHelpers};
 
-static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF {
-    r: 0.3,
-    g: 0.3,
-    b: 0.3,
-    a: 0.6,
-};
-
 #[derive(Debug, Copy, Clone)]
 struct ClipNode {
     id: ClipChainId,
     count: usize,
 }
 
 impl ClipNode {
     fn new(id: ClipChainId, count: usize) -> ClipNode {
@@ -139,51 +133,49 @@ pub struct DisplayListFlattener<'a> {
     sc_stack: Vec<FlattenedStackingContext>,
 
     /// A stack of the currently active shadows
     shadow_stack: Vec<(Shadow, PrimitiveIndex)>,
 
     /// The stack keeping track of the root clip chains associated with pipelines.
     pipeline_clip_chain_stack: Vec<ClipChainId>,
 
-    /// A list of scrollbar primitives.
-    pub scrollbar_prims: Vec<ScrollbarPrimitive>,
-
     /// The store of primitives.
     pub prim_store: PrimitiveStore,
 
     /// Information about all primitives involved in hit testing.
     pub hit_testing_runs: Vec<HitTestingRun>,
 
     /// The store which holds all complex clipping information.
     pub clip_store: ClipStore,
 
     /// The configuration to use for the FrameBuilder. We consult this in
     /// order to determine the default font.
     pub config: FrameBuilderConfig,
 
-    /// Reference to the clip interner for this document.
-    clip_interner: &'a mut ClipDataInterner,
+    /// Reference to the document resources, which contains
+    /// shared (interned) data between display lists.
+    resources: &'a mut DocumentResources,
 
     /// The estimated count of primtives we expect to encounter during flattening.
     prim_count_estimate: usize,
 }
 
 impl<'a> DisplayListFlattener<'a> {
     pub fn create_frame_builder(
         scene: &Scene,
         clip_scroll_tree: &mut ClipScrollTree,
         font_instances: FontInstanceMap,
         view: &DocumentView,
         output_pipelines: &FastHashSet<PipelineId>,
         frame_builder_config: &FrameBuilderConfig,
         new_scene: &mut Scene,
         scene_id: u64,
         picture_id_generator: &mut PictureIdGenerator,
-        clip_interner: &mut ClipDataInterner,
+        resources: &mut DocumentResources,
     ) -> FrameBuilder {
         // We checked that the root pipeline is available on the render backend.
         let root_pipeline_id = scene.root_pipeline_id.unwrap();
         let root_pipeline = scene.pipelines.get(&root_pipeline_id).unwrap();
 
         let background_color = root_pipeline
             .background_color
             .and_then(|color| if color.a > 0.0 { Some(color) } else { None });
@@ -191,24 +183,23 @@ impl<'a> DisplayListFlattener<'a> {
         let mut flattener = DisplayListFlattener {
             scene,
             clip_scroll_tree,
             font_instances,
             config: *frame_builder_config,
             output_pipelines,
             id_to_index_mapper: ClipIdToIndexMapper::default(),
             hit_testing_runs: Vec::new(),
-            scrollbar_prims: Vec::new(),
             shadow_stack: Vec::new(),
             sc_stack: Vec::new(),
             pipeline_clip_chain_stack: vec![ClipChainId::NONE],
             prim_store: PrimitiveStore::new(),
             clip_store: ClipStore::new(),
             picture_id_generator,
-            clip_interner,
+            resources,
             prim_count_estimate: 0,
         };
 
         flattener.push_root(
             root_pipeline_id,
             &root_pipeline.viewport_size,
             &root_pipeline.content_size,
         );
@@ -254,17 +245,16 @@ impl<'a> DisplayListFlattener<'a> {
 
     fn flatten_root(&mut self, pipeline: &'a ScenePipeline, frame_size: &LayoutSize) {
         let pipeline_id = pipeline.pipeline_id;
         let reference_frame_info = self.simple_scroll_and_clip_chain(
             &ClipId::root_reference_frame(pipeline_id),
         );
 
         let root_scroll_node = ClipId::root_scroll_node(pipeline_id);
-        let scroll_frame_info = self.simple_scroll_and_clip_chain(&root_scroll_node);
 
         self.push_stacking_context(
             pipeline_id,
             CompositeOps::default(),
             TransformStyle::Flat,
             true,
             true,
             root_scroll_node,
@@ -290,27 +280,16 @@ impl<'a> DisplayListFlattener<'a> {
             }
         }
 
         self.prim_count_estimate += pipeline.display_list.prim_count_estimate();
         self.prim_store.primitives.reserve(self.prim_count_estimate);
 
         self.flatten_items(&mut pipeline.display_list.iter(), pipeline_id, LayoutVector2D::zero());
 
-        if self.config.enable_scrollbars {
-            let scrollbar_rect = LayoutRect::new(LayoutPoint::zero(), LayoutSize::new(10.0, 70.0));
-            let container_rect = LayoutRect::new(LayoutPoint::zero(), *frame_size);
-            self.add_scroll_bar(
-                reference_frame_info.spatial_node_index,
-                &LayoutPrimitiveInfo::new(scrollbar_rect),
-                DEFAULT_SCROLLBAR_COLOR,
-                ScrollbarInfo(scroll_frame_info.spatial_node_index, container_rect),
-            );
-        }
-
         self.pop_stacking_context();
     }
 
     fn flatten_items(
         &mut self,
         traversal: &mut BuiltDisplayListIter<'a>,
         pipeline_id: PipelineId,
         reference_frame_relative_offset: LayoutVector2D,
@@ -814,17 +793,17 @@ impl<'a> DisplayListFlattener<'a> {
         if clip_items.is_empty() {
             parent_clip_chain_id
         } else {
             let mut clip_chain_id = parent_clip_chain_id;
 
             for item in clip_items {
                 // Intern this clip item, and store the handle
                 // in the clip chain node.
-                let handle = self.clip_interner.intern(&item);
+                let handle = self.resources.clip_interner.intern(&item);
 
                 clip_chain_id = self.clip_store
                                     .add_clip_chain_node(
                                         handle,
                                         spatial_node_index,
                                         clip_chain_id,
                                     );
             }
@@ -1332,45 +1311,48 @@ impl<'a> DisplayListFlattener<'a> {
 
         // Intern each clip item in this clip node, and add the interned
         // handle to a clip chain node, parented to form a chain.
         // TODO(gw): We could re-structure this to share some of the
         //           interning and chaining code.
 
         // Build the clip sources from the supplied region.
         let handle = self
+            .resources
             .clip_interner
             .intern(&ClipItemKey::rectangle(clip_region.main, ClipMode::Clip));
 
         parent_clip_chain_index = self
             .clip_store
             .add_clip_chain_node(
                 handle,
                 spatial_node,
                 parent_clip_chain_index,
             );
         clip_count += 1;
 
         if let Some(ref image_mask) = clip_region.image_mask {
             let handle = self
+                .resources
                 .clip_interner
                 .intern(&ClipItemKey::image_mask(image_mask));
 
             parent_clip_chain_index = self
                 .clip_store
                 .add_clip_chain_node(
                     handle,
                     spatial_node,
                     parent_clip_chain_index,
                 );
             clip_count += 1;
         }
 
         for region in clip_region.complex_clips {
             let handle = self
+                .resources
                 .clip_interner
                 .intern(&ClipItemKey::rounded_rect(region.rect, region.radii, region.mode));
 
             parent_clip_chain_index = self
                 .clip_store
                 .add_clip_chain_node(
                     handle,
                     spatial_node,
@@ -1442,19 +1424,20 @@ impl<'a> DisplayListFlattener<'a> {
         } else {
             RasterSpace::Local(1.0)
         };
 
         // Create a picture that the shadow primitives will be added to. If the
         // blur radius is 0, the code in Picture::prepare_for_render will
         // detect this and mark the picture to be drawn directly into the
         // parent picture, which avoids an intermediate surface and blur.
+        let blur_filter = FilterOp::Blur(std_deviation).sanitize();
         let shadow_pic = PicturePrimitive::new_image(
             self.picture_id_generator.next(),
-            Some(PictureCompositeMode::Filter(FilterOp::Blur(std_deviation))),
+            Some(PictureCompositeMode::Filter(blur_filter)),
             false,
             pipeline_id,
             None,
             is_passthrough,
             raster_space,
         );
 
         // Create the primitive to draw the shadow picture into the scene.
@@ -1523,50 +1506,16 @@ impl<'a> DisplayListFlattener<'a> {
         self.add_primitive(
             clip_and_scroll,
             info,
             Vec::new(),
             PrimitiveContainer::Brush(prim),
         );
     }
 
-    pub fn add_scroll_bar(
-        &mut self,
-        spatial_node_index: SpatialNodeIndex,
-        info: &LayoutPrimitiveInfo,
-        color: ColorF,
-        scrollbar_info: ScrollbarInfo,
-    ) {
-        if color.a == 0.0 {
-            return;
-        }
-
-        let prim = BrushPrimitive::new(
-            BrushKind::new_solid(color),
-            None,
-        );
-
-        let prim_index = self.create_primitive(
-            info,
-            ClipChainId::NONE,
-            spatial_node_index,
-            PrimitiveContainer::Brush(prim),
-        );
-
-        self.add_primitive_to_draw_list(
-            prim_index,
-        );
-
-        self.scrollbar_prims.push(ScrollbarPrimitive {
-            prim_index,
-            scroll_frame_index: scrollbar_info.0,
-            frame_rect: scrollbar_info.1,
-        });
-    }
-
     pub fn add_line(
         &mut self,
         clip_and_scroll: ScrollNodeAndClipChain,
         info: &LayoutPrimitiveInfo,
         wavy_line_thickness: f32,
         orientation: LineOrientation,
         line_color: &ColorF,
         style: LineStyle,
@@ -2112,11 +2061,8 @@ struct FlattenedStackingContext {
     leaf_prim_index: PrimitiveIndex,
 
     /// If true, this stacking context establishes a new
     /// 3d rendering context.
     establishes_3d_context: bool,
     participating_in_3d_context: bool,
     has_mix_blend_mode: bool,
 }
-
-#[derive(Debug)]
-pub struct ScrollbarInfo(pub SpatialNodeIndex, pub LayoutRect);
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -10,27 +10,26 @@ use clip_scroll_tree::{ClipScrollTree, R
 use display_list_flattener::{DisplayListFlattener};
 use gpu_cache::GpuCache;
 use gpu_types::{PrimitiveHeaders, TransformPalette, UvRectKind};
 use hit_test::{HitTester, HitTestingRun};
 use internal_types::{FastHashMap};
 use picture::{PictureCompositeMode, PictureSurface, RasterConfig};
 use prim_store::{PrimitiveIndex, PrimitiveStore, SpaceMapper};
 use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
-use render_backend::FrameId;
+use render_backend::{FrameResources, FrameId};
 use render_task::{RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree};
 use resource_cache::{ResourceCache};
 use scene::{ScenePipeline, SceneProperties};
 use segment::SegmentBuilder;
 use spatial_node::SpatialNode;
 use std::f32;
 use std::sync::Arc;
 use tiling::{Frame, RenderPass, RenderPassKind, RenderTargetContext};
-use tiling::{ScrollbarPrimitive, SpecialRenderPasses};
-use util;
+use tiling::{SpecialRenderPasses};
 
 
 #[derive(Clone, Copy, Debug, PartialEq)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub enum ChasePrimitive {
     Nothing,
     Index(PrimitiveIndex),
@@ -42,34 +41,32 @@ impl Default for ChasePrimitive {
         ChasePrimitive::Nothing
     }
 }
 
 #[derive(Clone, Copy)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct FrameBuilderConfig {
-    pub enable_scrollbars: bool,
     pub default_font_render_mode: FontRenderMode,
     pub dual_source_blending_is_supported: bool,
     pub dual_source_blending_is_enabled: bool,
     pub chase_primitive: ChasePrimitive,
 }
 
 /// A builder structure for `tiling::Frame`
 pub struct FrameBuilder {
     screen_rect: DeviceUintRect,
     background_color: Option<ColorF>,
     window_size: DeviceUintSize,
     scene_id: u64,
     pub prim_store: PrimitiveStore,
     pub clip_store: ClipStore,
     pub hit_testing_runs: Vec<HitTestingRun>,
     pub config: FrameBuilderConfig,
-    pub scrollbar_prims: Vec<ScrollbarPrimitive>,
 }
 
 pub struct FrameBuildingContext<'a> {
     pub scene_id: u64,
     pub device_pixel_scale: DevicePixelScale,
     pub scene_properties: &'a SceneProperties,
     pub pipelines: &'a FastHashMap<PipelineId, Arc<ScenePipeline>>,
     pub world_rect: WorldRect,
@@ -80,27 +77,26 @@ pub struct FrameBuildingContext<'a> {
 pub struct FrameBuildingState<'a> {
     pub render_tasks: &'a mut RenderTaskTree,
     pub profile_counters: &'a mut FrameProfileCounters,
     pub clip_store: &'a mut ClipStore,
     pub resource_cache: &'a mut ResourceCache,
     pub gpu_cache: &'a mut GpuCache,
     pub special_render_passes: &'a mut SpecialRenderPasses,
     pub transforms: &'a mut TransformPalette,
-    pub clip_data_store: &'a mut ClipDataStore,
+    pub resources: &'a mut FrameResources,
     pub segment_builder: SegmentBuilder,
 }
 
 pub struct PictureContext {
     pub pipeline_id: PipelineId,
     pub apply_local_clip_rect: bool,
     pub inflation_factor: f32,
     pub allow_subpixel_aa: bool,
     pub is_passthrough: bool,
-    pub establishes_raster_root: bool,
     pub raster_space: RasterSpace,
 }
 
 #[derive(Debug)]
 pub struct PictureState {
     pub tasks: Vec<RenderTaskId>,
     pub has_non_root_coord_system: bool,
     pub is_cacheable: bool,
@@ -130,25 +126,23 @@ impl<'a> PrimitiveContext<'a> {
     }
 }
 
 impl FrameBuilder {
     #[cfg(feature = "replay")]
     pub fn empty() -> Self {
         FrameBuilder {
             hit_testing_runs: Vec::new(),
-            scrollbar_prims: Vec::new(),
             prim_store: PrimitiveStore::new(),
             clip_store: ClipStore::new(),
             screen_rect: DeviceUintRect::zero(),
             window_size: DeviceUintSize::zero(),
             background_color: None,
             scene_id: 0,
             config: FrameBuilderConfig {
-                enable_scrollbars: false,
                 default_font_render_mode: FontRenderMode::Mono,
                 dual_source_blending_is_enabled: true,
                 dual_source_blending_is_supported: false,
                 chase_primitive: ChasePrimitive::Nothing,
             },
         }
     }
 
@@ -156,17 +150,16 @@ impl FrameBuilder {
         screen_rect: DeviceUintRect,
         background_color: Option<ColorF>,
         window_size: DeviceUintSize,
         scene_id: u64,
         flattener: DisplayListFlattener,
     ) -> Self {
         FrameBuilder {
             hit_testing_runs: flattener.hit_testing_runs,
-            scrollbar_prims: flattener.scrollbar_prims,
             prim_store: flattener.prim_store,
             clip_store: flattener.clip_store,
             screen_rect,
             background_color,
             window_size,
             scene_id,
             config: flattener.config,
         }
@@ -181,17 +174,17 @@ impl FrameBuilder {
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         render_tasks: &mut RenderTaskTree,
         special_render_passes: &mut SpecialRenderPasses,
         profile_counters: &mut FrameProfileCounters,
         device_pixel_scale: DevicePixelScale,
         scene_properties: &SceneProperties,
         transform_palette: &mut TransformPalette,
-        clip_data_store: &mut ClipDataStore,
+        resources: &mut FrameResources,
     ) -> Option<RenderTaskId> {
         profile_scope!("cull");
 
         if self.prim_store.primitives.is_empty() {
             return None
         }
         self.prim_store.reset_prim_visibility();
 
@@ -219,17 +212,17 @@ impl FrameBuilder {
         let mut frame_state = FrameBuildingState {
             render_tasks,
             profile_counters,
             clip_store: &mut self.clip_store,
             resource_cache,
             gpu_cache,
             special_render_passes,
             transforms: transform_palette,
-            clip_data_store,
+            resources,
             segment_builder: SegmentBuilder::new(),
         };
 
         let prim_context = PrimitiveContext::new(
             &clip_scroll_tree.spatial_nodes[root_spatial_node_index.0],
             root_spatial_node_index,
         );
 
@@ -285,59 +278,30 @@ impl FrameBuilder {
         pic.raster_config = Some(RasterConfig {
             composite_mode: PictureCompositeMode::Blit,
             surface: Some(PictureSurface::RenderTask(render_task_id)),
             raster_spatial_node_index: ROOT_SPATIAL_NODE_INDEX,
         });
         Some(render_task_id)
     }
 
-    fn update_scroll_bars(&mut self, clip_scroll_tree: &ClipScrollTree, gpu_cache: &mut GpuCache) {
-        static SCROLLBAR_PADDING: f32 = 8.0;
-
-        for scrollbar_prim in &self.scrollbar_prims {
-            let metadata = &mut self.prim_store.primitives[scrollbar_prim.prim_index.0].metadata;
-            let scroll_frame = &clip_scroll_tree.spatial_nodes[scrollbar_prim.scroll_frame_index.0];
-
-            // Invalidate what's in the cache so it will get rebuilt.
-            gpu_cache.invalidate(&metadata.gpu_location);
-
-            let scrollable_distance = scroll_frame.scrollable_size().height;
-            if scrollable_distance <= 0.0 {
-                metadata.local_clip_rect.size = LayoutSize::zero();
-                continue;
-            }
-            let amount_scrolled = -scroll_frame.scroll_offset().y / scrollable_distance;
-
-            let frame_rect = scrollbar_prim.frame_rect;
-            let min_y = frame_rect.origin.y + SCROLLBAR_PADDING;
-            let max_y = frame_rect.origin.y + frame_rect.size.height -
-                (SCROLLBAR_PADDING + metadata.local_rect.size.height);
-
-            metadata.local_rect.origin.x = frame_rect.origin.x + frame_rect.size.width -
-                (metadata.local_rect.size.width + SCROLLBAR_PADDING);
-            metadata.local_rect.origin.y = util::lerp(min_y, max_y, amount_scrolled);
-            metadata.local_clip_rect = metadata.local_rect;
-        }
-    }
-
     pub fn build(
         &mut self,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         frame_id: FrameId,
         clip_scroll_tree: &mut ClipScrollTree,
         pipelines: &FastHashMap<PipelineId, Arc<ScenePipeline>>,
         device_pixel_scale: DevicePixelScale,
         layer: DocumentLayer,
         pan: WorldPoint,
         texture_cache_profile: &mut TextureCacheProfileCounters,
         gpu_cache_profile: &mut GpuCacheProfileCounters,
         scene_properties: &SceneProperties,
-        clip_data_store: &mut ClipDataStore,
+        resources: &mut FrameResources,
     ) -> Frame {
         profile_scope!("build");
         debug_assert!(
             DeviceUintRect::new(DeviceUintPoint::zero(), self.window_size)
                 .contains_rect(&self.screen_rect)
         );
 
         let mut profile_counters = FrameProfileCounters::new();
@@ -350,35 +314,33 @@ impl FrameBuilder {
 
         let mut transform_palette = TransformPalette::new();
         clip_scroll_tree.update_tree(
             pan,
             scene_properties,
             Some(&mut transform_palette),
         );
 
-        self.update_scroll_bars(clip_scroll_tree, gpu_cache);
-
         let mut render_tasks = RenderTaskTree::new(frame_id);
 
         let screen_size = self.screen_rect.size.to_i32();
         let mut special_render_passes = SpecialRenderPasses::new(&screen_size);
 
         let main_render_task_id = self.build_layer_screen_rects_and_cull_layers(
             clip_scroll_tree,
             pipelines,
             resource_cache,
             gpu_cache,
             &mut render_tasks,
             &mut special_render_passes,
             &mut profile_counters,
             device_pixel_scale,
             scene_properties,
             &mut transform_palette,
-            clip_data_store,
+            resources,
         );
 
         resource_cache.block_until_all_resources_added(gpu_cache,
                                                        &mut render_tasks,
                                                        texture_cache_profile);
 
         let mut passes = vec![
             special_render_passes.alpha_glyph_pass,
@@ -412,17 +374,17 @@ impl FrameBuilder {
 
         for pass in &mut passes {
             let mut ctx = RenderTargetContext {
                 device_pixel_scale,
                 prim_store: &self.prim_store,
                 resource_cache,
                 use_dual_source_blending,
                 clip_scroll_tree,
-                clip_data_store,
+                resources,
             };
 
             pass.build(
                 &mut ctx,
                 gpu_cache,
                 &mut render_tasks,
                 &mut deferred_resolves,
                 &self.clip_store,
--- a/gfx/webrender/src/image.rs
+++ b/gfx/webrender/src/image.rs
@@ -1,14 +1,14 @@
 /* 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::{TileOffset, TileRange, LayoutRect, LayoutSize, LayoutPoint};
-use api::{DeviceUintSize, NormalizedRect};
+use api::{DeviceUintSize, DeviceUintRect};
 use euclid::{vec2, point2};
 use prim_store::EdgeAaSegmentMask;
 
 /// If repetitions are far enough apart that only one is within
 /// the primitive rect, then we can simplify the parameters and
 /// treat the primitive as not repeated.
 /// This can let us avoid unnecessary work later to handle some
 /// of the parameters.
@@ -222,32 +222,31 @@ pub fn for_each_tile(
             }
 
             callback(&segment_rect, tile_offset, edge_flags);
         }
     }
 }
 
 pub fn compute_tile_range(
-    visible_area: &NormalizedRect,
-    image_size: &DeviceUintSize,
+    visible_area: &DeviceUintRect,
     tile_size: u16,
 ) -> TileRange {
     // Tile dimensions in normalized coordinates.
-    let tw = (image_size.width as f32) / (tile_size as f32);
-    let th = (image_size.height as f32) / (tile_size as f32);
+    let tw = 1. / (tile_size as f32);
+    let th = 1. / (tile_size as f32);
 
     let t0 = point2(
-        f32::floor(visible_area.origin.x * tw),
-        f32::floor(visible_area.origin.y * th),
+        f32::floor(visible_area.origin.x as f32 * tw),
+        f32::floor(visible_area.origin.y as f32 * th),
     ).cast::<u16>();
 
     let t1 = point2(
-        f32::ceil(visible_area.max_x() * tw),
-        f32::ceil(visible_area.max_y() * th),
+        f32::ceil(visible_area.max_x() as f32 * tw),
+        f32::ceil(visible_area.max_y() as f32 * th),
     ).cast::<u16>();
 
     TileRange {
         origin: t0,
         size: (t1 - t0).to_size(),
     }
 }
 
@@ -318,9 +317,9 @@ mod tests {
               &size2(400, 400),
               36,
               &mut |_tile_rect, _tile_offset, _tile_flags| {
                 count += 1;
               },
         );
         assert_eq!(count, 0);
     }
-}
\ No newline at end of file
+}
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -287,19 +287,19 @@ impl PicturePrimitive {
         let establishes_raster_root = has_surface && wants_raster_root;
 
         let raster_spatial_node_index = if establishes_raster_root {
             surface_spatial_node_index
         } else {
             raster_spatial_node_index
         };
 
-        if establishes_raster_root {
+        if has_surface {
             frame_state.clip_store
-                       .push_raster_root(raster_spatial_node_index);
+                       .push_surface(surface_spatial_node_index);
         }
 
         let map_pic_to_world = SpaceMapper::new_with_target(
             ROOT_SPATIAL_NODE_INDEX,
             surface_spatial_node_index,
             frame_context.world_rect,
             frame_context.clip_scroll_tree,
         );
@@ -356,17 +356,16 @@ impl PicturePrimitive {
         };
 
         let context = PictureContext {
             pipeline_id: self.pipeline_id,
             apply_local_clip_rect: self.apply_local_clip_rect,
             inflation_factor,
             allow_subpixel_aa,
             is_passthrough: self.raster_config.is_none(),
-            establishes_raster_root,
             raster_space,
         };
 
         let instances = mem::replace(&mut self.prim_instances, Vec::new());
 
         Some((context, state, instances))
     }
 
@@ -420,20 +419,20 @@ impl PicturePrimitive {
                 }
             }
             None => {
                 assert!(self.raster_config.is_none());
                 LayoutRect::zero()
             }
         };
 
-        let clip_node_collector = if context.establishes_raster_root {
-            Some(frame_state.clip_store.pop_raster_root())
+        let clip_node_collector = if context.is_passthrough {
+            None
         } else {
-            None
+            Some(frame_state.clip_store.pop_surface())
         };
 
         (local_rect, clip_node_collector)
     }
 
     pub fn take_state(&mut self) -> PictureState {
         self.state.take().expect("bug: no state present!")
     }
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1728,17 +1728,17 @@ impl PrimitiveStore {
                     &pic_state.map_local_to_pic,
                     &pic_state.map_pic_to_world,
                     &frame_context.clip_scroll_tree,
                     frame_state.gpu_cache,
                     frame_state.resource_cache,
                     frame_context.device_pixel_scale,
                     &frame_context.world_rect,
                     &clip_node_collector,
-                    frame_state.clip_data_store,
+                    &mut frame_state.resources.clip_data_store,
                 );
 
             let clip_chain = match clip_chain {
                 Some(clip_chain) => clip_chain,
                 None => {
                     prim.metadata.clipped_world_rect = None;
                     return false;
                 }
@@ -2082,17 +2082,17 @@ fn write_brush_segment_description(
     );
 
     // Segment the primitive on all the local-space clip sources that we can.
     let mut local_clip_count = 0;
     for i in 0 .. clip_chain.clips_range.count {
         let clip_instance = frame_state
             .clip_store
             .get_instance_from_range(&clip_chain.clips_range, i);
-        let clip_node = &frame_state.clip_data_store[clip_instance.handle];
+        let clip_node = &frame_state.resources.clip_data_store[clip_instance.handle];
 
         // If this clip item is positioned by another positioning node, its relative position
         // could change during scrolling. This means that we would need to resegment. Instead
         // of doing that, only segment with clips that have the same positioning node.
         // TODO(mrobinson, #2858): It may make sense to include these nodes, resegmenting only
         // when necessary while scrolling.
         if !clip_instance.flags.contains(ClipNodeFlags::SAME_SPATIAL_NODE) {
             continue;
@@ -2249,17 +2249,17 @@ impl Primitive {
                     &pic_state.map_local_to_pic,
                     &pic_state.map_pic_to_world,
                     &frame_context.clip_scroll_tree,
                     frame_state.gpu_cache,
                     frame_state.resource_cache,
                     frame_context.device_pixel_scale,
                     &frame_context.world_rect,
                     clip_node_collector,
-                    frame_state.clip_data_store,
+                    &mut frame_state.resources.clip_data_store,
                 );
 
             match segment_clip_chain {
                 Some(segment_clip_chain) => {
                     if !segment_clip_chain.needs_mask ||
                        (!segment.may_need_clip_mask && !segment_clip_chain.has_non_local_clips) {
                         segment.clip_task_id = BrushSegmentTaskId::Opaque;
                         continue;
@@ -2282,17 +2282,17 @@ impl Primitive {
                     let clip_task = RenderTask::new_mask(
                         device_rect.to_i32(),
                         segment_clip_chain.clips_range,
                         root_spatial_node_index,
                         frame_state.clip_store,
                         frame_state.gpu_cache,
                         frame_state.resource_cache,
                         frame_state.render_tasks,
-                        frame_state.clip_data_store,
+                        &mut frame_state.resources.clip_data_store,
                     );
 
                     let clip_task_id = frame_state.render_tasks.add(clip_task);
                     pic_state.tasks.push(clip_task_id);
                     segment.clip_task_id = BrushSegmentTaskId::RenderTaskId(clip_task_id);
                 }
                 None => {
                     segment.clip_task_id = BrushSegmentTaskId::Empty;
@@ -2823,17 +2823,17 @@ impl Primitive {
                 let clip_task = RenderTask::new_mask(
                     device_rect,
                     clip_chain.clips_range,
                     root_spatial_node_index,
                     frame_state.clip_store,
                     frame_state.gpu_cache,
                     frame_state.resource_cache,
                     frame_state.render_tasks,
-                    frame_state.clip_data_store,
+                    &mut frame_state.resources.clip_data_store,
                 );
 
                 let clip_task_id = frame_state.render_tasks.add(clip_task);
                 if cfg!(debug_assertions) && is_chased {
                     println!("\tcreated task {:?} with world rect {:?}",
                         clip_task_id, self.metadata.clipped_world_rect);
                 }
                 self.metadata.clip_task_id = Some(clip_task_id);
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -11,19 +11,17 @@ use api::{IdNamespace, LayoutPoint, Pipe
 use api::{MemoryReport, VoidPtrToSizeFn};
 use api::{ScrollLocation, ScrollNodeState, TransactionMsg, ResourceUpdate, ImageKey};
 use api::{NotificationRequest, Checkpoint};
 use api::channel::{MsgReceiver, Payload};
 #[cfg(feature = "capture")]
 use api::CaptureBits;
 #[cfg(feature = "replay")]
 use api::CapturedDocument;
-#[cfg(feature = "replay")]
-use clip::ClipDataInterner;
-use clip::{ClipDataUpdateList, ClipDataStore};
+use clip::ClipDataStore;
 use clip_scroll_tree::{SpatialNodeIndex, ClipScrollTree};
 #[cfg(feature = "debugger")]
 use debug_server;
 use frame_builder::{FrameBuilder, FrameBuilderConfig};
 use gpu_cache::GpuCache;
 use hit_test::{HitTest, HitTester};
 use internal_types::{DebugOutput, FastHashMap, FastHashSet, RenderedDocument, ResultMsg};
 use profiler::{BackendProfileCounters, IpcProfileCounters, ResourceProfileCounters};
@@ -75,16 +73,34 @@ impl DocumentView {
     }
 }
 
 #[derive(Copy, Clone, Hash, PartialEq, PartialOrd, Debug, Eq, Ord)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct FrameId(pub u32);
 
+// A collection of resources that are shared by clips, primitives
+// between display lists.
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct FrameResources {
+    // The store of currently active / available clip nodes. This is kept
+    // in sync with the clip interner in the scene builder for each document.
+    pub clip_data_store: ClipDataStore,
+}
+
+impl FrameResources {
+    fn new() -> Self {
+        FrameResources {
+            clip_data_store: ClipDataStore::new(),
+        }
+    }
+}
+
 struct Document {
     // The latest built scene, usable to build frames.
     // received from the scene builder thread.
     scene: Scene,
 
     // Temporary list of removed pipelines received from the scene builder
     // thread and forwarded to the renderer.
     removed_pipelines: Vec<PipelineId>,
@@ -113,19 +129,17 @@ struct Document {
     /// without requiring the scene to be re-built.
     dynamic_properties: SceneProperties,
 
     /// Track whether the last built frame is up to date or if it will need to be re-built
     /// before rendering again.
     frame_is_valid: bool,
     hit_tester_is_valid: bool,
 
-    // The store of currently active / available clip nodes. This is kept
-    // in sync with the clip interner in the scene builder for each document.
-    clip_data_store: ClipDataStore,
+    resources: FrameResources,
 }
 
 impl Document {
     pub fn new(
         window_size: DeviceUintSize,
         layer: DocumentLayer,
         default_device_pixel_ratio: f32,
     ) -> Self {
@@ -144,17 +158,17 @@ impl Document {
             clip_scroll_tree: ClipScrollTree::new(),
             frame_id: FrameId(0),
             frame_builder: None,
             output_pipelines: FastHashSet::default(),
             hit_tester: None,
             dynamic_properties: SceneProperties::new(),
             frame_is_valid: false,
             hit_tester_is_valid: false,
-            clip_data_store: ClipDataStore::new(),
+            resources: FrameResources::new(),
         }
     }
 
     fn can_render(&self) -> bool {
         self.frame_builder.is_some() && self.scene.has_root_pipeline()
     }
 
     fn has_pixels(&self) -> bool {
@@ -273,21 +287,21 @@ impl Document {
                 &mut self.clip_scroll_tree,
                 &self.scene.pipelines,
                 accumulated_scale_factor,
                 self.view.layer,
                 pan,
                 &mut resource_profile.texture_cache,
                 &mut resource_profile.gpu_cache,
                 &self.dynamic_properties,
-                &mut self.clip_data_store,
+                &mut self.resources,
             );
             self.hit_tester = Some(frame_builder.create_hit_tester(
                 &self.clip_scroll_tree,
-                &self.clip_data_store,
+                &self.resources.clip_data_store,
             ));
             frame
         };
 
         self.frame_is_valid = true;
         self.hit_tester_is_valid = true;
 
         RenderedDocument {
@@ -304,17 +318,17 @@ impl Document {
             self.clip_scroll_tree.update_tree(
                 pan,
                 &self.dynamic_properties,
                 None,
             );
 
             self.hit_tester = Some(frame_builder.create_hit_tester(
                 &self.clip_scroll_tree,
-                &self.clip_data_store,
+                &self.resources.clip_data_store,
             ));
         }
     }
 
     pub fn updated_pipeline_info(&mut self) -> PipelineInfo {
         let removed_pipelines = replace(&mut self.removed_pipelines, Vec::new());
         PipelineInfo {
             epochs: self.scene.pipeline_epochs.clone(),
@@ -414,16 +428,17 @@ pub struct RenderBackend {
 
     frame_config: FrameBuilderConfig,
     documents: FastHashMap<DocumentId, Document>,
 
     notifier: Box<RenderNotifier>,
     recorder: Option<Box<ApiRecordingReceiver>>,
     sampler: Option<Box<AsyncPropertySampler + Send>>,
     size_of_op: Option<VoidPtrToSizeFn>,
+    namespace_alloc_by_client: bool,
 
     last_scene_id: u64,
 }
 
 impl RenderBackend {
     pub fn new(
         api_rx: MsgReceiver<ApiMsg>,
         payload_rx: Receiver<Payload>,
@@ -433,16 +448,17 @@ impl RenderBackend {
         scene_rx: Receiver<SceneBuilderResult>,
         default_device_pixel_ratio: f32,
         resource_cache: ResourceCache,
         notifier: Box<RenderNotifier>,
         frame_config: FrameBuilderConfig,
         recorder: Option<Box<ApiRecordingReceiver>>,
         sampler: Option<Box<AsyncPropertySampler + Send>>,
         size_of_op: Option<VoidPtrToSizeFn>,
+        namespace_alloc_by_client: bool,
     ) -> RenderBackend {
         RenderBackend {
             api_rx,
             payload_rx,
             result_tx,
             scene_tx,
             low_priority_scene_tx,
             scene_rx,
@@ -452,16 +468,17 @@ impl RenderBackend {
             gpu_cache: GpuCache::new(),
             frame_config,
             documents: FastHashMap::default(),
             notifier,
             recorder,
             sampler,
             size_of_op,
             last_scene_id: 0,
+            namespace_alloc_by_client,
         }
     }
 
     fn process_scene_msg(
         &mut self,
         document_id: DocumentId,
         message: SceneMsg,
         frame_counter: u32,
@@ -624,17 +641,17 @@ impl RenderBackend {
                         );
                         if let Some(rasterizer) = txn.blob_rasterizer.take() {
                             self.resource_cache.set_blob_rasterizer(rasterizer);
                         }
 
                         self.update_document(
                             txn.document_id,
                             replace(&mut txn.resource_updates, Vec::new()),
-                            txn.clip_updates.take(),
+                            txn.doc_resource_updates.take(),
                             replace(&mut txn.frame_ops, Vec::new()),
                             replace(&mut txn.notifications, Vec::new()),
                             txn.render_frame,
                             &mut frame_counter,
                             &mut profile_counters,
                             has_built_scene,
                         );
                     },
@@ -724,18 +741,23 @@ impl RenderBackend {
                 let mut glyph_indices = Vec::new();
                 for ch in text.chars() {
                     let index = self.resource_cache.get_glyph_index(font_key, ch);
                     glyph_indices.push(index);
                 }
                 tx.send(glyph_indices).unwrap();
             }
             ApiMsg::CloneApi(sender) => {
+                assert!(!self.namespace_alloc_by_client);
                 sender.send(self.next_namespace_id()).unwrap();
             }
+            ApiMsg::CloneApiByClient(namespace_id) => {
+                assert!(self.namespace_alloc_by_client);
+                debug_assert!(!self.documents.iter().any(|(did, _doc)| did.0 == namespace_id));
+            }
             ApiMsg::AddDocument(document_id, initial_size, layer) => {
                 let document = Document::new(
                     initial_size,
                     layer,
                     self.default_device_pixel_ratio,
                 );
                 self.documents.insert(document_id, document);
             }
@@ -968,17 +990,17 @@ impl RenderBackend {
 
         tx.send(SceneBuilderRequest::Transaction(txn)).unwrap();
     }
 
     fn update_document(
         &mut self,
         document_id: DocumentId,
         resource_updates: Vec<ResourceUpdate>,
-        clip_updates: Option<ClipDataUpdateList>,
+        doc_resource_updates: Option<DocumentResourceUpdates>,
         mut frame_ops: Vec<FrameMsg>,
         mut notifications: Vec<NotificationRequest>,
         mut render_frame: bool,
         frame_counter: &mut u32,
         profile_counters: &mut BackendProfileCounters,
         has_built_scene: bool,
     ) {
         let requested_frame = render_frame;
@@ -993,18 +1015,18 @@ impl RenderBackend {
                 frame_ops.append(&mut sampler.sample());
             }
         }
 
         let doc = self.documents.get_mut(&document_id).unwrap();
 
         // If there are any additions or removals of clip modes
         // during the scene build, apply them to the data store now.
-        if let Some(clip_updates) = clip_updates {
-            doc.clip_data_store.apply_updates(clip_updates);
+        if let Some(updates) = doc_resource_updates {
+            doc.resources.clip_data_store.apply_updates(updates.clip_updates);
         }
 
         // TODO: this scroll variable doesn't necessarily mean we scrolled. It is only used
         // for something wrench specific and we should remove it.
         let mut scroll = false;
         for frame_msg in frame_ops {
             let _timer = profile_counters.total_time.timer();
             let op = doc.process_frame_msg(frame_msg);
@@ -1311,18 +1333,18 @@ impl RenderBackend {
                 );
                 //TODO: write down doc's pipeline info?
                 // it has `pipeline_epoch_map`,
                 // which may capture necessary details for some cases.
                 let file_name = format!("frame-{}-{}", (id.0).0, id.1);
                 config.serialize(&rendered_document.frame, file_name);
             }
 
-            let clip_data_name = format!("clip-data-{}-{}", (id.0).0, id.1);
-            config.serialize(&doc.clip_data_store, clip_data_name);
+            let frame_resources_name = format!("frame-resources-{}-{}", (id.0).0, id.1);
+            config.serialize(&doc.resources, frame_resources_name);
         }
 
         debug!("\tscene builder");
         self.scene_tx.send(SceneBuilderRequest::SaveScene(config.clone())).unwrap();
 
         debug!("\tresource cache");
         let (resources, deferred) = self.resource_cache.save_capture(&config.root);
 
@@ -1398,37 +1420,37 @@ impl RenderBackend {
 
         let mut last_scene_id = backend.last_scene_id;
         for (id, view) in backend.documents {
             debug!("\tdocument {:?}", id);
             let scene_name = format!("scene-{}-{}", (id.0).0, id.1);
             let scene = CaptureConfig::deserialize::<Scene, _>(root, &scene_name)
                 .expect(&format!("Unable to open {}.ron", scene_name));
 
-            let clip_interner_name = format!("clip-interner-{}-{}", (id.0).0, id.1);
-            let clip_interner = CaptureConfig::deserialize::<ClipDataInterner, _>(root, &clip_interner_name)
-                .expect(&format!("Unable to open {}.ron", clip_interner_name));
+            let doc_resources_name = format!("doc-resources-{}-{}", (id.0).0, id.1);
+            let doc_resources = CaptureConfig::deserialize::<DocumentResources, _>(root, &doc_resources_name)
+                .expect(&format!("Unable to open {}.ron", doc_resources_name));
 
-            let clip_data_name = format!("clip-data-{}-{}", (id.0).0, id.1);
-            let clip_data_store = CaptureConfig::deserialize::<ClipDataStore, _>(root, &clip_data_name)
-                .expect(&format!("Unable to open {}.ron", clip_data_name));
+            let frame_resources_name = format!("frame-resources-{}-{}", (id.0).0, id.1);
+            let frame_resources = CaptureConfig::deserialize::<FrameResources, _>(root, &frame_resources_name)
+                .expect(&format!("Unable to open {}.ron", frame_resources_name));
 
             let mut doc = Document {
                 scene: scene.clone(),
                 removed_pipelines: Vec::new(),
                 view: view.clone(),
                 clip_scroll_tree: ClipScrollTree::new(),
                 frame_id: FrameId(0),
                 frame_builder: Some(FrameBuilder::empty()),
                 output_pipelines: FastHashSet::default(),
                 dynamic_properties: SceneProperties::new(),
                 hit_tester: None,
                 frame_is_valid: false,
                 hit_tester_is_valid: false,
-                clip_data_store,
+                resources: frame_resources,
             };
 
             let frame_name = format!("frame-{}-{}", (id.0).0, id.1);
             let frame = CaptureConfig::deserialize::<Frame, _>(root, frame_name);
             let build_frame = match frame {
                 Some(frame) => {
                     info!("\tloaded a built frame with {} passes", frame.passes.len());
 
@@ -1459,17 +1481,17 @@ impl RenderBackend {
                 document_id: id,
                 scene: doc.scene.clone(),
                 view: view.clone(),
                 config: self.frame_config.clone(),
                 output_pipelines: doc.output_pipelines.clone(),
                 font_instances: self.resource_cache.get_font_instances(),
                 scene_id: last_scene_id,
                 build_frame,
-                clip_interner,
+                doc_resources,
             });
 
             self.documents.insert(id, doc);
         }
 
         if !scenes_to_build.is_empty() {
             self.low_priority_scene_tx.send(
                 SceneBuilderRequest::LoadScenes(scenes_to_build)
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -1716,17 +1716,16 @@ 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,
             dual_source_blending_is_enabled: true,
             dual_source_blending_is_supported: ext_dual_source_blending,
             chase_primitive: options.chase_primitive,
         };
 
         let device_pixel_ratio = options.device_pixel_ratio;
         // First set the flags to default and later call set_debug_flags to ensure any
@@ -1754,16 +1753,17 @@ impl Renderer {
                             thread_listener.thread_stopped(&format!("WRWorker#{}", idx));
                         }
                     })
                     .build();
                 Arc::new(worker.unwrap())
             });
         let sampler = options.sampler;
         let size_of_op = options.size_of_op;
+        let namespace_alloc_by_client = options.namespace_alloc_by_client;
 
         let blob_image_handler = options.blob_image_handler.take();
         let thread_listener_for_render_backend = thread_listener.clone();
         let thread_listener_for_scene_builder = thread_listener.clone();
         let thread_listener_for_lp_scene_builder = thread_listener.clone();
         let scene_builder_hooks = options.scene_builder_hooks;
         let rb_thread_name = format!("WRRenderBackend#{}", options.renderer_id.unwrap_or(0));
         let scene_thread_name = format!("WRSceneBuilder#{}", options.renderer_id.unwrap_or(0));
@@ -1837,16 +1837,17 @@ impl Renderer {
                 scene_rx,
                 device_pixel_ratio,
                 resource_cache,
                 backend_notifier,
                 config,
                 recorder,
                 sampler,
                 size_of_op,
+                namespace_alloc_by_client,
             );
             backend.run(backend_profile_counters);
             if let Some(ref thread_listener) = *thread_listener_for_render_backend {
                 thread_listener.thread_stopped(&rb_thread_name);
             }
         })?;
 
         let ext_debug_marker = device.supports_extension("GL_EXT_debug_marker");
@@ -4395,17 +4396,16 @@ pub trait AsyncPropertySampler {
 }
 
 pub struct RendererOptions {
     pub device_pixel_ratio: f32,
     pub resource_override_path: Option<PathBuf>,
     pub enable_aa: bool,
     pub enable_dithering: bool,
     pub max_recorded_profiles: usize,
-    pub enable_scrollbars: bool,
     pub precache_shaders: bool,
     pub renderer_kind: RendererKind,
     pub enable_subpixel_aa: bool,
     pub clear_color: Option<ColorF>,
     pub enable_clear_scissor: bool,
     pub max_texture_size: Option<u32>,
     pub scatter_gpu_cache_updates: bool,
     pub upload_method: UploadMethod,
@@ -4417,28 +4417,28 @@ pub struct RendererOptions {
     pub cached_programs: Option<Rc<ProgramCache>>,
     pub debug_flags: DebugFlags,
     pub renderer_id: Option<u64>,
     pub disable_dual_source_blending: bool,
     pub scene_builder_hooks: Option<Box<SceneBuilderHooks + Send>>,
     pub sampler: Option<Box<AsyncPropertySampler + Send>>,
     pub chase_primitive: ChasePrimitive,
     pub support_low_priority_transactions: bool,
+    pub namespace_alloc_by_client: bool,
 }
 
 impl Default for RendererOptions {
     fn default() -> Self {
         RendererOptions {
             device_pixel_ratio: 1.0,
             resource_override_path: None,
             enable_aa: true,
             enable_dithering: true,
             debug_flags: DebugFlags::empty(),
             max_recorded_profiles: 0,
-            enable_scrollbars: false,
             precache_shaders: false,
             renderer_kind: RendererKind::Native,
             enable_subpixel_aa: false,
             clear_color: Some(ColorF::new(1.0, 1.0, 1.0, 1.0)),
             enable_clear_scissor: true,
             max_texture_size: None,
             // Scattered GPU cache updates haven't met a test that would show their superiority yet.
             scatter_gpu_cache_updates: false,
@@ -4452,16 +4452,17 @@ impl Default for RendererOptions {
             size_of_op: None,
             renderer_id: None,
             cached_programs: None,
             disable_dual_source_blending: false,
             scene_builder_hooks: None,
             sampler: None,
             chase_primitive: ChasePrimitive::Nothing,
             support_low_priority_transactions: false,
+            namespace_alloc_by_client: false,
         }
     }
 }
 
 #[cfg(not(feature = "debugger"))]
 pub struct DebugServer;
 
 #[cfg(not(feature = "debugger"))]
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -6,17 +6,17 @@ use api::{AddFont, BlobImageResources, A
 use api::{BlobImageDescriptor, BlobImageHandler, BlobImageRequest, RasterizedBlobImage};
 use api::{ClearCache, ColorF, DevicePoint, DeviceUintPoint, DeviceUintRect, DeviceUintSize};
 use api::{FontInstanceKey, FontKey, FontTemplate, GlyphIndex};
 use api::{ExternalImageData, ExternalImageType, BlobImageResult, BlobImageParams};
 use api::{FontInstanceData, FontInstanceOptions, FontInstancePlatformOptions, FontVariation};
 use api::{GlyphDimensions, IdNamespace};
 use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering};
 use api::{MemoryReport, VoidPtrToSizeFn};
-use api::{TileOffset, TileSize, TileRange, NormalizedRect, BlobImageData};
+use api::{TileOffset, TileSize, TileRange, BlobImageData};
 use app_units::Au;
 #[cfg(feature = "capture")]
 use capture::ExternalCaptureImage;
 #[cfg(feature = "replay")]
 use capture::PlainExternalImage;
 #[cfg(any(feature = "replay", feature = "png"))]
 use capture::CaptureConfig;
 use device::TextureFilter;
@@ -550,17 +550,16 @@ impl ResourceCache {
                         );
                     }
                 }
                 ResourceUpdate::SetImageVisibleArea(ref key, ref area) => {
                     if let Some(template) = self.blob_image_templates.get_mut(&key) {
                         if let Some(tile_size) = template.tiling {
                             template.viewport_tiles = Some(compute_tile_range(
                                 &area,
-                                &template.descriptor.size,
                                 tile_size,
                             ));
                         }
                     }
                 }
                 _ => {}
             }
         }
@@ -1035,41 +1034,38 @@ impl ResourceCache {
             let template = self.blob_image_templates.get_mut(key).unwrap();
 
             if let Some(tile_size) = template.tiling {
                 // If we know that only a portion of the blob image is in the viewport,
                 // only request these visible tiles since blob images can be huge.
                 let mut tiles = template.viewport_tiles.unwrap_or_else(|| {
                     // Default to requesting the full range of tiles.
                     compute_tile_range(
-                        &NormalizedRect {
-                            origin: point2(0.0, 0.0),
-                            size: size2(1.0, 1.0),
+                        &DeviceUintRect {
+                            origin: point2(0, 0),
+                            size: template.descriptor.size,
                         },
-                        &template.descriptor.size,
                         tile_size,
                     )
                 });
 
                 // Don't request tiles that weren't invalidated.
                 if let Some(dirty_rect) = template.dirty_rect {
-                    let f32_size = template.descriptor.size.to_f32();
-                    let normalized_dirty_rect = NormalizedRect {
+                    let dirty_rect = DeviceUintRect {
                         origin: point2(
-                            dirty_rect.origin.x as f32 / f32_size.width,
-                            dirty_rect.origin.y as f32 / f32_size.height,
+                            dirty_rect.origin.x,
+                            dirty_rect.origin.y,
                         ),
                         size: size2(
-                            dirty_rect.size.width as f32 / f32_size.width,
-                            dirty_rect.size.height as f32 / f32_size.height,
+                            dirty_rect.size.width,
+                            dirty_rect.size.height,
                         ),
                     };
                     let dirty_tiles = compute_tile_range(
-                        &normalized_dirty_rect,
-                        &template.descriptor.size,
+                        &dirty_rect,
                         tile_size,
                     );
 
                     tiles = tiles.intersection(&dirty_tiles).unwrap_or(TileRange::zero());
                 }
 
                 // This code tries to keep things sane if Gecko sends
                 // nonsensical blob image requests.
@@ -1168,17 +1164,17 @@ impl ResourceCache {
         let handler = self.blob_image_handler.as_mut().unwrap();
         handler.prepare_resources(&self.resources, &blob_request_params);
         (Some(handler.create_blob_rasterizer()), blob_request_params)
     }
 
     fn discard_tiles_outside_visible_area(
         &mut self,
         key: ImageKey,
-        area: &NormalizedRect
+        area: &DeviceUintRect
     ) {
         let template = match self.blob_image_templates.get(&key) {
             Some(template) => template,
             None => {
                 //println!("Missing image template (key={:?})!", key);
                 return;
             }
         };
@@ -1189,17 +1185,16 @@ impl ResourceCache {
 
         let tiles = match self.rasterized_blob_images.get_mut(&key) {
             Some(RasterizedBlob::Tiled(tiles)) => tiles,
             _ => { return; }
         };
 
         let tile_range = compute_tile_range(
             &area,
-            &template.descriptor.size,
             tile_size,
         );
 
         tiles.retain(|tile, _| { tile_range.contains(tile) });
     }
 
     pub fn request_glyphs(
         &mut self,
--- a/gfx/webrender/src/scene_builder.rs
+++ b/gfx/webrender/src/scene_builder.rs
@@ -20,16 +20,20 @@ use renderer::{PipelineInfo, SceneBuilde
 use scene::Scene;
 use std::sync::mpsc::{channel, Receiver, Sender};
 use std::mem::replace;
 use time::precise_time_ns;
 use util::drain_filter;
 use std::thread;
 use std::time::Duration;
 
+pub struct DocumentResourceUpdates {
+    pub clip_updates: ClipDataUpdateList,
+}
+
 /// Represents the work associated to a transaction before scene building.
 pub struct Transaction {
     pub document_id: DocumentId,
     pub display_list_updates: Vec<DisplayListUpdate>,
     pub removed_pipelines: Vec<PipelineId>,
     pub epoch_updates: Vec<(PipelineId, Epoch)>,
     pub request_scene_build: Option<SceneRequest>,
     pub blob_requests: Vec<BlobImageParams>,
@@ -64,17 +68,17 @@ pub struct BuiltTransaction {
     pub document_id: DocumentId,
     pub built_scene: Option<BuiltScene>,
     pub resource_updates: Vec<ResourceUpdate>,
     pub rasterized_blobs: Vec<(BlobImageRequest, BlobImageResult)>,
     pub blob_rasterizer: Option<Box<AsyncBlobImageRasterizer>>,
     pub frame_ops: Vec<FrameMsg>,
     pub removed_pipelines: Vec<PipelineId>,
     pub notifications: Vec<NotificationRequest>,
-    pub clip_updates: Option<ClipDataUpdateList>,
+    pub doc_resource_updates: Option<DocumentResourceUpdates>,
     pub scene_build_start_time: u64,
     pub scene_build_end_time: u64,
     pub render_frame: bool,
 }
 
 pub struct DisplayListUpdate {
     pub pipeline_id: PipelineId,
     pub epoch: Epoch,
@@ -97,17 +101,17 @@ pub struct LoadScene {
     pub document_id: DocumentId,
     pub scene: Scene,
     pub scene_id: u64,
     pub output_pipelines: FastHashSet<PipelineId>,
     pub font_instances: FontInstanceMap,
     pub view: DocumentView,
     pub config: FrameBuilderConfig,
     pub build_frame: bool,
-    pub clip_interner: ClipDataInterner,
+    pub doc_resources: DocumentResources,
 }
 
 pub struct BuiltScene {
     pub scene: Scene,
     pub frame_builder: FrameBuilder,
     pub clip_scroll_tree: ClipScrollTree,
 }
 
@@ -139,30 +143,50 @@ pub enum SceneBuilderResult {
 // Message from render backend to scene builder to indicate the
 // scene swap was completed. We need a separate channel for this
 // so that they don't get mixed with SceneBuilderRequest messages.
 pub enum SceneSwapResult {
     Complete(Sender<()>),
     Aborted,
 }
 
+// This struct contains all items that can be shared between
+// display lists. We want to intern and share the same clips,
+// primitives and other things between display lists so that:
+// - GPU cache handles remain valid, reducing GPU cache updates.
+// - Comparison of primitives and pictures between two
+//   display lists is (a) fast (b) done during scene building.
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct DocumentResources {
+    pub clip_interner: ClipDataInterner,
+}
+
+impl DocumentResources {
+    fn new() -> Self {
+        DocumentResources {
+            clip_interner: ClipDataInterner::new(),
+        }
+    }
+}
+
 // A document in the scene builder contains the current scene,
 // as well as a persistent clip interner. This allows clips
 // to be de-duplicated, and persisted in the GPU cache between
 // display lists.
 struct Document {
     scene: Scene,
-    clip_interner: ClipDataInterner,
+    resources: DocumentResources,
 }
 
 impl Document {
     fn new(scene: Scene) -> Self {
         Document {
             scene,
-            clip_interner: ClipDataInterner::new(),
+            resources: DocumentResources::new(),
         }
     }
 }
 
 pub struct SceneBuilder {
     documents: FastHashMap<DocumentId, Document>,
     rx: Receiver<SceneBuilderRequest>,
     tx: Sender<SceneBuilderResult>,
@@ -255,78 +279,87 @@ impl SceneBuilder {
         if let Some(ref hooks) = self.hooks {
             hooks.deregister();
         }
     }
 
     #[cfg(feature = "capture")]
     fn save_scene(&mut self, config: CaptureConfig) {
         for (id, doc) in &self.documents {
-            let clip_interner_name = format!("clip-interner-{}-{}", (id.0).0, id.1);
-            config.serialize(&doc.clip_interner, clip_interner_name);
+            let doc_resources_name = format!("doc-resources-{}-{}", (id.0).0, id.1);
+            config.serialize(&doc.resources, doc_resources_name);
         }
     }
 
     #[cfg(feature = "replay")]
     fn load_scenes(&mut self, scenes: Vec<LoadScene>) {
         for mut item in scenes {
             self.config = item.config;
 
             let scene_build_start_time = precise_time_ns();
 
             let mut built_scene = None;
-            let mut clip_updates = None;
+            let mut doc_resource_updates = None;
 
             if item.scene.has_root_pipeline() {
                 let mut clip_scroll_tree = ClipScrollTree::new();
                 let mut new_scene = Scene::new();
 
                 let frame_builder = DisplayListFlattener::create_frame_builder(
                     &item.scene,
                     &mut clip_scroll_tree,
                     item.font_instances,
                     &item.view,
                     &item.output_pipelines,
                     &self.config,
                     &mut new_scene,
                     item.scene_id,
                     &mut self.picture_id_generator,
-                    &mut item.clip_interner,
+                    &mut item.doc_resources,
                 );
 
-                clip_updates = Some(item.clip_interner.end_frame_and_get_pending_updates());
+                let clip_updates = item
+                    .doc_resources
+                    .clip_interner
+                    .end_frame_and_get_pending_updates();
+
+                doc_resource_updates = Some(
+                    DocumentResourceUpdates {
+                        clip_updates,
+                    }
+                );
 
                 built_scene = Some(BuiltScene {
                     scene: new_scene,
                     frame_builder,
                     clip_scroll_tree,
                 });
             }
 
             self.documents.insert(
                 item.document_id,
                 Document {
                     scene: item.scene,
-                    clip_interner: item.clip_interner,
+                    resources: item.doc_resources,
                 },
             );
 
             let txn = Box::new(BuiltTransaction {
                 document_id: item.document_id,
                 render_frame: item.build_frame,
                 built_scene,
                 resource_updates: Vec::new(),
                 rasterized_blobs: Vec::new(),
                 blob_rasterizer: None,
                 frame_ops: Vec::new(),
                 removed_pipelines: Vec::new(),
                 notifications: Vec::new(),
                 scene_build_start_time,
                 scene_build_end_time: precise_time_ns(),
-                clip_updates,
+                doc_resource_updates,
             });
 
             self.forward_built_transaction(txn);
         }
     }
 
     /// Do the bulk of the work of the scene builder thread.
     fn process_transaction(&mut self, txn: &mut Transaction) -> Box<BuiltTransaction> {
@@ -357,37 +390,46 @@ impl SceneBuilder {
             scene.set_root_pipeline_id(id);
         }
 
         for pipeline_id in &txn.removed_pipelines {
             scene.remove_pipeline(*pipeline_id)
         }
 
         let mut built_scene = None;
-        let mut clip_updates = None;
+        let mut doc_resource_updates = None;
         if scene.has_root_pipeline() {
             if let Some(request) = txn.request_scene_build.take() {
                 let mut clip_scroll_tree = ClipScrollTree::new();
                 let mut new_scene = Scene::new();
 
                 let frame_builder = DisplayListFlattener::create_frame_builder(
                     &scene,
                     &mut clip_scroll_tree,
                     request.font_instances,
                     &request.view,
                     &request.output_pipelines,
                     &self.config,
                     &mut new_scene,
                     request.scene_id,
                     &mut self.picture_id_generator,
-                    &mut doc.clip_interner,
+                    &mut doc.resources,
                 );
 
                 // Retrieve the list of updates from the clip interner.
-                clip_updates = Some(doc.clip_interner.end_frame_and_get_pending_updates());
+                let clip_updates = doc
+                    .resources
+                    .clip_interner
+                    .end_frame_and_get_pending_updates();
+
+                doc_resource_updates = Some(
+                    DocumentResourceUpdates {
+                        clip_updates,
+                    }
+                );
 
                 built_scene = Some(BuiltScene {
                     scene: new_scene,
                     frame_builder,
                     clip_scroll_tree,
                 });
             }
         }
@@ -414,17 +456,17 @@ impl SceneBuilder {
             render_frame: txn.render_frame,
             built_scene,
             rasterized_blobs,
             resource_updates: replace(&mut txn.resource_updates, Vec::new()),
             blob_rasterizer: replace(&mut txn.blob_rasterizer, None),
             frame_ops: replace(&mut txn.frame_ops, Vec::new()),
             removed_pipelines: replace(&mut txn.removed_pipelines, Vec::new()),
             notifications: replace(&mut txn.notifications, Vec::new()),
-            clip_updates,
+            doc_resource_updates,
             scene_build_start_time,
             scene_build_end_time: precise_time_ns(),
         })
     }
 
     /// Send the result of process_transaction back to the render backend.
     fn forward_built_transaction(&mut self, txn: Box<BuiltTransaction>) {
         // We only need the pipeline info and the result channel if we
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -1,60 +1,54 @@
 /* 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::{ColorF, BorderStyle, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixelScale};
 use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentLayer, FilterOp, ImageFormat};
-use api::{LayoutRect, MixBlendMode, PipelineId};
+use api::{MixBlendMode, PipelineId};
 use batch::{AlphaBatchBuilder, AlphaBatchContainer, ClipBatcher, resolve_image};
-use clip::{ClipDataStore, ClipStore};
-use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex};
+use clip::ClipStore;
+use clip_scroll_tree::{ClipScrollTree};
 use device::{FrameId, Texture};
 #[cfg(feature = "pathfinder")]
 use euclid::{TypedPoint2D, TypedVector2D};
 use gpu_cache::{GpuCache};
 use gpu_types::{BorderInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance};
 use gpu_types::{TransformData, TransformPalette};
 use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
 #[cfg(feature = "pathfinder")]
 use pathfinder_partitioner::mesh::Mesh;
-use prim_store::{PrimitiveIndex, PrimitiveStore, DeferredResolve};
+use prim_store::{PrimitiveStore, DeferredResolve};
 use profiler::FrameProfileCounters;
+use render_backend::FrameResources;
 use render_task::{BlitSource, RenderTaskAddress, RenderTaskId, RenderTaskKind};
 use render_task::{BlurTask, ClearMode, GlyphTask, RenderTaskLocation, RenderTaskTree, ScalingTask};
 use resource_cache::ResourceCache;
 use std::{cmp, usize, f32, i32, mem};
 use texture_allocator::GuillotineAllocator;
 #[cfg(feature = "pathfinder")]
 use webrender_api::{DevicePixel, FontRenderMode};
 
 const MIN_TARGET_SIZE: u32 = 2048;
 const STYLE_SOLID: i32 = ((BorderStyle::Solid as i32) << 8) | ((BorderStyle::Solid as i32) << 16);
 const STYLE_MASK: i32 = 0x00FF_FF00;
 
-#[derive(Debug)]
-pub struct ScrollbarPrimitive {
-    pub scroll_frame_index: SpatialNodeIndex,
-    pub prim_index: PrimitiveIndex,
-    pub frame_rect: LayoutRect,
-}
-
 #[derive(Debug, Copy, Clone)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct RenderTargetIndex(pub usize);
 
 pub struct RenderTargetContext<'a, 'rc> {
     pub device_pixel_scale: DevicePixelScale,
     pub prim_store: &'a PrimitiveStore,
     pub resource_cache: &'rc mut ResourceCache,
     pub use_dual_source_blending: bool,
     pub clip_scroll_tree: &'a ClipScrollTree,
-    pub clip_data_store: &'a ClipDataStore,
+    pub resources: &'a FrameResources,
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 struct TextureAllocator {
     // TODO(gw): Replace this with a simpler allocator for
     // render target allocation - this use case doesn't need
     // to deal with coalescing etc that the general texture
@@ -589,17 +583,17 @@ impl RenderTarget for AlphaRenderTarget 
                     task_address,
                     task_info.clip_node_range,
                     task_info.root_spatial_node_index,
                     ctx.resource_cache,
                     gpu_cache,
                     clip_store,
                     ctx.clip_scroll_tree,
                     transforms,
-                    ctx.clip_data_store,
+                    &ctx.resources.clip_data_store,
                 );
             }
             RenderTaskKind::ClipRegion(ref task) => {
                 let task_address = render_tasks.get_task_address(task_id);
                 self.clip_batcher.add_clip_region(
                     task_address,
                     task.clip_data_address,
                 );
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -12,28 +12,28 @@ use std::marker::PhantomData;
 use std::os::raw::c_void;
 use std::path::PathBuf;
 use std::sync::Arc;
 use std::u32;
 use {BuiltDisplayList, BuiltDisplayListDescriptor, ColorF, DeviceIntPoint, DeviceUintRect};
 use {DeviceUintSize, ExternalScrollId, FontInstanceKey, FontInstanceOptions};
 use {FontInstancePlatformOptions, FontKey, FontVariation, GlyphDimensions, GlyphIndex, ImageData};
 use {ImageDescriptor, ImageKey, ItemTag, LayoutPoint, LayoutSize, LayoutTransform, LayoutVector2D};
-use {NativeFontHandle, WorldPoint, NormalizedRect};
+use {NativeFontHandle, WorldPoint};
 
 pub type TileSize = u16;
 /// Documents are rendered in the ascending order of their associated layer values.
 pub type DocumentLayer = i8;
 
 #[derive(Clone, Deserialize, Serialize)]
 pub enum ResourceUpdate {
     AddImage(AddImage),
     UpdateImage(UpdateImage),
     DeleteImage(ImageKey),
-    SetImageVisibleArea(ImageKey, NormalizedRect),
+    SetImageVisibleArea(ImageKey, DeviceUintRect),
     AddFont(AddFont),
     DeleteFont(FontKey),
     AddFontInstance(AddFontInstance),
     DeleteFontInstance(FontInstanceKey),
 }
 
 /// A Transaction is a group of commands to apply atomically to a document.
 ///
@@ -316,17 +316,17 @@ impl Transaction {
             dirty_rect,
         }));
     }
 
     pub fn delete_image(&mut self, key: ImageKey) {
         self.resource_updates.push(ResourceUpdate::DeleteImage(key));
     }
 
-    pub fn set_image_visible_area(&mut self, key: ImageKey, area: NormalizedRect) {
+    pub fn set_image_visible_area(&mut self, key: ImageKey, area: DeviceUintRect) {
         self.resource_updates.push(ResourceUpdate::SetImageVisibleArea(key, area))
     }
 
     pub fn add_raw_font(&mut self, key: FontKey, bytes: Vec<u8>, index: u32) {
         self.resource_updates
             .push(ResourceUpdate::AddFont(AddFont::Raw(key, bytes, index)));
     }
 
@@ -657,16 +657,18 @@ pub enum ApiMsg {
         FontInstanceKey,
         Vec<GlyphIndex>,
         MsgSender<Vec<Option<GlyphDimensions>>>,
     ),
     /// Gets the glyph indices from a string
     GetGlyphIndices(FontKey, String, MsgSender<Vec<Option<u32>>>),
     /// Adds a new document namespace.
     CloneApi(MsgSender<IdNamespace>),
+    /// Adds a new document namespace.
+    CloneApiByClient(IdNamespace),
     /// Adds a new document with given initial size.
     AddDocument(DocumentId, DeviceUintSize, DocumentLayer),
     /// A message targeted at a particular document.
     UpdateDocument(DocumentId, TransactionMsg),
     /// Deletes an existing document.
     DeleteDocument(DocumentId),
     /// 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
@@ -690,16 +692,17 @@ pub enum ApiMsg {
 
 impl fmt::Debug for ApiMsg {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         f.write_str(match *self {
             ApiMsg::UpdateResources(..) => "ApiMsg::UpdateResources",
             ApiMsg::GetGlyphDimensions(..) => "ApiMsg::GetGlyphDimensions",
             ApiMsg::GetGlyphIndices(..) => "ApiMsg::GetGlyphIndices",
             ApiMsg::CloneApi(..) => "ApiMsg::CloneApi",
+            ApiMsg::CloneApiByClient(..) => "ApiMsg::CloneApiByClient",
             ApiMsg::AddDocument(..) => "ApiMsg::AddDocument",
             ApiMsg::UpdateDocument(..) => "ApiMsg::UpdateDocument",
             ApiMsg::DeleteDocument(..) => "ApiMsg::DeleteDocument",
             ApiMsg::ExternalEvent(..) => "ApiMsg::ExternalEvent",
             ApiMsg::ClearNamespace(..) => "ApiMsg::ClearNamespace",
             ApiMsg::MemoryPressure => "ApiMsg::MemoryPressure",
             ApiMsg::ReportMemory(..) => "ApiMsg::ReportMemory",
             ApiMsg::DebugCommand(..) => "ApiMsg::DebugCommand",
@@ -859,16 +862,33 @@ impl RenderApiSender {
         };
         RenderApi {
             api_sender: self.api_sender.clone(),
             payload_sender: self.payload_sender.clone(),
             namespace_id,
             next_id: Cell::new(ResourceId(0)),
         }
     }
+
+    /// Creates a new resource API object with a dedicated namespace.
+    /// Namespace id is allocated by client.
+    ///
+    /// The function could be used only when RendererOptions::namespace_alloc_by_client is true.
+    /// When the option is true, create_api() could not be used to prevent namespace id conflict.
+    pub fn create_api_by_client(&self, namespace_id: IdNamespace) -> RenderApi {
+        let msg = ApiMsg::CloneApiByClient(namespace_id);
+        self.api_sender.send(msg).expect("Failed to send CloneApiByClient message");
+        RenderApi {
+            api_sender: self.api_sender.clone(),
+            payload_sender: self.payload_sender.clone(),
+            namespace_id,
+            next_id: Cell::new(ResourceId(0)),
+        }
+    }
+
 }
 
 pub struct RenderApi {
     api_sender: MsgSender<ApiMsg>,
     payload_sender: PayloadSender,
     namespace_id: IdNamespace,
     next_id: Cell<ResourceId>,
 }
--- a/gfx/webrender_api/src/units.rs
+++ b/gfx/webrender_api/src/units.rs
@@ -118,22 +118,16 @@ pub type PictureToRasterTransform = Type
 pub type RasterToPictureTransform = TypedTransform3D<f32, RasterPixel, PicturePixel>;
 
 // Fixed position coordinates, to avoid float precision errors.
 pub type LayoutPointAu = TypedPoint2D<Au, LayoutPixel>;
 pub type LayoutRectAu = TypedRect<Au, LayoutPixel>;
 pub type LayoutSizeAu = TypedSize2D<Au, LayoutPixel>;
 pub type LayoutVector2DAu = TypedVector2D<Au, LayoutPixel>;
 
-/// Coordinates in normalized space (between zero and one).
-#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
-pub struct NormalizedCoordinates;
-
-pub type NormalizedRect = TypedRect<f32, NormalizedCoordinates>;
-
 /// 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, Serialize, Deserialize)]
 pub struct TexelRect {
     pub uv0: DevicePoint,
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-0d2e9611c07e04ac830277f16a3b46bf125b9e7c
+d7a6d081384ce0da9dd359b0cf4b9f758aab1b67
--- a/gfx/wrench/src/rawtest.rs
+++ b/gfx/wrench/src/rawtest.rs
@@ -180,19 +180,19 @@ impl<'a> RawtestHarness<'a> {
             image_size,
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img,
             ColorF::WHITE,
         );
         txn.set_image_visible_area(
             blob_img,
-            NormalizedRect {
-                origin: point2(0.0, 0.03),
-                size: size2(1.0, 0.03),
+            DeviceUintRect {
+                origin: point2(0, 111256 / 30),
+                size: size2(1510, 111256 / 30),
             }
         );
 
         builder.pop_clip_id();
 
         let mut epoch = Epoch(0);
 
         self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
@@ -305,19 +305,19 @@ impl<'a> RawtestHarness<'a> {
                 false,
                 false
             ),
             ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
             Some(100),
         );
         // Set a visible rectangle that is too small.
         // This will force sync rasterization of the missing tiles during frame building.
-        txn.set_image_visible_area(blob_img2, NormalizedRect {
-            origin: point2(0.25, 0.25),
-            size: size2(0.1, 0.1),
+        txn.set_image_visible_area(blob_img2, DeviceUintRect {
+            origin: point2(200, 200),
+            size: size2(80, 80),
         });
 
         builder.push_image(
             &info,
             image_size,
             image_size,
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,