Bug 1536121 - rearchitect the webrender display-list. r=gw
authorAlexis Beingessner <a.beingessner@gmail.com>
Tue, 23 Apr 2019 17:29:58 +0000
changeset 470519 9550416c06abcb24b583cecc7cc09220e3208318
parent 470518 63ab1f26526681b3a14ba1e839a9f0ad13eb390d
child 470520 ba4736a11504d19edc53805e38079349f4fb9744
push id35906
push useraciure@mozilla.com
push dateTue, 23 Apr 2019 22:14:56 +0000
treeherdermozilla-central@0ce3633f8b80 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgw
bugs1536121
milestone68.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 1536121 - rearchitect the webrender display-list. r=gw disclaimer: this isn't an *amazing* cleanup, but more of a major step that unlocks the ability to do more minor cleanups and refinements. There's some messy things and inconsistencies here and there, but we can hopefully iron them out over time. 1. The primary change here is to move from struct { common_fields, enum(specific_fields) } to enum (maybe_common_fields, specific_fields) most notably this drops the common fields from a ton of things that don't need them PopXXX, SetXXX, ClipChain, etc. 2. Additionally some types have had some redundant states shaved off, for instance, rect no longer has *both* bounds and a clip_rect, as the intersection of the two can be used. This was done a bit conservatively as some adjustments will need to be done to the backend to fully eliminate some states, and this can be done more incrementally. 2.5. As a minor side-effect of 2, we now early-reject some primitives whose bounds and clip_rect are disjoint. 3. A HitTest display item has been added, which is just a Rect without color. In addition to the minor space wins from this, this makes it much easier to debug display lists 4. Adds a bunch of comments to the display list, making it easier to understand things. The end result of all these changes is a significantly smaller and easier to understand display list. Especially on pages like gmail which have so many clip chains. However this ultimately just makes text an even greater percentage of pages (often 70-80%). Differential Revision: https://phabricator.services.mozilla.com/D27439
gfx/webrender_bindings/WebRenderAPI.cpp
gfx/webrender_bindings/WebRenderAPI.h
gfx/webrender_bindings/src/bindings.rs
gfx/wr/direct-composition/src/main_windows.rs
gfx/wr/examples/alpha_perf.rs
gfx/wr/examples/animation.rs
gfx/wr/examples/basic.rs
gfx/wr/examples/blob.rs
gfx/wr/examples/document.rs
gfx/wr/examples/frame_output.rs
gfx/wr/examples/iframe.rs
gfx/wr/examples/image_resize.rs
gfx/wr/examples/multiwindow.rs
gfx/wr/examples/scrolling.rs
gfx/wr/examples/texture_cache_stress.rs
gfx/wr/examples/yuv.rs
gfx/wr/webrender/src/border.rs
gfx/wr/webrender/src/box_shadow.rs
gfx/wr/webrender/src/display_list_flattener.rs
gfx/wr/webrender/src/hit_test.rs
gfx/wr/webrender/src/internal_types.rs
gfx/wr/webrender/src/prim_store/borders.rs
gfx/wr/webrender/src/prim_store/gradient.rs
gfx/wr/webrender/src/prim_store/image.rs
gfx/wr/webrender/src/prim_store/line_dec.rs
gfx/wr/webrender/src/prim_store/mod.rs
gfx/wr/webrender/src/prim_store/picture.rs
gfx/wr/webrender/src/prim_store/text_run.rs
gfx/wr/webrender/src/render_backend.rs
gfx/wr/webrender_api/src/display_item.rs
gfx/wr/webrender_api/src/display_list.rs
gfx/wr/wrench/src/rawtest.rs
gfx/wr/wrench/src/yaml_frame_reader.rs
gfx/wr/wrench/src/yaml_frame_writer.rs
layout/painting/nsDisplayList.cpp
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -932,16 +932,26 @@ void DisplayListBuilder::PushRoundedRect
   // TODO: use `mCurrentSpaceAndClipChain.clip_chain` as a parent?
   auto clipId = DefineClip(Nothing(), aBounds, &clips, nullptr);
   auto spaceAndClip = WrSpaceAndClip{mCurrentSpaceAndClipChain.space, clipId};
 
   wr_dp_push_rect_with_parent_clip(mWrState, aBounds, clip, aIsBackfaceVisible,
                                    &spaceAndClip, aColor);
 }
 
+void DisplayListBuilder::PushHitTest(const wr::LayoutRect& aBounds,
+                                     const wr::LayoutRect& aClip,
+                                     bool aIsBackfaceVisible) {
+  wr::LayoutRect clip = MergeClipLeaf(aClip);
+  WRDL_LOG("PushHitTest b=%s cl=%s\n", mWrState, Stringify(aBounds).c_str(),
+           Stringify(clip).c_str());
+  wr_dp_push_hit_test(mWrState, aBounds, clip, aIsBackfaceVisible,
+                      &mCurrentSpaceAndClipChain);
+}
+
 void DisplayListBuilder::PushClearRect(const wr::LayoutRect& aBounds) {
   wr::LayoutRect clip = MergeClipLeaf(aBounds);
   WRDL_LOG("PushClearRect b=%s c=%s\n", mWrState, Stringify(aBounds).c_str(),
            Stringify(clip).c_str());
   wr_dp_push_clear_rect(mWrState, aBounds, clip, &mCurrentSpaceAndClipChain);
 }
 
 void DisplayListBuilder::PushClearRectWithComplexRegion(
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -418,17 +418,18 @@ class DisplayListBuilder final {
       const wr::LayoutRect& aContentRect, const wr::LayoutRect& aClipRect,
       const wr::LayoutPoint& aScrollOffset);
 
   void PushRect(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
                 bool aIsBackfaceVisible, const wr::ColorF& aColor);
   void PushRoundedRect(const wr::LayoutRect& aBounds,
                        const wr::LayoutRect& aClip, bool aIsBackfaceVisible,
                        const wr::ColorF& aColor);
-
+  void PushHitTest(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+                   bool aIsBackfaceVisible);
   void PushClearRect(const wr::LayoutRect& aBounds);
   void PushClearRectWithComplexRegion(const wr::LayoutRect& aBounds,
                                       const wr::ComplexClipRegion& aRegion);
 
   void PushLinearGradient(const wr::LayoutRect& aBounds,
                           const wr::LayoutRect& aClip, bool aIsBackfaceVisible,
                           const wr::LayoutPoint& aStartPoint,
                           const wr::LayoutPoint& aEndPoint,
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -2138,38 +2138,33 @@ pub extern "C" fn wr_dp_push_stacking_co
 
         let reference_frame_kind = match params.reference_frame_kind {
             WrReferenceFrameKind::Transform => ReferenceFrameKind::Transform,
             WrReferenceFrameKind::Perspective => ReferenceFrameKind::Perspective {
                 scrolling_relative_to,
             },
         };
         wr_spatial_id = state.frame_builder.dl_builder.push_reference_frame(
-            &bounds,
+            bounds.origin,
             wr_spatial_id,
             params.transform_style,
             transform_binding,
             reference_frame_kind,
         );
 
         bounds.origin = LayoutPoint::zero();
         result.id = wr_spatial_id.0;
         assert_ne!(wr_spatial_id.0, 0);
     }
 
-    let prim_info = LayoutPrimitiveInfo {
-        is_backface_visible: params.is_backface_visible,
-        tag: state.current_tag,
-        .. LayoutPrimitiveInfo::new(bounds)
-    };
-
     state.frame_builder
          .dl_builder
-         .push_stacking_context(&prim_info,
+         .push_stacking_context(bounds.origin,
                                 wr_spatial_id,
+                                params.is_backface_visible,
                                 wr_clip_id,
                                 params.transform_style,
                                 params.mix_blend_mode,
                                 &filters,
                                 &r_filter_datas,
                                 glyph_raster_space,
                                 params.cache_tiles);
 
@@ -2314,100 +2309,165 @@ pub extern "C" fn wr_dp_define_scroll_la
 
     WrSpaceAndClip::from_webrender(space_and_clip)
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_iframe(state: &mut WrState,
                                     rect: LayoutRect,
                                     clip: LayoutRect,
-                                    is_backface_visible: bool,
+                                    _is_backface_visible: bool,
                                     parent: &WrSpaceAndClipChain,
                                     pipeline_id: WrPipelineId,
                                     ignore_missing_pipeline: bool) {
     debug_assert!(unsafe { is_in_main_thread() });
 
-    let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(rect, clip.into());
-    prim_info.is_backface_visible = is_backface_visible;
-    prim_info.tag = state.current_tag;
     state.frame_builder.dl_builder.push_iframe(
-        &prim_info,
+        rect,
+        clip,
         &parent.to_webrender(state.pipeline_id),
         pipeline_id,
         ignore_missing_pipeline,
     );
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_rect(state: &mut WrState,
                                   rect: LayoutRect,
                                   clip: LayoutRect,
                                   is_backface_visible: bool,
                                   parent: &WrSpaceAndClipChain,
                                   color: ColorF) {
     debug_assert!(unsafe { !is_in_render_thread() });
 
-    let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(rect, clip.into());
-    prim_info.is_backface_visible = is_backface_visible;
-    prim_info.tag = state.current_tag;
+    let space_and_clip = parent.to_webrender(state.pipeline_id);
+    let clip_rect = clip.intersection(&rect);
+
+    let prim_info = CommonItemProperties {
+        // NB: the damp-e10s talos-test will frequently crash on startup if we
+        // early-return here for empty rects. I couldn't figure out why, but
+        // it's pretty harmless to feed these through, so, uh, we do?
+        clip_rect: clip_rect.unwrap_or(LayoutRect::zero()),
+        clip_id: space_and_clip.clip_id,
+        spatial_id: space_and_clip.spatial_id,
+        is_backface_visible,
+        hit_info: state.current_tag,
+    };
+
     state.frame_builder.dl_builder.push_rect(
         &prim_info,
-        &parent.to_webrender(state.pipeline_id),
         color,
     );
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_rect_with_parent_clip(
     state: &mut WrState,
     rect: LayoutRect,
     clip: LayoutRect,
     is_backface_visible: bool,
     parent: &WrSpaceAndClip,
     color: ColorF,
 ) {
     debug_assert!(unsafe { !is_in_render_thread() });
 
-    let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(rect, clip.into());
-    prim_info.is_backface_visible = is_backface_visible;
-    prim_info.tag = state.current_tag;
+    let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+    let clip_rect = clip.intersection(&rect);
+    if clip_rect.is_none() { return; }
+
+    let prim_info = CommonItemProperties {
+        clip_rect: clip_rect.unwrap(),
+        clip_id: space_and_clip.clip_id,
+        spatial_id: space_and_clip.spatial_id,
+        is_backface_visible,
+        hit_info: state.current_tag,
+    };
+
     state.frame_builder.dl_builder.push_rect(
         &prim_info,
-        &parent.to_webrender(state.pipeline_id),
         color,
     );
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_clear_rect(state: &mut WrState,
                                         rect: LayoutRect,
                                         clip: LayoutRect,
                                         parent: &WrSpaceAndClipChain) {
     debug_assert!(unsafe { !is_in_render_thread() });
 
-    let prim_info = LayoutPrimitiveInfo::with_clip_rect(rect, clip.into());
+    let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+    let clip_rect = clip.intersection(&rect);
+    if clip_rect.is_none() { return; }
+
+    let prim_info = CommonItemProperties {
+        clip_rect: clip_rect.unwrap(),
+        clip_id: space_and_clip.clip_id,
+        spatial_id: space_and_clip.spatial_id,
+        is_backface_visible: true,
+        hit_info: state.current_tag,
+    };
+
     state.frame_builder.dl_builder.push_clear_rect(
         &prim_info,
-        &parent.to_webrender(state.pipeline_id),
+    );
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_hit_test(state: &mut WrState,
+                                      rect: LayoutRect,
+                                      clip: LayoutRect,
+                                      is_backface_visible: bool,
+                                      parent: &WrSpaceAndClipChain) {
+    debug_assert!(unsafe { !is_in_render_thread() });
+
+    let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+    let clip_rect = clip.intersection(&rect);
+    if clip_rect.is_none() { return; }
+
+    let prim_info = CommonItemProperties {
+        clip_rect: clip_rect.unwrap(),
+        clip_id: space_and_clip.clip_id,
+        spatial_id: space_and_clip.spatial_id,
+        is_backface_visible,
+        hit_info: state.current_tag,
+    };
+
+    state.frame_builder.dl_builder.push_hit_test(
+        &prim_info,
     );
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_clear_rect_with_parent_clip(
     state: &mut WrState,
     rect: LayoutRect,
     clip: LayoutRect,
     parent: &WrSpaceAndClip,
 ) {
     debug_assert!(unsafe { !is_in_render_thread() });
 
-    let prim_info = LayoutPrimitiveInfo::with_clip_rect(rect, clip.into());
+    let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+    let clip_rect = clip.intersection(&rect);
+    if clip_rect.is_none() { return; }
+
+    let prim_info = CommonItemProperties {
+        clip_rect: clip_rect.unwrap(),
+        clip_id: space_and_clip.clip_id,
+        spatial_id: space_and_clip.spatial_id,
+        is_backface_visible: true,
+        hit_info: state.current_tag,
+    };
+
     state.frame_builder.dl_builder.push_clear_rect(
         &prim_info,
-        &parent.to_webrender(state.pipeline_id),
     );
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_image(state: &mut WrState,
                                    bounds: LayoutRect,
                                    clip: LayoutRect,
                                    is_backface_visible: bool,
@@ -2415,28 +2475,36 @@ pub extern "C" fn wr_dp_push_image(state
                                    stretch_size: LayoutSize,
                                    tile_spacing: LayoutSize,
                                    image_rendering: ImageRendering,
                                    key: WrImageKey,
                                    premultiplied_alpha: bool,
                                    color: ColorF) {
     debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
 
-    let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(bounds, clip.into());
-    prim_info.is_backface_visible = is_backface_visible;
-    prim_info.tag = state.current_tag;
+    let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+    let prim_info = CommonItemProperties {
+        clip_rect: clip,
+        clip_id: space_and_clip.clip_id,
+        spatial_id: space_and_clip.spatial_id,
+        is_backface_visible,
+        hit_info: state.current_tag,
+    };
+
     let alpha_type = if premultiplied_alpha {
         AlphaType::PremultipliedAlpha
     } else {
         AlphaType::Alpha
     };
+
     state.frame_builder
          .dl_builder
          .push_image(&prim_info,
-                     &parent.to_webrender(state.pipeline_id),
+                     bounds,
                      stretch_size,
                      tile_spacing,
                      image_rendering,
                      alpha_type,
                      key,
                      color);
 }
 
@@ -2450,23 +2518,30 @@ pub extern "C" fn wr_dp_push_yuv_planar_
                                               image_key_0: WrImageKey,
                                               image_key_1: WrImageKey,
                                               image_key_2: WrImageKey,
                                               color_depth: WrColorDepth,
                                               color_space: WrYuvColorSpace,
                                               image_rendering: ImageRendering) {
     debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
 
-    let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(bounds, clip.into());
-    prim_info.is_backface_visible = is_backface_visible;
-    prim_info.tag = state.current_tag;
+    let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+    let prim_info = CommonItemProperties {
+        clip_rect: clip,
+        clip_id: space_and_clip.clip_id,
+        spatial_id: space_and_clip.spatial_id,
+        is_backface_visible,
+        hit_info: state.current_tag,
+    };
+
     state.frame_builder
          .dl_builder
          .push_yuv_image(&prim_info,
-                         &parent.to_webrender(state.pipeline_id),
+                         bounds,
                          YuvData::PlanarYCbCr(image_key_0, image_key_1, image_key_2),
                          color_depth,
                          color_space,
                          image_rendering);
 }
 
 /// Push a 2 planar NV12 image.
 #[no_mangle]
@@ -2477,23 +2552,30 @@ pub extern "C" fn wr_dp_push_yuv_NV12_im
                                             parent: &WrSpaceAndClipChain,
                                             image_key_0: WrImageKey,
                                             image_key_1: WrImageKey,
                                             color_depth: WrColorDepth,
                                             color_space: WrYuvColorSpace,
                                             image_rendering: ImageRendering) {
     debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
 
-    let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(bounds, clip.into());
-    prim_info.is_backface_visible = is_backface_visible;
-    prim_info.tag = state.current_tag;
+    let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+    let prim_info = CommonItemProperties {
+        clip_rect: clip,
+        clip_id: space_and_clip.clip_id,
+        spatial_id: space_and_clip.spatial_id,
+        is_backface_visible,
+        hit_info: state.current_tag,
+    };
+
     state.frame_builder
          .dl_builder
          .push_yuv_image(&prim_info,
-                         &parent.to_webrender(state.pipeline_id),
+                         bounds,
                          YuvData::NV12(image_key_0, image_key_1),
                          color_depth,
                          color_space,
                          image_rendering);
 }
 
 /// Push a yuv interleaved image.
 #[no_mangle]
@@ -2503,23 +2585,30 @@ pub extern "C" fn wr_dp_push_yuv_interle
                                                    is_backface_visible: bool,
                                                    parent: &WrSpaceAndClipChain,
                                                    image_key_0: WrImageKey,
                                                    color_depth: WrColorDepth,
                                                    color_space: WrYuvColorSpace,
                                                    image_rendering: ImageRendering) {
     debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
 
-    let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(bounds, clip.into());
-    prim_info.is_backface_visible = is_backface_visible;
-    prim_info.tag = state.current_tag;
+    let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+    let prim_info = CommonItemProperties {
+        clip_rect: clip,
+        clip_id: space_and_clip.clip_id,
+        spatial_id: space_and_clip.spatial_id,
+        is_backface_visible,
+        hit_info: state.current_tag,
+    };
+
     state.frame_builder
          .dl_builder
          .push_yuv_image(&prim_info,
-                         &parent.to_webrender(state.pipeline_id),
+                         bounds,
                          YuvData::InterleavedYCbCr(image_key_0),
                          color_depth,
                          color_space,
                          image_rendering);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_text(state: &mut WrState,
@@ -2531,43 +2620,46 @@ pub extern "C" fn wr_dp_push_text(state:
                                   font_key: WrFontInstanceKey,
                                   glyphs: *const GlyphInstance,
                                   glyph_count: u32,
                                   glyph_options: *const GlyphOptions) {
     debug_assert!(unsafe { is_in_main_thread() });
 
     let glyph_slice = unsafe { make_slice(glyphs, glyph_count as usize) };
 
-    let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(bounds, clip.into());
-    prim_info.is_backface_visible = is_backface_visible;
-    prim_info.tag = state.current_tag;
+    let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+    let prim_info = CommonItemProperties {
+        clip_rect: clip,
+        spatial_id: space_and_clip.spatial_id,
+        clip_id: space_and_clip.clip_id,
+        is_backface_visible,
+        hit_info: state.current_tag
+    };
+
     state.frame_builder
          .dl_builder
          .push_text(&prim_info,
-                    &parent.to_webrender(state.pipeline_id),
+                    bounds,
                     &glyph_slice,
                     font_key,
                     color,
                     unsafe { glyph_options.as_ref().cloned() });
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_shadow(state: &mut WrState,
-                                    bounds: LayoutRect,
-                                    clip: LayoutRect,
-                                    is_backface_visible: bool,
+                                    _bounds: LayoutRect,
+                                    _clip: LayoutRect,
+                                    _is_backface_visible: bool,
                                     parent: &WrSpaceAndClipChain,
                                     shadow: Shadow) {
     debug_assert!(unsafe { is_in_main_thread() });
 
-    let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(bounds, clip.into());
-    prim_info.is_backface_visible = is_backface_visible;
-    prim_info.tag = state.current_tag;
     state.frame_builder.dl_builder.push_shadow(
-        &prim_info,
         &parent.to_webrender(state.pipeline_id),
         shadow.into(),
     );
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_pop_all_shadows(state: &mut WrState) {
     debug_assert!(unsafe { is_in_main_thread() });
@@ -2582,23 +2674,30 @@ pub extern "C" fn wr_dp_push_line(state:
                                   parent: &WrSpaceAndClipChain,
                                   bounds: &LayoutRect,
                                   wavy_line_thickness: f32,
                                   orientation: LineOrientation,
                                   color: &ColorF,
                                   style: LineStyle) {
     debug_assert!(unsafe { is_in_main_thread() });
 
-    let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(*bounds, (*clip).into());
-    prim_info.is_backface_visible = is_backface_visible;
-    prim_info.tag = state.current_tag;
+    let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+    let prim_info = CommonItemProperties {
+        clip_rect: *clip,
+        clip_id: space_and_clip.clip_id,
+        spatial_id: space_and_clip.spatial_id,
+        is_backface_visible,
+        hit_info: state.current_tag,
+    };
+
     state.frame_builder
          .dl_builder
          .push_line(&prim_info,
-                    &parent.to_webrender(state.pipeline_id),
+                    bounds,
                     wavy_line_thickness,
                     orientation,
                     color,
                     style);
 
 }
 
 #[no_mangle]
@@ -2620,23 +2719,30 @@ pub extern "C" fn wr_dp_push_border(stat
         left: left.into(),
         right: right.into(),
         top: top.into(),
         bottom: bottom.into(),
         radius: radius.into(),
         do_aa: do_aa == AntialiasBorder::Yes,
     });
 
-    let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(rect, clip.into());
-    prim_info.is_backface_visible = is_backface_visible;
-    prim_info.tag = state.current_tag;
+    let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+    let prim_info = CommonItemProperties {
+        clip_rect: clip,
+        clip_id: space_and_clip.clip_id,
+        spatial_id: space_and_clip.spatial_id,
+        is_backface_visible,
+        hit_info: state.current_tag,
+    };
+
     state.frame_builder
          .dl_builder
          .push_border(&prim_info,
-                      &parent.to_webrender(state.pipeline_id),
+                      rect,
                       widths,
                       border_details);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_border_image(state: &mut WrState,
                                           rect: LayoutRect,
                                           clip: LayoutRect,
@@ -2656,22 +2762,29 @@ pub extern "C" fn wr_dp_push_border_imag
         width,
         height,
         slice,
         fill: false,
         outset: outset.into(),
         repeat_horizontal: repeat_horizontal.into(),
         repeat_vertical: repeat_vertical.into(),
     });
-    let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(rect, clip.into());
-    prim_info.is_backface_visible = is_backface_visible;
-    prim_info.tag = state.current_tag;
+    let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+    let prim_info = CommonItemProperties {
+        clip_rect: clip,
+        clip_id: space_and_clip.clip_id,
+        spatial_id: space_and_clip.spatial_id,
+        is_backface_visible,
+        hit_info: state.current_tag,
+    };
+
     state.frame_builder.dl_builder.push_border(
         &prim_info,
-        &parent.to_webrender(state.pipeline_id),
+        rect,
         widths.into(),
         border_details,
     );
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_border_gradient(state: &mut WrState,
                                              rect: LayoutRect,
@@ -2706,22 +2819,29 @@ pub extern "C" fn wr_dp_push_border_grad
         height,
         slice,
         fill: false,
         outset: outset.into(),
         repeat_horizontal: RepeatMode::Stretch,
         repeat_vertical: RepeatMode::Stretch,
     });
 
-    let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(rect, clip.into());
-    prim_info.is_backface_visible = is_backface_visible;
-    prim_info.tag = state.current_tag;
+    let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+    let prim_info = CommonItemProperties {
+        clip_rect: clip,
+        clip_id: space_and_clip.clip_id,
+        spatial_id: space_and_clip.spatial_id,
+        is_backface_visible,
+        hit_info: state.current_tag,
+    };
+
     state.frame_builder.dl_builder.push_border(
         &prim_info,
-        &parent.to_webrender(state.pipeline_id),
+        rect,
         widths.into(),
         border_details,
     );
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_border_radial_gradient(state: &mut WrState,
                                                     rect: LayoutRect,
@@ -2759,22 +2879,30 @@ pub extern "C" fn wr_dp_push_border_radi
         width: rect.size.width as i32,
         height: rect.size.height as i32,
         slice,
         fill: false,
         outset: outset.into(),
         repeat_horizontal: RepeatMode::Stretch,
         repeat_vertical: RepeatMode::Stretch,
     });
-    let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(rect, clip.into());
-    prim_info.is_backface_visible = is_backface_visible;
-    prim_info.tag = state.current_tag;
+
+    let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+    let prim_info = CommonItemProperties {
+        clip_rect: clip,
+        clip_id: space_and_clip.clip_id,
+        spatial_id: space_and_clip.spatial_id,
+        is_backface_visible,
+        hit_info: state.current_tag,
+    };
+
     state.frame_builder.dl_builder.push_border(
         &prim_info,
-        &parent.to_webrender(state.pipeline_id),
+        rect,
         widths.into(),
         border_details,
     );
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_linear_gradient(state: &mut WrState,
                                              rect: LayoutRect,
@@ -2794,22 +2922,30 @@ pub extern "C" fn wr_dp_push_linear_grad
     let stops_vector = stops_slice.to_owned();
 
     let gradient = state.frame_builder
                         .dl_builder
                         .create_gradient(start_point.into(),
                                          end_point.into(),
                                          stops_vector,
                                          extend_mode.into());
-    let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(rect, clip.into());
-    prim_info.is_backface_visible = is_backface_visible;
-    prim_info.tag = state.current_tag;
+
+    let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+    let prim_info = CommonItemProperties {
+        clip_rect: clip,
+        clip_id: space_and_clip.clip_id,
+        spatial_id: space_and_clip.spatial_id,
+        is_backface_visible,
+        hit_info: state.current_tag,
+    };
+
     state.frame_builder.dl_builder.push_gradient(
         &prim_info,
-        &parent.to_webrender(state.pipeline_id),
+        rect,
         gradient,
         tile_size.into(),
         tile_spacing.into(),
     );
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_radial_gradient(state: &mut WrState,
@@ -2830,49 +2966,63 @@ pub extern "C" fn wr_dp_push_radial_grad
     let stops_vector = stops_slice.to_owned();
 
     let gradient = state.frame_builder
                         .dl_builder
                         .create_radial_gradient(center.into(),
                                                 radius.into(),
                                                 stops_vector,
                                                 extend_mode.into());
-    let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(rect, clip.into());
-    prim_info.is_backface_visible = is_backface_visible;
-    prim_info.tag = state.current_tag;
+
+    let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+    let prim_info = CommonItemProperties {
+        clip_rect: clip,
+        clip_id: space_and_clip.clip_id,
+        spatial_id: space_and_clip.spatial_id,
+        is_backface_visible,
+        hit_info: state.current_tag,
+    };
+
     state.frame_builder.dl_builder.push_radial_gradient(
         &prim_info,
-        &parent.to_webrender(state.pipeline_id),
+        rect,
         gradient,
         tile_size,
         tile_spacing);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_box_shadow(state: &mut WrState,
-                                        rect: LayoutRect,
+                                        _rect: LayoutRect,
                                         clip: LayoutRect,
                                         is_backface_visible: bool,
                                         parent: &WrSpaceAndClipChain,
                                         box_bounds: LayoutRect,
                                         offset: LayoutVector2D,
                                         color: ColorF,
                                         blur_radius: f32,
                                         spread_radius: f32,
                                         border_radius: BorderRadius,
                                         clip_mode: BoxShadowClipMode) {
     debug_assert!(unsafe { is_in_main_thread() });
 
-    let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(rect, clip.into());
-    prim_info.is_backface_visible = is_backface_visible;
-    prim_info.tag = state.current_tag;
+    let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+    let prim_info = CommonItemProperties {
+        clip_rect: clip,
+        clip_id: space_and_clip.clip_id,
+        spatial_id: space_and_clip.spatial_id,
+        is_backface_visible,
+        hit_info: state.current_tag,
+    };
+
     state.frame_builder
          .dl_builder
          .push_box_shadow(&prim_info,
-                          &parent.to_webrender(state.pipeline_id),
                           box_bounds,
                           offset,
                           color,
                           blur_radius,
                           spread_radius,
                           border_radius,
                           clip_mode);
 }
--- a/gfx/wr/direct-composition/src/main_windows.rs
+++ b/gfx/wr/direct-composition/src/main_windows.rs
@@ -146,21 +146,23 @@ impl Rectangle {
         let clip_id = builder.define_clip(
             &api::SpaceAndClipInfo::root_scroll(pipeline_id),
             rect,
             vec![region],
             None,
         );
 
         builder.push_rect(
-            &api::PrimitiveInfo::new(rect),
-            &api::SpaceAndClipInfo {
-                spatial_id: api::SpatialId::root_scroll_node(pipeline_id),
-                clip_id,
-            },
+            &api::CommonItemProperties::new(
+                rect,
+                api::SpaceAndClipInfo {
+                    spatial_id: api::SpatialId::root_scroll_node(pipeline_id),
+                    clip_id,
+                },
+            ),
             self.color,
         );
 
         let mut transaction = api::Transaction::new();
         transaction.set_display_list(
             api::Epoch(0),
             None,
             layout_size,
--- a/gfx/wr/examples/alpha_perf.rs
+++ b/gfx/wr/examples/alpha_perf.rs
@@ -27,26 +27,29 @@ impl Example for App {
         _api: &RenderApi,
         builder: &mut DisplayListBuilder,
         _txn: &mut Transaction,
         _framebuffer_size: FramebufferIntSize,
         pipeline_id: PipelineId,
         _document_id: DocumentId,
     ) {
         let bounds = (0, 0).to(1920, 1080);
-        let info = LayoutPrimitiveInfo::new(bounds);
         let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
 
         builder.push_simple_stacking_context(
-            &info,
+            bounds.origin,
             space_and_clip.spatial_id,
+            true,
         );
 
         for _ in 0 .. self.rect_count {
-            builder.push_rect(&info, &space_and_clip, ColorF::new(1.0, 1.0, 1.0, 0.05));
+            builder.push_rect(
+                &CommonItemProperties::new(bounds, space_and_clip),
+                ColorF::new(1.0, 1.0, 1.0, 0.05)
+            );
         }
 
         builder.pop_stacking_context();
     }
 
     fn on_event(
         &mut self,
         event: winit::WindowEvent,
--- a/gfx/wr/examples/animation.rs
+++ b/gfx/wr/examples/animation.rs
@@ -53,26 +53,27 @@ impl App {
                 ]
             }
             None => {
                 vec![]
             }
         };
 
         let spatial_id = builder.push_reference_frame(
-            &LayoutRect::new(bounds.origin, LayoutSize::zero()),
+            bounds.origin,
             SpatialId::root_scroll_node(pipeline_id),
             TransformStyle::Flat,
             PropertyBinding::Binding(property_key, LayoutTransform::identity()),
             ReferenceFrameKind::Transform,
         );
 
         builder.push_simple_stacking_context_with_filters(
-            &LayoutPrimitiveInfo::new(LayoutRect::zero()),
+            LayoutPoint::zero(),
             spatial_id,
+            true,
             &filters,
             &[],
         );
 
         let space_and_clip = SpaceAndClipInfo {
             spatial_id,
             clip_id: ClipId::root(pipeline_id),
         };
@@ -81,21 +82,23 @@ impl App {
             rect: clip_bounds,
             radii: BorderRadius::uniform(30.0),
             mode: ClipMode::Clip,
         };
         let clip_id = builder.define_clip(&space_and_clip, clip_bounds, vec![complex_clip], None);
 
         // Fill it with a white rect
         builder.push_rect(
-            &LayoutPrimitiveInfo::new(LayoutRect::new(LayoutPoint::zero(), bounds.size)),
-            &SpaceAndClipInfo {
-                spatial_id,
-                clip_id,
-            },
+            &CommonItemProperties::new(
+                LayoutRect::new(LayoutPoint::zero(), bounds.size),
+                SpaceAndClipInfo {
+                    spatial_id,
+                    clip_id,
+                }
+            ),
             color,
         );
 
         builder.pop_stacking_context();
         builder.pop_reference_frame();
     }
 }
 
--- a/gfx/wr/examples/basic.rs
+++ b/gfx/wr/examples/basic.rs
@@ -185,24 +185,24 @@ impl Example for App {
         &mut self,
         api: &RenderApi,
         builder: &mut DisplayListBuilder,
         txn: &mut Transaction,
         _: FramebufferIntSize,
         pipeline_id: PipelineId,
         _document_id: DocumentId,
     ) {
-        let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
-        let info = LayoutPrimitiveInfo::new(bounds);
+        let content_bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
         let root_space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
         let spatial_id = root_space_and_clip.spatial_id;
 
         builder.push_simple_stacking_context(
-            &info,
+            content_bounds.origin,
             spatial_id,
+            true,
         );
 
         let image_mask_key = api.generate_image_key();
         txn.add_image(
             image_mask_key,
             ImageDescriptor::new(2, 2, ImageFormat::R8, true, false),
             ImageData::new(vec![0, 80, 180, 255]),
             None,
@@ -212,64 +212,75 @@ impl Example for App {
             rect: (75, 75).by(100, 100),
             repeat: false,
         };
         let complex = ComplexClipRegion::new(
             (50, 50).to(150, 150),
             BorderRadius::uniform(20.0),
             ClipMode::Clip
         );
-        let clip_id = builder.define_clip(&root_space_and_clip, bounds, vec![complex], Some(mask));
+        let clip_id = builder.define_clip(
+            &root_space_and_clip,
+            content_bounds,
+            vec![complex],
+            Some(mask)
+        );
 
         builder.push_rect(
-            &LayoutPrimitiveInfo::new((100, 100).to(200, 200)),
-            &SpaceAndClipInfo { spatial_id, clip_id },
+            &CommonItemProperties::new(
+                (100, 100).to(200, 200),
+                SpaceAndClipInfo { spatial_id, clip_id },
+            ),
             ColorF::new(0.0, 1.0, 0.0, 1.0),
         );
 
         builder.push_rect(
-            &LayoutPrimitiveInfo::new((250, 100).to(350, 200)),
-            &SpaceAndClipInfo { spatial_id, clip_id },
+            &CommonItemProperties::new(
+                (250, 100).to(350, 200),
+                SpaceAndClipInfo { spatial_id, clip_id },
+            ),
             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: BorderStyle::Groove,
         };
         let border_widths = LayoutSideOffsets::new_all_same(10.0);
         let border_details = BorderDetails::Normal(NormalBorder {
             top: border_side,
             right: border_side,
             bottom: border_side,
             left: border_side,
             radius: BorderRadius::uniform(20.0),
             do_aa: true,
         });
 
+        let bounds = (100, 100).to(200, 200);
         builder.push_border(
-            &LayoutPrimitiveInfo::new((100, 100).to(200, 200)),
-            &SpaceAndClipInfo { spatial_id, clip_id },
+            &CommonItemProperties::new(
+                bounds,
+                SpaceAndClipInfo { spatial_id, clip_id },
+            ),
+            bounds,
             border_widths,
             border_details,
         );
 
         if false {
             // draw box shadow?
-            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;
 
             builder.push_box_shadow(
-                &LayoutPrimitiveInfo::with_clip_rect(rect, bounds),
-                &root_space_and_clip,
+                &CommonItemProperties::new(content_bounds, root_space_and_clip),
                 simple_box_bounds,
                 offset,
                 color,
                 blur_radius,
                 spread_radius,
                 BorderRadius::uniform(simple_border_radius),
                 box_shadow_type,
             );
--- a/gfx/wr/examples/blob.rs
+++ b/gfx/wr/examples/blob.rs
@@ -12,17 +12,17 @@ extern crate winit;
 mod boilerplate;
 
 use boilerplate::{Example, HandyDandyRectBuilder};
 use rayon::{ThreadPool, ThreadPoolBuilder};
 use rayon::prelude::*;
 use std::collections::HashMap;
 use std::sync::Arc;
 use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, RenderApi, Transaction};
-use webrender::api::{ColorF, SpaceAndClipInfo};
+use webrender::api::{ColorF, CommonItemProperties, SpaceAndClipInfo};
 use webrender::api::units::*;
 use webrender::euclid::size2;
 
 // This example shows how to implement a very basic BlobImageHandler that can only render
 // a checkerboard pattern.
 
 // The deserialized command list internally used by this example is just a color.
 type ImageRenderingCommands = api::ColorU;
@@ -215,38 +215,40 @@ impl Example for App {
         let blob_img2 = api.generate_blob_image_key();
         txn.add_blob_image(
             blob_img2,
             api::ImageDescriptor::new(200, 200, api::ImageFormat::BGRA8, true, false),
             serialize_blob(api::ColorU::new(50, 150, 50, 255)),
             None,
         );
 
-        let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
         let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
 
         builder.push_simple_stacking_context(
-            &api::LayoutPrimitiveInfo::new(bounds),
+            LayoutPoint::zero(),
             space_and_clip.spatial_id,
+            true,
         );
 
+        let bounds = (30, 30).by(500, 500);
         builder.push_image(
-            &api::LayoutPrimitiveInfo::new((30, 30).by(500, 500)),
-            &space_and_clip,
+            &CommonItemProperties::new(bounds, space_and_clip),
+            bounds,
             LayoutSize::new(500.0, 500.0),
             LayoutSize::new(0.0, 0.0),
             api::ImageRendering::Auto,
             api::AlphaType::PremultipliedAlpha,
             blob_img1.as_image(),
             ColorF::WHITE,
         );
 
+        let bounds = (600, 600).by(200, 200);
         builder.push_image(
-            &api::LayoutPrimitiveInfo::new((600, 600).by(200, 200)),
-            &space_and_clip,
+            &CommonItemProperties::new(bounds, space_and_clip),
+            bounds,
             LayoutSize::new(200.0, 200.0),
             LayoutSize::new(0.0, 0.0),
             api::ImageRendering::Auto,
             api::AlphaType::PremultipliedAlpha,
             blob_img2.as_image(),
             ColorF::WHITE,
         );
 
--- a/gfx/wr/examples/document.rs
+++ b/gfx/wr/examples/document.rs
@@ -111,22 +111,22 @@ impl Example for App {
                 doc.content_rect.size,
             );
             let local_rect = LayoutRect::new(
                 LayoutPoint::zero(),
                 doc.content_rect.size,
             );
 
             builder.push_simple_stacking_context(
-                &LayoutPrimitiveInfo::new(doc.content_rect),
+                doc.content_rect.origin,
                 space_and_clip.spatial_id,
+                true,
             );
             builder.push_rect(
-                &LayoutPrimitiveInfo::new(local_rect),
-                &space_and_clip,
+                &CommonItemProperties::new(local_rect, space_and_clip),
                 doc.color,
             );
             builder.pop_stacking_context();
 
             let mut txn = Transaction::new();
             txn.set_display_list(
                 Epoch(0),
                 None,
--- a/gfx/wr/examples/frame_output.rs
+++ b/gfx/wr/examples/frame_output.rs
@@ -108,29 +108,32 @@ impl App {
             ImageData::External(ExternalImageData {
                 id: ExternalImageId(0),
                 channel_index: 0,
                 image_type: ExternalImageType::TextureHandle(TextureTarget::Default),
             }),
             None,
         );
 
-        let info = LayoutPrimitiveInfo::new(document.content_rect);
         let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
         let mut builder = DisplayListBuilder::new(
             document.pipeline_id,
             document.content_rect.size,
         );
 
         builder.push_simple_stacking_context(
-            &info,
+            document.content_rect.origin,
             space_and_clip.spatial_id,
+            true,
         );
 
-        builder.push_rect(&info, &space_and_clip, ColorF::new(1.0, 1.0, 0.0, 1.0));
+        builder.push_rect(
+            &CommonItemProperties::new(document.content_rect, space_and_clip),
+            ColorF::new(1.0, 1.0, 0.0, 1.0)
+        );
         builder.pop_stacking_context();
 
         txn.set_root_pipeline(pipeline_id);
         txn.set_display_list(
             Epoch(0),
             Some(document.color),
             document.content_rect.size,
             builder.finalize(),
@@ -153,28 +156,29 @@ impl Example for App {
         _document_id: DocumentId,
     ) {
         if self.output_document.is_none() {
             let device_pixel_ratio = framebuffer_size.width as f32 /
                 builder.content_size().width;
             self.init_output_document(api, FramebufferIntSize::new(200, 200), device_pixel_ratio);
         }
 
-        let info = LayoutPrimitiveInfo::new((100, 100).to(200, 200));
+        let bounds = (100, 100).to(200, 200);
         let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
 
         builder.push_simple_stacking_context(
-            &info,
+            bounds.origin,
             space_and_clip.spatial_id,
+            true,
         );
 
         builder.push_image(
-            &info,
-            &space_and_clip,
-            info.rect.size,
+            &CommonItemProperties::new(bounds, space_and_clip),
+            bounds,
+            bounds.size,
             LayoutSize::zero(),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             self.external_image_key.unwrap(),
             ColorF::WHITE,
         );
 
         builder.pop_stacking_context();
--- a/gfx/wr/examples/iframe.rs
+++ b/gfx/wr/examples/iframe.rs
@@ -33,53 +33,60 @@ impl Example for App {
         // All the sub_* things are for the nested pipeline
         let sub_size = DeviceIntSize::new(100, 100);
         let sub_bounds = (0, 0).to(sub_size.width as i32, sub_size.height as i32);
 
         let sub_pipeline_id = PipelineId(pipeline_id.0, 42);
         let mut sub_builder = DisplayListBuilder::new(sub_pipeline_id, sub_bounds.size);
         let mut space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
 
-        let info = LayoutPrimitiveInfo::new(sub_bounds);
         sub_builder.push_simple_stacking_context(
-            &info,
+            sub_bounds.origin,
             space_and_clip.spatial_id,
+            true,
         );
 
         // green rect visible == success
-        sub_builder.push_rect(&info, &space_and_clip, ColorF::new(0.0, 1.0, 0.0, 1.0));
+        sub_builder.push_rect(
+            &CommonItemProperties::new(sub_bounds, space_and_clip),
+            ColorF::new(0.0, 1.0, 0.0, 1.0)
+        );
         sub_builder.pop_stacking_context();
 
         let mut txn = Transaction::new();
         txn.set_display_list(
             Epoch(0),
             None,
             sub_bounds.size,
             sub_builder.finalize(),
             true,
         );
         api.send_transaction(document_id, txn);
 
         space_and_clip.spatial_id = builder.push_reference_frame(
-            &sub_bounds,
+            sub_bounds.origin,
             space_and_clip.spatial_id,
             TransformStyle::Flat,
             PropertyBinding::Binding(PropertyBindingKey::new(42), LayoutTransform::identity()),
             ReferenceFrameKind::Transform,
         );
 
         // And this is for the root pipeline
-        let info = LayoutPrimitiveInfo::new(sub_bounds);
         builder.push_simple_stacking_context(
-            &info,
+            sub_bounds.origin,
             space_and_clip.spatial_id,
+            true,
         );
+
         // red rect under the iframe: if this is visible, things have gone wrong
-        builder.push_rect(&info, &space_and_clip, ColorF::new(1.0, 0.0, 0.0, 1.0));
-        builder.push_iframe(&info, &space_and_clip, sub_pipeline_id, false);
+        builder.push_rect(
+            &CommonItemProperties::new(sub_bounds, space_and_clip),
+            ColorF::new(1.0, 0.0, 0.0, 1.0)
+        );
+        builder.push_iframe(sub_bounds, sub_bounds, &space_and_clip, sub_pipeline_id, false);
         builder.pop_stacking_context();
         builder.pop_reference_frame();
     }
 }
 
 fn main() {
     let mut app = App {};
     boilerplate::main_wrapper(&mut app, None);
--- a/gfx/wr/examples/image_resize.rs
+++ b/gfx/wr/examples/image_resize.rs
@@ -34,48 +34,46 @@ impl Example for App {
         txn.add_image(
             self.image_key,
             image_descriptor,
             image_data,
             None,
         );
 
         let bounds = (0, 0).to(512, 512);
-        let info = LayoutPrimitiveInfo::new(bounds);
         let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
 
         builder.push_simple_stacking_context(
-            &info,
+            bounds.origin,
             space_and_clip.spatial_id,
+            true,
         );
 
         let image_size = LayoutSize::new(100.0, 100.0);
 
-        let info = LayoutPrimitiveInfo::with_clip_rect(
-            LayoutRect::new(LayoutPoint::new(100.0, 100.0), image_size),
+        builder.push_image(
+            &CommonItemProperties::new(
+                LayoutRect::new(LayoutPoint::new(100.0, 100.0), image_size),
+                space_and_clip,
+            ),
             bounds,
-        );
-        builder.push_image(
-            &info,
-            &space_and_clip,
             image_size,
             LayoutSize::zero(),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             self.image_key,
             ColorF::WHITE,
         );
 
-        let info = LayoutPrimitiveInfo::with_clip_rect(
-            LayoutRect::new(LayoutPoint::new(250.0, 100.0), image_size),
+        builder.push_image(
+            &CommonItemProperties::new(
+                LayoutRect::new(LayoutPoint::new(250.0, 100.0), image_size),
+                space_and_clip,
+            ),
             bounds,
-        );
-        builder.push_image(
-            &info,
-            &space_and_clip,
             image_size,
             LayoutSize::zero(),
             ImageRendering::Pixelated,
             AlphaType::PremultipliedAlpha,
             self.image_key,
             ColorF::WHITE,
         );
 
--- a/gfx/wr/examples/multiwindow.rs
+++ b/gfx/wr/examples/multiwindow.rs
@@ -184,27 +184,31 @@ impl Window {
             FramebufferIntSize::new(size.width as i32, size.height as i32)
         };
         let layout_size = framebuffer_size.to_f32() / euclid::TypedScale::new(device_pixel_ratio);
         let mut txn = Transaction::new();
         let mut builder = DisplayListBuilder::new(self.pipeline_id, layout_size);
         let space_and_clip = SpaceAndClipInfo::root_scroll(self.pipeline_id);
 
         let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
-        let info = LayoutPrimitiveInfo::new(bounds);
         builder.push_simple_stacking_context(
-            &info,
+            bounds.origin,
             space_and_clip.spatial_id,
+            true,
         );
 
-        let info = LayoutPrimitiveInfo::new(LayoutRect::new(
-            LayoutPoint::new(100.0, 100.0),
-            LayoutSize::new(100.0, 200.0)
-        ));
-        builder.push_rect(&info, &space_and_clip, ColorF::new(0.0, 1.0, 0.0, 1.0));
+        builder.push_rect(
+            &CommonItemProperties::new(
+                LayoutRect::new(
+                    LayoutPoint::new(100.0, 200.0),
+                    LayoutSize::new(100.0, 200.0),
+                ),
+                space_and_clip,
+            ),
+            ColorF::new(0.0, 1.0, 0.0, 1.0));
 
         let text_bounds = LayoutRect::new(
             LayoutPoint::new(100.0, 50.0),
             LayoutSize::new(700.0, 200.0)
         );
         let glyphs = vec![
             GlyphInstance {
                 index: 48,
@@ -251,20 +255,22 @@ impl Window {
                 point: LayoutPoint::new(600.0, 100.0),
             },
             GlyphInstance {
                 index: 17,
                 point: LayoutPoint::new(650.0, 100.0),
             },
         ];
 
-        let info = LayoutPrimitiveInfo::new(text_bounds);
         builder.push_text(
-            &info,
-            &space_and_clip,
+            &CommonItemProperties::new(
+                text_bounds,
+                space_and_clip,
+            ),
+            text_bounds,
             &glyphs,
             self.font_instance_key,
             ColorF::new(1.0, 1.0, 0.0, 1.0),
             None,
         );
 
         builder.pop_stacking_context();
 
--- a/gfx/wr/examples/scrolling.rs
+++ b/gfx/wr/examples/scrolling.rs
@@ -27,62 +27,63 @@ impl Example for App {
         &mut self,
         _api: &RenderApi,
         builder: &mut DisplayListBuilder,
         _txn: &mut Transaction,
         _framebuffer_size: FramebufferIntSize,
         pipeline_id: PipelineId,
         _document_id: DocumentId,
     ) {
-        let info = LayoutPrimitiveInfo::new(
-            LayoutRect::new(LayoutPoint::zero(), builder.content_size())
-        );
         let root_space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
         builder.push_simple_stacking_context(
-            &info,
+            LayoutPoint::zero(),
             root_space_and_clip.spatial_id,
+            true,
         );
 
         if true {
             // scrolling and clips stuff
             // let's make a scrollbox
             let scrollbox = (0, 0).to(300, 400);
             builder.push_simple_stacking_context(
-                &LayoutPrimitiveInfo::new((10, 10).by(0, 0)),
+                LayoutPoint::new(10., 10.),
                 root_space_and_clip.spatial_id,
+                true,
             );
             // set the scrolling clip
             let space_and_clip1 = builder.define_scroll_frame(
                 &root_space_and_clip,
                 None,
                 (0, 0).by(1000, 1000),
                 scrollbox,
                 vec![],
                 None,
                 ScrollSensitivity::ScriptAndInputEvents,
                 LayoutVector2D::zero(),
             );
 
             // now put some content into it.
             // start with a white background
-            let mut info = LayoutPrimitiveInfo::new((0, 0).to(1000, 1000));
-            info.tag = Some((0, 1));
-            builder.push_rect(&info, &space_and_clip1, ColorF::new(1.0, 1.0, 1.0, 1.0));
+            let mut info = CommonItemProperties::new((0, 0).to(1000, 1000), space_and_clip1);
+            info.hit_info = Some((0, 1));
+            builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 1.0));
 
             // let's make a 50x50 blue square as a visual reference
-            let mut info = LayoutPrimitiveInfo::new((0, 0).to(50, 50));
-            info.tag = Some((0, 2));
-            builder.push_rect(&info, &space_and_clip1, ColorF::new(0.0, 0.0, 1.0, 1.0));
+            let mut info = CommonItemProperties::new((0, 0).to(50, 50), space_and_clip1);
+            info.hit_info = Some((0, 2));
+            builder.push_rect(&info, 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 mut info =
-                LayoutPrimitiveInfo::with_clip_rect((50, 0).to(100, 50), (60, 10).to(110, 60));
-            info.tag = Some((0, 3));
-            builder.push_rect(&info, &space_and_clip1, ColorF::new(0.0, 1.0, 0.0, 1.0));
+            let mut info = CommonItemProperties::new(
+                (50, 0).to(100, 50).intersection(&(60, 10).to(110, 60)).unwrap(),
+                space_and_clip1,
+            );
+            info.hit_info = Some((0, 3));
+            builder.push_rect(&info, 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 space_and_clip2 = builder.define_scroll_frame(
                 &space_and_clip1,
                 None,
                 (0, 100).to(300, 1000),
@@ -90,55 +91,63 @@ impl Example for App {
                 vec![],
                 None,
                 ScrollSensitivity::ScriptAndInputEvents,
                 LayoutVector2D::zero(),
             );
 
             // give it a giant gray background just to distinguish it and to easily
             // visually identify the nested scrollbox
-            let mut info = LayoutPrimitiveInfo::new((-1000, -1000).to(5000, 5000));
-            info.tag = Some((0, 4));
-            builder.push_rect(&info, &space_and_clip2, ColorF::new(0.5, 0.5, 0.5, 1.0));
+            let mut info = CommonItemProperties::new(
+                (-1000, -1000).to(5000, 5000),
+                space_and_clip2,
+            );
+            info.hit_info = Some((0, 4));
+            builder.push_rect(&info, 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
-            let mut info = LayoutPrimitiveInfo::new((0, 200).to(50, 250));
-            info.tag = Some((0, 5));
-            builder.push_rect(&info, &space_and_clip2, ColorF::new(0.0, 1.0, 1.0, 1.0));
+            let mut info = CommonItemProperties::new((0, 200).to(50, 250), space_and_clip2);
+            info.hit_info = Some((0, 5));
+            builder.push_rect(&info, ColorF::new(0.0, 1.0, 1.0, 1.0));
 
             // Add a sticky frame. It will "stick" twice while scrolling, once
             // at a margin of 10px from the bottom, for 40 pixels of scrolling,
             // and once at a margin of 10px from the top, for 60 pixels of
             // scrolling.
             let sticky_id = builder.define_sticky_frame(
                 space_and_clip2.spatial_id,
                 (50, 350).by(50, 50),
                 SideOffsets2D::new(Some(10.0), None, Some(10.0), None),
                 StickyOffsetBounds::new(-40.0, 60.0),
                 StickyOffsetBounds::new(0.0, 0.0),
                 LayoutVector2D::new(0.0, 0.0)
             );
 
-            let mut info = LayoutPrimitiveInfo::new((50, 350).by(50, 50));
-            info.tag = Some((0, 6));
-            builder.push_rect(
-                &info,
-                &SpaceAndClipInfo {
+            let mut info = CommonItemProperties::new(
+                (50, 350).by(50, 50),
+                SpaceAndClipInfo {
                     spatial_id: sticky_id,
                     clip_id: space_and_clip2.clip_id,
                 },
+            );
+            info.hit_info = Some((0, 6));
+            builder.push_rect(
+                &info,
                 ColorF::new(0.5, 0.5, 1.0, 1.0),
             );
 
             // just for good measure add another teal square further down and to
             // the right, which can be scrolled into view by the user
-            let mut info = LayoutPrimitiveInfo::new((250, 350).to(300, 400));
-            info.tag = Some((0, 7));
-            builder.push_rect(&info, &space_and_clip2, ColorF::new(0.0, 1.0, 1.0, 1.0));
+            let mut info = CommonItemProperties::new(
+                (250, 350).to(300, 400),
+                space_and_clip2,
+            );
+            info.hit_info = Some((0, 7));
+            builder.push_rect(&info, ColorF::new(0.0, 1.0, 1.0, 1.0));
 
             builder.pop_stacking_context();
         }
 
         builder.pop_stacking_context();
     }
 
     fn on_event(&mut self, event: winit::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool {
--- a/gfx/wr/examples/texture_cache_stress.rs
+++ b/gfx/wr/examples/texture_cache_stress.rs
@@ -92,22 +92,22 @@ impl Example for App {
         api: &RenderApi,
         builder: &mut DisplayListBuilder,
         txn: &mut Transaction,
         _framebuffer_size: FramebufferIntSize,
         pipeline_id: PipelineId,
         _document_id: DocumentId,
     ) {
         let bounds = (0, 0).to(512, 512);
-        let info = LayoutPrimitiveInfo::new(bounds);
         let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
 
         builder.push_simple_stacking_context(
-            &info,
+            bounds.origin,
             space_and_clip.spatial_id,
+            true,
         );
 
         let x0 = 50.0;
         let y0 = 50.0;
         let image_size = LayoutSize::new(4.0, 4.0);
 
         if self.swap_keys.is_empty() {
             let key0 = api.generate_image_key();
@@ -131,63 +131,63 @@ impl Example for App {
 
             self.swap_keys.push(key0);
             self.swap_keys.push(key1);
         }
 
         for (i, key) in self.stress_keys.iter().enumerate() {
             let x = (i % 128) as f32;
             let y = (i / 128) as f32;
-            let info = LayoutPrimitiveInfo::with_clip_rect(
+            let info = CommonItemProperties::new(
                 LayoutRect::new(
                     LayoutPoint::new(x0 + image_size.width * x, y0 + image_size.height * y),
                     image_size,
                 ),
-                bounds,
+                space_and_clip,
             );
 
             builder.push_image(
                 &info,
-                &space_and_clip,
+                bounds,
                 image_size,
                 LayoutSize::zero(),
                 ImageRendering::Auto,
                 AlphaType::PremultipliedAlpha,
                 *key,
                 ColorF::WHITE,
             );
         }
 
         if let Some(image_key) = self.image_key {
             let image_size = LayoutSize::new(100.0, 100.0);
-            let info = LayoutPrimitiveInfo::with_clip_rect(
+            let info = CommonItemProperties::new(
                 LayoutRect::new(LayoutPoint::new(100.0, 100.0), image_size),
-                bounds,
+                space_and_clip,
             );
             builder.push_image(
                 &info,
-                &space_and_clip,
+                bounds,
                 image_size,
                 LayoutSize::zero(),
                 ImageRendering::Auto,
                 AlphaType::PremultipliedAlpha,
                 image_key,
                 ColorF::WHITE,
             );
         }
 
         let swap_key = self.swap_keys[self.swap_index];
         let image_size = LayoutSize::new(64.0, 64.0);
-        let info = LayoutPrimitiveInfo::with_clip_rect(
+        let info = CommonItemProperties::new(
             LayoutRect::new(LayoutPoint::new(100.0, 400.0), image_size),
-            bounds,
+            space_and_clip,
         );
         builder.push_image(
             &info,
-            &space_and_clip,
+            bounds,
             image_size,
             LayoutSize::zero(),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             swap_key,
             ColorF::WHITE,
         );
         self.swap_index = 1 - self.swap_index;
--- a/gfx/wr/examples/yuv.rs
+++ b/gfx/wr/examples/yuv.rs
@@ -89,22 +89,22 @@ impl Example for App {
         api: &RenderApi,
         builder: &mut DisplayListBuilder,
         txn: &mut Transaction,
         _framebuffer_size: FramebufferIntSize,
         pipeline_id: PipelineId,
         _document_id: DocumentId,
     ) {
         let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
-        let info = LayoutPrimitiveInfo::new(bounds);
         let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
 
         builder.push_simple_stacking_context(
-            &info,
+            bounds.origin,
             space_and_clip.spatial_id,
+            true,
         );
 
         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();
         txn.add_image(
             yuv_chanel1,
@@ -150,36 +150,36 @@ impl Example for App {
                 channel_index: 0,
                 image_type: ExternalImageType::TextureHandle(
                     TextureTarget::Default,
                 ),
             }),
             None,
         );
 
-        let info = LayoutPrimitiveInfo::with_clip_rect(
+        let info = CommonItemProperties::new(
             LayoutRect::new(LayoutPoint::new(100.0, 0.0), LayoutSize::new(100.0, 100.0)),
-            bounds,
+            space_and_clip,
         );
         builder.push_yuv_image(
             &info,
-            &space_and_clip,
+            bounds,
             YuvData::NV12(yuv_chanel1, yuv_chanel2),
             ColorDepth::Color8,
             YuvColorSpace::Rec601,
             ImageRendering::Auto,
         );
 
-        let info = LayoutPrimitiveInfo::with_clip_rect(
+        let info = CommonItemProperties::new(
             LayoutRect::new(LayoutPoint::new(300.0, 0.0), LayoutSize::new(100.0, 100.0)),
-            bounds,
+            space_and_clip,
         );
         builder.push_yuv_image(
             &info,
-            &space_and_clip,
+            bounds,
             YuvData::PlanarYCbCr(yuv_chanel1, yuv_chanel2_1, yuv_chanel3),
             ColorDepth::Color8,
             YuvColorSpace::Rec601,
             ImageRendering::Auto,
         );
 
         builder.pop_stacking_context();
     }
--- a/gfx/wr/webrender/src/border.rs
+++ b/gfx/wr/webrender/src/border.rs
@@ -1,23 +1,24 @@
 /* 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, BorderSide, BorderStyle, ColorF, ColorU};
-use api::{LayoutPrimitiveInfo, NormalBorder as ApiNormalBorder, RepeatMode};
+use api::{NormalBorder as ApiNormalBorder, RepeatMode};
 use api::units::*;
 use ellipse::Ellipse;
 use euclid::vec2;
 use display_list_flattener::DisplayListFlattener;
 use gpu_types::{BorderInstance, BorderSegment, BrushFlags};
 use prim_store::{BorderSegmentInfo, BrushSegment, NinePatchDescriptor};
 use prim_store::{EdgeAaSegmentMask, ScrollNodeAndClipChain};
 use prim_store::borders::{NormalBorderPrim, NormalBorderData};
 use util::{lerp, RectHelpers};
+use internal_types::LayoutPrimitiveInfo;
 
 // Using 2048 as the maximum radius in device space before which we
 // start stretching is up for debate.
 // the value must be chosen so that the corners will not use an
 // unreasonable amount of memory but should allow crisp corners in the
 // common cases.
 
 /// Maximum resolution in device pixels at which borders are rasterized.
--- a/gfx/wr/webrender/src/box_shadow.rs
+++ b/gfx/wr/webrender/src/box_shadow.rs
@@ -1,22 +1,23 @@
 /* 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, BoxShadowClipMode, ClipMode, ColorF, LayoutPrimitiveInfo, PrimitiveKeyKind};
+use api::{BorderRadius, BoxShadowClipMode, ClipMode, ColorF, PrimitiveKeyKind};
 use api::MAX_BLUR_RADIUS;
 use api::units::*;
 use clip::ClipItemKey;
 use display_list_flattener::DisplayListFlattener;
 use gpu_cache::GpuCacheHandle;
 use gpu_types::BoxShadowStretchMode;
 use prim_store::ScrollNodeAndClipChain;
 use render_task::RenderTaskCacheEntryHandle;
 use util::RectHelpers;
+use internal_types::LayoutPrimitiveInfo;
 
 #[derive(Debug, Clone, MallocSizeOf)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct BoxShadowClipSource {
     // Parameters that define the shadow and are constant.
     pub shadow_radius: BorderRadius,
     pub blur_radius: f32,
--- a/gfx/wr/webrender/src/display_list_flattener.rs
+++ b/gfx/wr/webrender/src/display_list_flattener.rs
@@ -1,30 +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 api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayListIter};
-use api::{ClipId, ColorF, ComplexClipRegion, RasterSpace};
-use api::{DisplayItemRef, ExtendMode, ExternalScrollId};
+use api::{ClipId, ColorF, CommonItemProperties, ComplexClipRegion, RasterSpace};
+use api::{DisplayItem, DisplayItemRef, ExtendMode, ExternalScrollId};
 use api::{FilterOp, FontInstanceKey, GlyphInstance, GlyphOptions, GradientStop};
 use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, ColorDepth};
-use api::{LayoutPrimitiveInfo, LineOrientation, LineStyle, NinePatchBorderSource, PipelineId};
+use api::{LineOrientation, LineStyle, NinePatchBorderSource, PipelineId};
 use api::{PropertyBinding, ReferenceFrame, ReferenceFrameKind, ScrollFrameDisplayItem, ScrollSensitivity};
-use api::{Shadow, SpaceAndClipInfo, SpatialId, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem};
+use api::{Shadow, SpaceAndClipInfo, SpatialId, StackingContext, StickyFrameDisplayItem};
 use api::{ClipMode, PrimitiveKeyKind, TransformStyle, YuvColorSpace, YuvData, TempFilterData};
 use api::units::*;
 use clip::{ClipChainId, ClipRegion, ClipItemKey, ClipStore};
 use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex};
 use frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig};
 use glyph_rasterizer::FontInstance;
 use hit_test::{HitTestingItem, HitTestingScene};
 use image::simplify_repeated_primitive;
 use intern::Interner;
-use internal_types::{FastHashMap, FastHashSet};
+use internal_types::{FastHashMap, FastHashSet, LayoutPrimitiveInfo};
 use picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PictureOptions};
 use picture::{BlitReason, PrimitiveList, TileCache};
 use prim_store::{PrimitiveInstance, PrimitiveSceneData};
 use prim_store::{PrimitiveInstanceKind, NinePatchDescriptor, PrimitiveStore};
 use prim_store::{ScrollNodeAndClipChain, PictureIndex};
 use prim_store::{InternablePrimitive, SegmentInstanceIndex};
 use prim_store::{register_prim_chase_id, get_line_decoration_sizes};
 use prim_store::borders::{ImageBorder, NormalBorderPrim};
@@ -46,16 +46,18 @@ use util::{MaxRect, VecHelper};
 use ::filterdata::{SFilterDataComponent, SFilterData, SFilterDataKey};
 
 #[derive(Debug, Copy, Clone)]
 struct ClipNode {
     id: ClipChainId,
     count: usize,
 }
 
+
+
 impl ClipNode {
     fn new(id: ClipChainId, count: usize) -> Self {
         ClipNode {
             id,
             count,
         }
     }
 }
@@ -646,22 +648,21 @@ impl<'a> DisplayListFlattener<'a> {
                     stats.num_bytes / stats.total_count.max(1));
             }
             println!("");
         }
     }
 
     fn flatten_sticky_frame(
         &mut self,
-        item: &DisplayItemRef,
         info: &StickyFrameDisplayItem,
         parent_node_index: SpatialNodeIndex,
     ) {
         let current_offset = self.current_offset(parent_node_index);
-        let frame_rect = item.rect().translate(&current_offset);
+        let frame_rect = info.bounds.translate(&current_offset);
         let sticky_frame_info = StickyFrameInfo::new(
             frame_rect,
             info.margins,
             info.vertical_offset_bounds,
             info.horizontal_offset_bounds,
             info.previously_applied_offset,
         );
 
@@ -678,29 +679,29 @@ impl<'a> DisplayListFlattener<'a> {
         item: &DisplayItemRef,
         info: &ScrollFrameDisplayItem,
         parent_node_index: SpatialNodeIndex,
         pipeline_id: PipelineId,
     ) {
         let complex_clips = self.get_complex_clips(pipeline_id, item.complex_clip().0);
         let current_offset = self.current_offset(parent_node_index);
         let clip_region = ClipRegion::create_for_clip_node(
-            *item.clip_rect(),
+            info.clip_rect,
             complex_clips,
             info.image_mask,
             &current_offset,
         );
         // Just use clip rectangle as the frame rect for this scroll frame.
         // This is useful when calculating scroll extents for the
         // SpatialNode::scroll(..) API as well as for properly setting sticky
         // positioning offsets.
         let frame_rect = clip_region.main;
-        let content_size = item.rect().size;
+        let content_size = info.content_rect.size;
 
-        self.add_clip_node(info.clip_id, item.space_and_clip_info(), clip_region);
+        self.add_clip_node(info.clip_id, &info.parent_space_and_clip, clip_region);
 
         self.add_scroll_frame(
             info.scroll_frame_id,
             parent_node_index,
             info.external_id,
             pipeline_id,
             &frame_rect,
             &content_size,
@@ -811,41 +812,40 @@ impl<'a> DisplayListFlattener<'a> {
         );
         self.rf_mapper.pop_offset();
 
         self.pop_stacking_context();
     }
 
     fn flatten_iframe(
         &mut self,
-        item: &DisplayItemRef,
         info: &IframeDisplayItem,
         spatial_node_index: SpatialNodeIndex,
     ) {
         let iframe_pipeline_id = info.pipeline_id;
         let pipeline = match self.scene.pipelines.get(&iframe_pipeline_id) {
             Some(pipeline) => pipeline,
             None => {
                 debug_assert!(info.ignore_missing_pipeline);
                 return
             },
         };
 
         let current_offset = self.current_offset(spatial_node_index);
         let clip_chain_index = self.add_clip_node(
             ClipId::root(iframe_pipeline_id),
-            item.space_and_clip_info(),
+            &info.space_and_clip,
             ClipRegion::create_for_clip_node_with_local_clip(
-                item.clip_rect(),
+                &info.clip_rect,
                 &current_offset,
             ),
         );
         self.pipeline_clip_chain_stack.push(clip_chain_index);
 
-        let bounds = item.rect();
+        let bounds = info.bounds;
         let origin = current_offset + bounds.origin.to_vector();
         let spatial_node_index = self.push_reference_frame(
             SpatialId::root_reference_frame(iframe_pipeline_id),
             Some(spatial_node_index),
             iframe_pipeline_id,
             TransformStyle::Flat,
             PropertyBinding::Value(LayoutTransform::identity()),
             ReferenceFrameKind::Transform,
@@ -871,209 +871,318 @@ impl<'a> DisplayListFlattener<'a> {
             pipeline.pipeline_id,
             true,
         );
         self.rf_mapper.pop_scope();
 
         self.pipeline_clip_chain_stack.pop();
     }
 
+    fn get_space(&mut self, spatial_id: &SpatialId) -> SpatialNodeIndex {
+        self.id_to_index_mapper.get_spatial_node_index(*spatial_id)
+    }
+
+    fn get_clip_and_scroll(
+        &mut self,
+        clip_id: &ClipId,
+        spatial_id: &SpatialId,
+        apply_pipeline_clip: bool
+    ) -> ScrollNodeAndClipChain {
+        ScrollNodeAndClipChain::new(
+            self.id_to_index_mapper.get_spatial_node_index(*spatial_id),
+            if !apply_pipeline_clip && clip_id.is_root() {
+                ClipChainId::NONE
+            } else if clip_id.is_valid() {
+                self.id_to_index_mapper.get_clip_chain_id(*clip_id)
+            } else {
+                ClipChainId::INVALID
+            },
+        )
+    }
+
+    fn process_common_properties(
+        &mut self,
+        common: &CommonItemProperties,
+        apply_pipeline_clip: bool
+    ) -> (LayoutPrimitiveInfo, ScrollNodeAndClipChain) {
+        self.process_common_properties_with_bounds(common, &common.clip_rect, apply_pipeline_clip)
+    }
+
+    fn process_common_properties_with_bounds(
+        &mut self,
+        common: &CommonItemProperties,
+        bounds: &LayoutRect,
+        apply_pipeline_clip: bool
+    ) -> (LayoutPrimitiveInfo, ScrollNodeAndClipChain) {
+        let clip_and_scroll = self.get_clip_and_scroll(
+            &common.clip_id,
+            &common.spatial_id,
+            apply_pipeline_clip
+        );
+
+        let current_offset = self.current_offset(clip_and_scroll.spatial_node_index);
+
+        let clip_rect = common.clip_rect.translate(&current_offset);
+        let rect = bounds.translate(&current_offset);
+        let layout = LayoutPrimitiveInfo {
+            rect,
+            clip_rect,
+            is_backface_visible: common.is_backface_visible,
+            hit_info: common.hit_info,
+        };
+
+        (layout, clip_and_scroll)
+    }
+
     fn flatten_item<'b>(
         &'b mut self,
         item: DisplayItemRef<'a, 'b>,
         pipeline_id: PipelineId,
         apply_pipeline_clip: bool,
     ) -> Option<BuiltDisplayListIter<'a>> {
-        let space_and_clip = item.space_and_clip_info();
-        let clip_and_scroll = ScrollNodeAndClipChain::new(
-            self.id_to_index_mapper.get_spatial_node_index(space_and_clip.spatial_id),
-            if !apply_pipeline_clip && space_and_clip.clip_id.is_root() {
-                ClipChainId::NONE
-            } else if space_and_clip.clip_id.is_valid() {
-                self.id_to_index_mapper.get_clip_chain_id(space_and_clip.clip_id)
-            } else {
-                ClipChainId::INVALID
-            },
-        );
-        let prim_info = item.get_layout_primitive_info(
-            &self.current_offset(clip_and_scroll.spatial_node_index),
-        );
+        match *item.item() {
+            DisplayItem::Image(ref info) => {
+                let (mut layout, clip_and_scroll) = self.process_common_properties_with_bounds(
+                    &info.common,
+                    &info.bounds,
+                    apply_pipeline_clip,
+                );
 
-        match *item.item() {
-            SpecificDisplayItem::Image(ref info) => {
                 self.add_image(
                     clip_and_scroll,
-                    &prim_info,
+                    &layout,
                     info.stretch_size,
                     info.tile_spacing,
                     None,
                     info.image_key,
                     info.image_rendering,
                     info.alpha_type,
                     info.color,
                 );
             }
-            SpecificDisplayItem::YuvImage(ref info) => {
+            DisplayItem::YuvImage(ref info) => {
+                let (mut layout, clip_and_scroll) = self.process_common_properties_with_bounds(
+                    &info.common,
+                    &info.bounds,
+                    apply_pipeline_clip,
+                );
+
                 self.add_yuv_image(
                     clip_and_scroll,
-                    &prim_info,
+                    &layout,
                     info.yuv_data,
                     info.color_depth,
                     info.color_space,
                     info.image_rendering,
                 );
             }
-            SpecificDisplayItem::Text(ref text_info) => {
+            DisplayItem::Text(ref info) => {
+                let (mut layout, clip_and_scroll) = self.process_common_properties_with_bounds(
+                    &info.common,
+                    &info.bounds,
+                    apply_pipeline_clip,
+                );
+
                 self.add_text(
                     clip_and_scroll,
-                    &prim_info,
-                    &text_info.font_key,
-                    &text_info.color,
+                    &layout,
+                    &info.font_key,
+                    &info.color,
                     item.glyphs(),
-                    text_info.glyph_options,
+                    info.glyph_options,
                     pipeline_id,
                 );
             }
-            SpecificDisplayItem::Rectangle(ref info) => {
+            DisplayItem::Rectangle(ref info) => {
+                let (layout, clip_and_scroll) = self.process_common_properties(
+                    &info.common,
+                    apply_pipeline_clip,
+                );
+
                 self.add_solid_rectangle(
                     clip_and_scroll,
-                    &prim_info,
+                    &layout,
                     info.color,
                 );
             }
-            SpecificDisplayItem::ClearRectangle => {
+            DisplayItem::HitTest(ref info) => {
+                let (layout, clip_and_scroll) = self.process_common_properties(
+                    &info.common,
+                    apply_pipeline_clip,
+                );
+
+                self.add_solid_rectangle(
+                    clip_and_scroll,
+                    &layout,
+                    ColorF::TRANSPARENT,
+                );
+            }
+            DisplayItem::ClearRectangle(ref info) => {
+                let (layout, clip_and_scroll) = self.process_common_properties(
+                    &info.common,
+                    apply_pipeline_clip,
+                );
+
                 self.add_clear_rectangle(
                     clip_and_scroll,
-                    &prim_info,
+                    &layout,
                 );
             }
-            SpecificDisplayItem::Line(ref info) => {
+            DisplayItem::Line(ref info) => {
+                let (mut layout, clip_and_scroll) = self.process_common_properties_with_bounds(
+                    &info.common,
+                    &info.area,
+                    apply_pipeline_clip,
+                );
+
                 self.add_line(
                     clip_and_scroll,
-                    &prim_info,
+                    &layout,
                     info.wavy_line_thickness,
                     info.orientation,
                     info.color,
                     info.style,
                 );
             }
-            SpecificDisplayItem::Gradient(ref info) => {
+            DisplayItem::Gradient(ref info) => {
+                let (mut layout, clip_and_scroll) = self.process_common_properties_with_bounds(
+                    &info.common,
+                    &info.bounds,
+                    apply_pipeline_clip,
+                );
+
                 if let Some(prim_key_kind) = self.create_linear_gradient_prim(
-                    &prim_info,
+                    &layout,
                     info.gradient.start_point,
                     info.gradient.end_point,
                     item.gradient_stops(),
                     info.gradient.extend_mode,
                     info.tile_size,
                     info.tile_spacing,
                     pipeline_id,
                     None,
                 ) {
                     self.add_nonshadowable_primitive(
                         clip_and_scroll,
-                        &prim_info,
+                        &layout,
                         Vec::new(),
                         prim_key_kind,
                     );
                 }
             }
-            SpecificDisplayItem::RadialGradient(ref info) => {
+            DisplayItem::RadialGradient(ref info) => {
+                let (mut layout, clip_and_scroll) = self.process_common_properties_with_bounds(
+                    &info.common,
+                    &info.bounds,
+                    apply_pipeline_clip,
+                );
+
                 let prim_key_kind = self.create_radial_gradient_prim(
-                    &prim_info,
+                    &layout,
                     info.gradient.center,
                     info.gradient.start_offset * info.gradient.radius.width,
                     info.gradient.end_offset * info.gradient.radius.width,
                     info.gradient.radius.width / info.gradient.radius.height,
                     item.gradient_stops(),
                     info.gradient.extend_mode,
                     info.tile_size,
                     info.tile_spacing,
                     pipeline_id,
                     None,
                 );
+
                 self.add_nonshadowable_primitive(
                     clip_and_scroll,
-                    &prim_info,
+                    &layout,
                     Vec::new(),
                     prim_key_kind,
                 );
             }
-            SpecificDisplayItem::BoxShadow(ref box_shadow_info) => {
-                let current_offset = self.current_offset(clip_and_scroll.spatial_node_index);
-                let bounds = box_shadow_info
-                    .box_bounds
-                    .translate(&current_offset);
-                let mut prim_info = prim_info.clone();
-                prim_info.rect = bounds;
+            DisplayItem::BoxShadow(ref info) => {
+                let (mut layout, clip_and_scroll) = self.process_common_properties_with_bounds(
+                    &info.common,
+                    &info.box_bounds,
+                    apply_pipeline_clip,
+                );
+
                 self.add_box_shadow(
                     clip_and_scroll,
-                    &prim_info,
-                    &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,
+                    &layout,
+                    &info.offset,
+                    info.color,
+                    info.blur_radius,
+                    info.spread_radius,
+                    info.border_radius,
+                    info.clip_mode,
                 );
             }
-            SpecificDisplayItem::Border(ref info) => {
+            DisplayItem::Border(ref info) => {
+                let (mut layout, clip_and_scroll) = self.process_common_properties_with_bounds(
+                    &info.common,
+                    &info.bounds,
+                    apply_pipeline_clip,
+                );
+
                 self.add_border(
                     clip_and_scroll,
-                    &prim_info,
+                    &layout,
                     info,
                     item.gradient_stops(),
                     pipeline_id,
                 );
             }
-            SpecificDisplayItem::PushStackingContext(ref info) => {
+            DisplayItem::PushStackingContext(ref info) => {
+                let space = self.get_space(&info.spatial_id);
                 let mut subtraversal = item.sub_iter();
                 self.flatten_stacking_context(
                     &mut subtraversal,
                     pipeline_id,
                     &info.stacking_context,
-                    clip_and_scroll.spatial_node_index,
-                    item.rect().origin,
+                    space,
+                    info.origin,
                     item.filters(),
                     item.filter_datas(),
-                    prim_info.is_backface_visible,
+                    info.is_backface_visible,
                     apply_pipeline_clip,
                 );
                 return Some(subtraversal);
             }
-            SpecificDisplayItem::PushReferenceFrame(ref info) => {
+            DisplayItem::PushReferenceFrame(ref info) => {
+                let parent_space = self.get_space(&info.parent_spatial_id);
                 let mut subtraversal = item.sub_iter();
                 self.flatten_reference_frame(
                     &mut subtraversal,
                     pipeline_id,
-                    clip_and_scroll.spatial_node_index,
-                    item.rect().origin,
+                    parent_space,
+                    info.origin,
                     &info.reference_frame,
                     apply_pipeline_clip,
                 );
                 return Some(subtraversal);
             }
-            SpecificDisplayItem::Iframe(ref info) => {
+            DisplayItem::Iframe(ref info) => {
+                let space = self.get_space(&info.space_and_clip.spatial_id);
                 self.flatten_iframe(
-                    &item,
                     info,
-                    clip_and_scroll.spatial_node_index,
+                    space,
                 );
             }
-            SpecificDisplayItem::Clip(ref info) => {
-                let current_offset = self.current_offset(clip_and_scroll.spatial_node_index);
+            DisplayItem::Clip(ref info) => {
+                let parent_space = self.get_space(&info.parent_space_and_clip.spatial_id);
+                let current_offset = self.current_offset(parent_space);
                 let complex_clips = self.get_complex_clips(pipeline_id, item.complex_clip().0);
                 let clip_region = ClipRegion::create_for_clip_node(
-                    *item.clip_rect(),
+                    info.clip_rect,
                     complex_clips,
                     info.image_mask,
                     &current_offset,
                 );
-                self.add_clip_node(info.id, space_and_clip, clip_region);
+                self.add_clip_node(info.id, &info.parent_space_and_clip, clip_region);
             }
-            SpecificDisplayItem::ClipChain(ref info) => {
+            DisplayItem::ClipChain(ref info) => {
                 // For a user defined clip-chain the parent (if specified) must
                 // refer to another user defined clip-chain. If none is specified,
                 // the parent is the root clip-chain for the given pipeline. This
                 // is used to provide a root clip chain for iframes.
                 let parent_clip_chain_id = match info.parent {
                     Some(id) => {
                         self.id_to_index_mapper.get_clip_chain_id(ClipId::ClipChain(id))
                     }
@@ -1132,45 +1241,52 @@ impl<'a> DisplayListFlattener<'a> {
                             );
                     }
                 }
 
                 // Map the last entry in the clip chain to the supplied ClipId. This makes
                 // this ClipId available as a source to other user defined clip chains.
                 self.id_to_index_mapper.add_clip_chain(ClipId::ClipChain(info.id), clip_chain_id, 0);
             },
-            SpecificDisplayItem::ScrollFrame(ref info) => {
+            DisplayItem::ScrollFrame(ref info) => {
+                let parent_space = self.get_space(&info.parent_space_and_clip.spatial_id);
                 self.flatten_scroll_frame(
                     &item,
                     info,
-                    clip_and_scroll.spatial_node_index,
+                    parent_space,
                     pipeline_id,
                 );
             }
-            SpecificDisplayItem::StickyFrame(ref info) => {
+            DisplayItem::StickyFrame(ref info) => {
+                let parent_space = self.get_space(&info.parent_spatial_id);
                 self.flatten_sticky_frame(
-                    &item,
                     info,
-                    clip_and_scroll.spatial_node_index,
+                    parent_space,
                 );
             }
 
             // Do nothing; these are dummy items for the display list parser
-            SpecificDisplayItem::SetGradientStops => {}
-            SpecificDisplayItem::SetFilterOps => {}
-            SpecificDisplayItem::SetFilterData => {}
+            DisplayItem::SetGradientStops => {}
+            DisplayItem::SetFilterOps => {}
+            DisplayItem::SetFilterData => {}
 
-            SpecificDisplayItem::PopReferenceFrame |
-            SpecificDisplayItem::PopStackingContext => {
+            DisplayItem::PopReferenceFrame |
+            DisplayItem::PopStackingContext => {
                 unreachable!("Should have returned in parent method.")
             }
-            SpecificDisplayItem::PushShadow(shadow) => {
-                self.push_shadow(shadow, clip_and_scroll);
+            DisplayItem::PushShadow(info) => {
+                let clip_and_scroll = self.get_clip_and_scroll(
+                    &info.space_and_clip.clip_id,
+                    &info.space_and_clip.spatial_id,
+                    apply_pipeline_clip
+                );
+
+                self.push_shadow(info.shadow, clip_and_scroll);
             }
-            SpecificDisplayItem::PopAllShadows => {
+            DisplayItem::PopAllShadows => {
                 self.pop_all_shadows();
             }
         }
 
         None
     }
 
     // Given a list of clip sources, a positioning node and
@@ -1252,17 +1368,17 @@ impl<'a> DisplayListFlattener<'a> {
         )
     }
 
     pub fn add_primitive_to_hit_testing_list(
         &mut self,
         info: &LayoutPrimitiveInfo,
         clip_and_scroll: ScrollNodeAndClipChain
     ) {
-        let tag = match info.tag {
+        let tag = match info.hit_info {
             Some(tag) => tag,
             None => return,
         };
 
         // We want to get a range of clip chain roots that apply to this
         // hit testing primitive.
 
         // Get the start index for the clip chain root range for this primitive.
--- a/gfx/wr/webrender/src/hit_test.rs
+++ b/gfx/wr/webrender/src/hit_test.rs
@@ -1,19 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{BorderRadius, ClipMode, HitTestFlags, HitTestItem, HitTestResult, ItemTag};
-use api::{LayoutPrimitiveInfo, PipelineId};
+use api::PipelineId;
 use api::units::*;
 use clip::{ClipChainId, ClipDataStore, ClipNode, ClipItem, ClipStore};
 use clip::{rounded_rectangle_contains_point};
 use clip_scroll_tree::{SpatialNodeIndex, ClipScrollTree};
-use internal_types::FastHashMap;
+use internal_types::{FastHashMap, LayoutPrimitiveInfo};
 use std::{ops, u32};
 use std::sync::Arc;
 use util::{LayoutToWorldFastTransform, WorldToLayoutFastTransform};
 
 /// A copy of important clip scroll node data to use during hit testing. This a copy of
 /// data from the ClipScrollTree that will persist as a new frame is under construction,
 /// allowing hit tests consistent with the currently rendered frame.
 #[derive(MallocSizeOf)]
--- a/gfx/wr/webrender/src/internal_types.rs
+++ b/gfx/wr/webrender/src/internal_types.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::{DebugCommand, DocumentId, ExternalImageData, ExternalImageId};
-use api::{ImageFormat, NotificationRequest};
+use api::{ImageFormat, ItemTag, NotificationRequest};
 use api::units::*;
 use device::TextureFilter;
 use renderer::PipelineInfo;
 use gpu_cache::GpuCacheUpdateList;
 use fxhash::FxHasher;
 use plane_split::BspSplitter;
 use profiler::BackendProfileCounters;
 use std::{usize, i32};
@@ -318,8 +318,30 @@ pub struct ResourceCacheError {
 
 impl ResourceCacheError {
     pub fn new(description: String) -> ResourceCacheError {
         ResourceCacheError {
             description,
         }
     }
 }
+
+/// Primitive metadata we pass around in a bunch of places
+#[derive(Copy, Clone, Debug)]
+pub struct LayoutPrimitiveInfo {
+    /// NOTE: this is *ideally* redundant with the clip_rect
+    /// but that's an ongoing project, so for now it exists and is used :(
+    pub rect: LayoutRect,
+    pub clip_rect: LayoutRect,
+    pub is_backface_visible: bool,
+    pub hit_info: Option<ItemTag>,
+}
+
+impl LayoutPrimitiveInfo {
+    pub fn with_clip_rect(rect: LayoutRect, clip_rect: LayoutRect) -> Self {
+        Self {
+            rect,
+            clip_rect,
+            is_backface_visible: true,
+            hit_info: None,
+        }
+    }
+}
--- a/gfx/wr/webrender/src/prim_store/borders.rs
+++ b/gfx/wr/webrender/src/prim_store/borders.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::{LayoutPrimitiveInfo, NormalBorder, PremultipliedColorF, Shadow};
+use api::{NormalBorder, PremultipliedColorF, Shadow};
 use api::units::*;
 use border::create_border_segments;
 use border::NormalBorderAu;
 use display_list_flattener::{CreateShadow, IsVisible};
 use frame_builder::{FrameBuildingState};
 use gpu_cache::GpuDataRequest;
 use intern;
+use internal_types::LayoutPrimitiveInfo;
 use prim_store::{
     BorderSegmentInfo, BrushSegment, NinePatchDescriptor, PrimKey,
     PrimKeyCommonData, PrimTemplate, PrimTemplateCommonData,
     PrimitiveInstanceKind, PrimitiveOpacity, PrimitiveSceneData,
     PrimitiveStore, InternablePrimitive,
 };
 use resource_cache::ImageRequest;
 use storage;
@@ -353,12 +354,12 @@ fn test_struct_sizes() {
     // talos stress tests. If you get a failure here on CI, there's two possibilities:
     // (a) You made a structure smaller than it currently is. Great work! Update the
     //     test expectations and move on.
     // (b) You made a structure larger. This is not necessarily a problem, but should only
     //     be done with care, and after checking if talos performance regresses badly.
     assert_eq!(mem::size_of::<NormalBorderPrim>(), 84, "NormalBorderPrim size changed");
     assert_eq!(mem::size_of::<NormalBorderTemplate>(), 208, "NormalBorderTemplate size changed");
     assert_eq!(mem::size_of::<NormalBorderKey>(), 96, "NormalBorderKey size changed");
-    assert_eq!(mem::size_of::<ImageBorder>(), 92, "ImageBorder size changed");
+    assert_eq!(mem::size_of::<ImageBorder>(), 84, "ImageBorder size changed");
     assert_eq!(mem::size_of::<ImageBorderTemplate>(), 72, "ImageBorderTemplate size changed");
-    assert_eq!(mem::size_of::<ImageBorderKey>(), 104, "ImageBorderKey size changed");
+    assert_eq!(mem::size_of::<ImageBorderKey>(), 96, "ImageBorderKey size changed");
 }
--- a/gfx/wr/webrender/src/prim_store/gradient.rs
+++ b/gfx/wr/webrender/src/prim_store/gradient.rs
@@ -1,22 +1,23 @@
 /* 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, ColorU, ExtendMode, GradientStop,
-    LayoutPrimitiveInfo, PremultipliedColorF, LineOrientation,
+    PremultipliedColorF, LineOrientation,
 };
 use api::units::{LayoutPoint, LayoutSize, LayoutVector2D};
 use display_list_flattener::IsVisible;
 use euclid::approxeq::ApproxEq;
 use frame_builder::FrameBuildingState;
 use gpu_cache::{GpuCacheHandle, GpuDataRequest};
 use intern::{Internable, InternDebug, Handle as InternHandle};
+use internal_types::LayoutPrimitiveInfo;
 use prim_store::{BrushSegment, GradientTileRange, VectorKey};
 use prim_store::{PrimitiveInstanceKind, PrimitiveOpacity, PrimitiveSceneData};
 use prim_store::{PrimKeyCommonData, PrimTemplateCommonData, PrimitiveStore};
 use prim_store::{NinePatchDescriptor, PointKey, SizeKey, InternablePrimitive};
 use render_task::RenderTaskCacheEntryHandle;
 use std::{hash, ops::{Deref, DerefMut}, mem};
 use util::pack_as_float;
 
--- a/gfx/wr/webrender/src/prim_store/image.rs
+++ b/gfx/wr/webrender/src/prim_store/image.rs
@@ -1,22 +1,23 @@
 /* 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::{
     AlphaType, ColorDepth, ColorF, ColorU,
-    ImageKey as ApiImageKey, ImageRendering, LayoutPrimitiveInfo,
+    ImageKey as ApiImageKey, ImageRendering,
     PremultipliedColorF, Shadow, YuvColorSpace, YuvFormat,
 };
 use api::units::*;
 use display_list_flattener::{CreateShadow, IsVisible};
 use frame_builder::FrameBuildingState;
 use gpu_cache::{GpuDataRequest};
 use intern::{Internable, InternDebug, Handle as InternHandle};
+use internal_types::LayoutPrimitiveInfo;
 use prim_store::{
     EdgeAaSegmentMask, OpacityBindingIndex, PrimitiveInstanceKind,
     PrimitiveOpacity, PrimitiveSceneData, PrimKey, PrimKeyCommonData,
     PrimTemplate, PrimTemplateCommonData, PrimitiveStore, SegmentInstanceIndex,
     SizeKey, InternablePrimitive,
 };
 use render_task::{
     BlitSource, RenderTask, RenderTaskCacheEntryHandle, RenderTaskCacheKey,
@@ -528,15 +529,15 @@ impl IsVisible for YuvImage {
 fn test_struct_sizes() {
     use std::mem;
     // The sizes of these structures are critical for performance on a number of
     // talos stress tests. If you get a failure here on CI, there's two possibilities:
     // (a) You made a structure smaller than it currently is. Great work! Update the
     //     test expectations and move on.
     // (b) You made a structure larger. This is not necessarily a problem, but should only
     //     be done with care, and after checking if talos performance regresses badly.
-    assert_eq!(mem::size_of::<Image>(), 56, "Image size changed");
-    assert_eq!(mem::size_of::<ImageTemplate>(), 108, "ImageTemplate size changed");
-    assert_eq!(mem::size_of::<ImageKey>(), 68, "ImageKey size changed");
-    assert_eq!(mem::size_of::<YuvImage>(), 36, "YuvImage size changed");
-    assert_eq!(mem::size_of::<YuvImageTemplate>(), 56, "YuvImageTemplate size changed");
-    assert_eq!(mem::size_of::<YuvImageKey>(), 48, "YuvImageKey size changed");
+    assert_eq!(mem::size_of::<Image>(), 52, "Image size changed");
+    assert_eq!(mem::size_of::<ImageTemplate>(), 104, "ImageTemplate size changed");
+    assert_eq!(mem::size_of::<ImageKey>(), 64, "ImageKey size changed");
+    assert_eq!(mem::size_of::<YuvImage>(), 28, "YuvImage size changed");
+    assert_eq!(mem::size_of::<YuvImageTemplate>(), 48, "YuvImageTemplate size changed");
+    assert_eq!(mem::size_of::<YuvImageKey>(), 40, "YuvImageKey size changed");
 }
--- a/gfx/wr/webrender/src/prim_store/line_dec.rs
+++ b/gfx/wr/webrender/src/prim_store/line_dec.rs
@@ -1,21 +1,22 @@
 /* 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, ColorU, LayoutPrimitiveInfo,
+    ColorF, ColorU,
     LineOrientation, LineStyle, PremultipliedColorF, Shadow,
 };
 use api::units::{Au, LayoutSizeAu, LayoutVector2D};
 use display_list_flattener::{CreateShadow, IsVisible};
 use frame_builder::{FrameBuildingState};
 use gpu_cache::GpuDataRequest;
 use intern;
+use internal_types::LayoutPrimitiveInfo;
 use prim_store::{
     PrimKey, PrimKeyCommonData, PrimTemplate, PrimTemplateCommonData,
     InternablePrimitive, PrimitiveSceneData, PrimitiveStore,
 };
 use prim_store::PrimitiveInstanceKind;
 
 
 #[derive(Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)]
--- a/gfx/wr/webrender/src/prim_store/mod.rs
+++ b/gfx/wr/webrender/src/prim_store/mod.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 api::{BorderRadius, ClipMode, ColorF};
 use api::{FilterOp, ImageRendering, RepeatMode};
 use api::{PremultipliedColorF, PropertyBinding, Shadow, GradientStop};
 use api::{BoxShadowClipMode, LineStyle, LineOrientation};
-use api::{LayoutPrimitiveInfo, PrimitiveKeyKind};
+use api::PrimitiveKeyKind;
 use api::units::*;
 use border::{get_max_scale_for_border, build_border_instances};
 use border::BorderSegmentCacheKey;
 use clip::{ClipStore};
 use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex, VisibleFace};
 use clip::{ClipDataStore, ClipNodeFlags, ClipChainId, ClipChainInstance, ClipItem};
 use debug_colors;
 use debug_render::DebugItem;
@@ -47,16 +47,17 @@ use segment::SegmentBuilder;
 use std::{cmp, fmt, hash, ops, u32, usize, mem};
 #[cfg(debug_assertions)]
 use std::sync::atomic::{AtomicUsize, Ordering};
 use storage;
 use texture_cache::TEXTURE_REGION_DIMENSIONS;
 use util::{ScaleOffset, MatrixHelpers, MaxRect, Recycler, TransformedRectKind};
 use util::{pack_as_float, project_rect, raster_rect_to_device_pixels};
 use util::{scale_factors, clamp_to_scale_factor};
+use internal_types::LayoutPrimitiveInfo;
 use smallvec::SmallVec;
 
 pub mod borders;
 pub mod gradient;
 pub mod image;
 pub mod line_dec;
 pub mod picture;
 pub mod text_run;
--- a/gfx/wr/webrender/src/prim_store/picture.rs
+++ b/gfx/wr/webrender/src/prim_store/picture.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::{
-    ColorU, FilterOp, LayoutPrimitiveInfo, MixBlendMode,
+    ColorU, FilterOp, MixBlendMode,
     PropertyBinding, PropertyBindingId,
 };
 use api::units::{Au, LayoutSize, LayoutVector2D};
 use intern::ItemUid;
 use display_list_flattener::IsVisible;
 use intern::{Internable, InternDebug, Handle as InternHandle};
+use internal_types::LayoutPrimitiveInfo;
 use picture::PictureCompositeMode;
 use prim_store::{
     PrimKey, PrimKeyCommonData, PrimTemplate, PrimTemplateCommonData,
     PrimitiveInstanceKind, PrimitiveSceneData, PrimitiveStore, VectorKey,
     InternablePrimitive,
 };
 
 /// Represents a hashable description of how a picture primitive
--- a/gfx/wr/webrender/src/prim_store/text_run.rs
+++ b/gfx/wr/webrender/src/prim_store/text_run.rs
@@ -1,19 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use api::{ColorF, GlyphInstance, LayoutPrimitiveInfo, RasterSpace, Shadow};
+use api::{ColorF, GlyphInstance, RasterSpace, Shadow};
 use api::units::{DevicePixelScale, LayoutToWorldTransform, LayoutVector2D};
 use display_list_flattener::{CreateShadow, IsVisible};
 use frame_builder::{FrameBuildingState, PictureContext};
 use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT};
 use gpu_cache::GpuCache;
 use intern;
+use internal_types::LayoutPrimitiveInfo;
 use prim_store::{PrimitiveOpacity, PrimitiveSceneData,  PrimitiveScratchBuffer};
 use prim_store::{PrimitiveStore, PrimKeyCommonData, PrimTemplateCommonData};
 use render_task::{RenderTaskTree};
 use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
 use resource_cache::{ResourceCache};
 use util::{MatrixHelpers};
 use prim_store::{InternablePrimitive, PrimitiveInstanceKind};
 use std::ops;
--- a/gfx/wr/webrender/src/render_backend.rs
+++ b/gfx/wr/webrender/src/render_backend.rs
@@ -5,17 +5,17 @@
 //! The high-level module responsible for managing the pipeline and preparing
 //! commands to be issued by the `Renderer`.
 //!
 //! See the comment at the top of the `renderer` module for a description of
 //! how these two pieces interact.
 
 use api::{ApiMsg, BuiltDisplayList, ClearCache, DebugCommand, DebugFlags};
 #[cfg(feature = "debugger")]
-use api::{BuiltDisplayListIter, SpecificDisplayItem};
+use api::{BuiltDisplayListIter, DisplayItem};
 use api::{DocumentId, DocumentLayer, ExternalScrollId, FrameMsg, HitTestFlags, HitTestResult};
 use api::{IdNamespace, MemoryReport, PipelineId, RenderNotifier, SceneMsg, ScrollClamping};
 use api::{ScrollLocation, ScrollNodeState, TransactionMsg, ResourceUpdate, BlobImageKey};
 use api::{NotificationRequest, Checkpoint};
 use api::{ClipIntern, FilterDataIntern, PrimitiveKeyKind};
 use api::units::*;
 use api::channel::{MsgReceiver, MsgSender, Payload};
 #[cfg(feature = "capture")]
@@ -1520,25 +1520,25 @@ impl RenderBackend {
         loop {
             let subtraversal = {
                 let item = match traversal.next() {
                     Some(item) => item,
                     None => break,
                 };
 
                 match *item.item() {
-                    display_item @ SpecificDisplayItem::PushStackingContext(..) => {
+                    display_item @ DisplayItem::PushStackingContext(..) => {
                         let mut subtraversal = item.sub_iter();
                         let mut child_node =
                             debug_server::TreeNode::new(&display_item.debug_name().to_string());
                         self.traverse_items(&mut subtraversal, &mut child_node);
                         node.add_child(child_node);
                         Some(subtraversal)
                     }
-                    SpecificDisplayItem::PopStackingContext => {
+                    DisplayItem::PopStackingContext => {
                         return;
                     }
                     display_item => {
                         node.add_item(&display_item.debug_name().to_string());
                         None
                     }
                 }
             };
@@ -1769,17 +1769,17 @@ impl RenderBackend {
             let interners_name = format!("interners-{}-{}", id.namespace_id.0, id.id);
             let interners = CaptureConfig::deserialize::<Interners, _>(root, &interners_name)
                 .expect(&format!("Unable to open {}.ron", interners_name));
 
             let data_stores_name = format!("data-stores-{}-{}", id.namespace_id.0, id.id);
             let data_stores = CaptureConfig::deserialize::<DataStores, _>(root, &data_stores_name)
                 .expect(&format!("Unable to open {}.ron", data_stores_name));
 
-            let mut doc = Document {
+            let doc = Document {
                 id: id,
                 scene: scene.clone(),
                 removed_pipelines: Vec::new(),
                 view: view.clone(),
                 clip_scroll_tree: ClipScrollTree::new(),
                 stamp: FrameStamp::first(id),
                 frame_builder: Some(FrameBuilder::empty()),
                 output_pipelines: FastHashSet::default(),
--- a/gfx/wr/webrender_api/src/display_item.rs
+++ b/gfx/wr/webrender_api/src/display_item.rs
@@ -1,87 +1,75 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use euclid::{SideOffsets2D, TypedRect};
+use euclid::SideOffsets2D;
 use std::ops::Not;
 // local imports
 use font;
 use api::{PipelineId, PropertyBinding};
 use color::ColorF;
 use image::{ColorDepth, ImageKey};
 use units::*;
 
 // Maximum blur radius.
 // Taken from nsCSSRendering.cpp in Gecko.
 pub const MAX_BLUR_RADIUS: f32 = 300.;
 
-// 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.
+// ******************************************************************
+// * 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. DebugDisplayItem should  *
+// * make them explicit.                                            *
+// ******************************************************************
 
 /// A tag that can be used to identify items during hit testing. If the tag
 /// is missing then the item doesn't take part in hit testing at all. This
 /// is composed of two numbers. In Servo, the first is an identifier while the
 /// second is used to select the cursor that should be used during mouse
 /// movement. In Gecko, the first is a scrollframe identifier, while the second
 /// is used to store various flags that APZ needs to properly process input
 /// events.
 pub type ItemTag = (u64, u16);
 
-/// The DI is generic over the specifics, while allows to use
-/// the "complete" version of it for convenient serialization.
+/// A grouping of fields a lot of display items need, just to avoid
+/// repeating these over and over in this file.
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct GenericDisplayItem<T> {
-    pub item: T,
-    pub layout: LayoutPrimitiveInfo,
-    pub space_and_clip: SpaceAndClipInfo,
-}
-
-pub type DisplayItem = GenericDisplayItem<SpecificDisplayItem>;
-
-/// A modified version of DI where every field is borrowed instead of owned.
-/// It allows us to reduce copies during serialization.
-#[derive(Serialize)]
-pub struct SerializedDisplayItem<'a> {
-    pub item: &'a SpecificDisplayItem,
-    pub layout: &'a LayoutPrimitiveInfo,
-    pub space_and_clip: &'a SpaceAndClipInfo,
+pub struct CommonItemProperties {
+    /// Bounds of the display item to clip to. Many items are logically
+    /// infinite, and rely on this clip_rect to define their bounds
+    /// (solid colors, background-images, gradients, etc).
+    pub clip_rect: LayoutRect,
+    /// Additional clips
+    pub clip_id: ClipId,
+    /// The coordinate-space the item is in (yes, it can be really granular)
+    pub spatial_id: SpatialId,
+    /// Opaque bits for our clients to use for hit-testing. This is the most
+    /// dubious "common" field, but because it's an Option, it usually only
+    /// wastes a single byte (for None).
+    pub hit_info: Option<ItemTag>,
+    /// The CSS backface-visibility property (yes, it can be really granular)
+    pub is_backface_visible: bool,
 }
 
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct PrimitiveInfo<T> {
-    pub rect: TypedRect<f32, T>,
-    pub clip_rect: TypedRect<f32, T>,
-    pub is_backface_visible: bool,
-    pub tag: Option<ItemTag>,
-}
-
-impl LayoutPrimitiveInfo {
-    pub fn new(rect: TypedRect<f32, LayoutPixel>) -> Self {
-        Self::with_clip_rect(rect, rect)
-    }
-
-    pub fn with_clip_rect(
-        rect: TypedRect<f32, LayoutPixel>,
-        clip_rect: TypedRect<f32, LayoutPixel>,
-    ) -> Self {
-        PrimitiveInfo {
-            rect,
+impl CommonItemProperties {
+    /// Convenience for tests.
+    pub fn new(clip_rect: LayoutRect, space_and_clip: SpaceAndClipInfo) -> Self {
+        Self {
             clip_rect,
+            spatial_id: space_and_clip.spatial_id,
+            clip_id: space_and_clip.clip_id,
+            hit_info: None,
             is_backface_visible: true,
-            tag: None,
         }
     }
 }
 
-pub type LayoutPrimitiveInfo = PrimitiveInfo<LayoutPixel>;
-
 /// Per-primitive information about the nodes in the clip tree and
 /// the spatial tree that the primitive belongs to.
 ///
 /// Note: this is a separate struct from `PrimitiveInfo` because
 /// it needs indirectional mapping during the DL flattening phase,
 /// turning into `ScrollNodeAndClipChain`.
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct SpaceAndClipInfo {
@@ -97,81 +85,98 @@ impl SpaceAndClipInfo {
             spatial_id: SpatialId::root_scroll_node(pipeline_id),
             clip_id: ClipId::root(pipeline_id),
         }
     }
 }
 
 #[repr(u8)]
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub enum SpecificDisplayItem {
-    Clip(ClipDisplayItem),
-    ScrollFrame(ScrollFrameDisplayItem),
-    StickyFrame(StickyFrameDisplayItem),
+pub enum DisplayItem {
+    // These are the "real content" display items
     Rectangle(RectangleDisplayItem),
-    ClearRectangle,
-    Line(LineDisplayItem),
+    ClearRectangle(ClearRectangleDisplayItem),
+    HitTest(HitTestDisplayItem),
     Text(TextDisplayItem),
-    Image(ImageDisplayItem),
-    YuvImage(YuvImageDisplayItem),
+    Line(LineDisplayItem),
     Border(BorderDisplayItem),
     BoxShadow(BoxShadowDisplayItem),
+    PushShadow(PushShadowDisplayItem),
     Gradient(GradientDisplayItem),
     RadialGradient(RadialGradientDisplayItem),
+    Image(ImageDisplayItem),
+    YuvImage(YuvImageDisplayItem),
+
+    // Clips
+    Clip(ClipDisplayItem),
     ClipChain(ClipChainItem),
+
+    // Spaces and Frames that content can be scoped under.
+    ScrollFrame(ScrollFrameDisplayItem),
+    StickyFrame(StickyFrameDisplayItem),
     Iframe(IframeDisplayItem),
     PushReferenceFrame(ReferenceFrameDisplayListItem),
-    PopReferenceFrame,
     PushStackingContext(PushStackingContextDisplayItem),
-    PopStackingContext,
+
+    // These marker items indicate an array of data follows, to be used for the
+    // next non-marker item.
     SetGradientStops,
-    PushShadow(Shadow),
-    PopAllShadows,
     SetFilterOps,
     SetFilterData,
+
+    // These marker items terminate a scope introduced by a previous item.
+    PopReferenceFrame,
+    PopStackingContext,
+    PopAllShadows,
 }
 
-/// This is a "complete" version of the DI specifics,
-/// containing the auxiliary data within the corresponding
-/// enumeration variants, to be used for debug serialization.
+/// This is a "complete" version of the DisplayItem, with all implicit trailing
+/// arrays included, for debug serialization (captures).
 #[cfg(any(feature = "serialize", feature = "deserialize"))]
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
-pub enum CompletelySpecificDisplayItem {
-    Clip(ClipDisplayItem, Vec<ComplexClipRegion>),
-    ClipChain(ClipChainItem, Vec<ClipId>),
-    ScrollFrame(ScrollFrameDisplayItem, Vec<ComplexClipRegion>),
-    StickyFrame(StickyFrameDisplayItem),
+pub enum DebugDisplayItem {
     Rectangle(RectangleDisplayItem),
-    ClearRectangle,
-    Line(LineDisplayItem),
+    ClearRectangle(ClearRectangleDisplayItem),
+    HitTest(HitTestDisplayItem),
     Text(TextDisplayItem, Vec<font::GlyphInstance>),
-    Image(ImageDisplayItem),
-    YuvImage(YuvImageDisplayItem),
+    Line(LineDisplayItem),
     Border(BorderDisplayItem),
     BoxShadow(BoxShadowDisplayItem),
+    PushShadow(PushShadowDisplayItem),
     Gradient(GradientDisplayItem),
     RadialGradient(RadialGradientDisplayItem),
+    Image(ImageDisplayItem),
+    YuvImage(YuvImageDisplayItem),
+
+    Clip(ClipDisplayItem, Vec<ComplexClipRegion>),
+    ClipChain(ClipChainItem, Vec<ClipId>),
+
+    ScrollFrame(ScrollFrameDisplayItem, Vec<ComplexClipRegion>),
+    StickyFrame(StickyFrameDisplayItem),
     Iframe(IframeDisplayItem),
     PushReferenceFrame(ReferenceFrameDisplayListItem),
-    PopReferenceFrame,
     PushStackingContext(PushStackingContextDisplayItem),
-    PopStackingContext,
+
     SetGradientStops(Vec<GradientStop>),
-    PushShadow(Shadow),
-    PopAllShadows,
     SetFilterOps(Vec<FilterOp>),
     SetFilterData(FilterData),
+
+    PopReferenceFrame,
+    PopStackingContext,
+    PopAllShadows,
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct ClipDisplayItem {
     pub id: ClipId,
+    pub parent_space_and_clip: SpaceAndClipInfo,
+    pub clip_rect: LayoutRect,
     pub image_mask: Option<ImageMask>,
-}
+} // IMPLICIT: complex_clips: Vec<ComplexClipRegion>
 
 /// The minimum and maximum allowable offset for a sticky frame in a single dimension.
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct StickyOffsetBounds {
     /// The minimum offset for this frame, typically a negative value, which specifies how
     /// far in the negative direction the sticky frame can offset its contents in this
     /// dimension.
@@ -187,16 +192,18 @@ impl StickyOffsetBounds {
     pub fn new(min: f32, max: f32) -> StickyOffsetBounds {
         StickyOffsetBounds { min, max }
     }
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct StickyFrameDisplayItem {
     pub id: SpatialId,
+    pub parent_spatial_id: SpatialId,
+    pub bounds: LayoutRect,
 
     /// The margins that should be maintained between the edge of the parent viewport and this
     /// sticky frame. A margin of None indicates that the sticky frame should not stick at all
     /// to that particular edge of the viewport.
     pub margins: SideOffsets2D<Option<f32>>,
 
     /// The minimum and maximum vertical offsets for this sticky frame. Ignoring these constraints,
     /// the sticky frame will continue to stick to the edge of the viewport as its original
@@ -221,37 +228,74 @@ pub struct StickyFrameDisplayItem {
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub enum ScrollSensitivity {
     ScriptAndInputEvents,
     Script,
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct ScrollFrameDisplayItem {
+    /// The id of the clip this scroll frame creates
     pub clip_id: ClipId,
+    /// The id of the space this scroll frame creates
     pub scroll_frame_id: SpatialId,
+    /// The size of the contents this contains (so the backend knows how far it can scroll).
+    // FIXME: this can *probably* just be a size? Origin seems to just get thrown out.
+    pub content_rect: LayoutRect,
+    pub clip_rect: LayoutRect,
+    pub parent_space_and_clip: SpaceAndClipInfo,
     pub external_id: Option<ExternalScrollId>,
     pub image_mask: Option<ImageMask>,
     pub scroll_sensitivity: ScrollSensitivity,
     /// The amount this scrollframe has already been scrolled by, in the caller.
     /// This means that all the display items that are inside the scrollframe
     /// will have their coordinates shifted by this amount, and this offset
     /// should be added to those display item coordinates in order to get a
     /// normalized value that is consistent across display lists.
     pub external_scroll_offset: LayoutVector2D,
 }
 
+/// A solid color to draw (may not actually be a rectangle due to complex clips)
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct RectangleDisplayItem {
+    pub common: CommonItemProperties,
     pub color: ColorF,
 }
 
+/// Clears all colors from the area, making it possible to cut holes in the window.
+/// (useful for things like the macos frosted-glass effect).
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct ClearRectangleDisplayItem {
+    pub common: CommonItemProperties,
+}
+
+/// A minimal hit-testable item for the parent browser's convenience, and is
+/// slimmer than a RectangleDisplayItem (no color). The existence of this as a
+/// distinct item also makes it easier to inspect/debug display items.
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct HitTestDisplayItem {
+    pub common: CommonItemProperties,
+}
+
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct LineDisplayItem {
-    pub orientation: LineOrientation, // toggles whether above values are interpreted as x/y values
+    pub common: CommonItemProperties,
+    /// We need a separate rect from common.clip_rect to encode cute
+    /// tricks that firefox does to make a series of text-decorations seamlessly
+    /// line up -- snapping the decorations to a multiple of their period, and
+    /// then clipping them to their "proper" area. This rect is that "logical"
+    /// snapped area that may be clipped to the right size by the clip_rect.
+    pub area: LayoutRect,
+    /// Whether the rect is interpretted as vertical or horizontal
+    pub orientation: LineOrientation,
+    /// This could potentially be implied from area, but we currently prefer
+    /// that this is the responsibility of the layout engine. Value irrelevant
+    /// for non-wavy lines.
+    // FIXME: this was done before we could use tagged unions in enums, but now
+    // it should just be part of LineStyle::Wavy.
     pub wavy_line_thickness: f32,
     pub color: ColorF,
     pub style: LineStyle,
 }
 
 #[repr(u8)]
 #[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, Eq, Hash)]
 pub enum LineOrientation {
@@ -265,16 +309,25 @@ pub enum LineStyle {
     Solid,
     Dotted,
     Dashed,
     Wavy,
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct TextDisplayItem {
+    pub common: CommonItemProperties,
+    /// The area all the glyphs should be found in. Strictly speaking this isn't
+    /// necessarily needed, but layout engines should already "know" this, and we
+    /// use it cull and size things quickly before glyph layout is done. Currently
+    /// the glyphs *can* be outside these bounds, but that should imply they
+    /// can be cut off.
+    // FIXME: these are currently sometimes ignored to keep some old wrench tests
+    // working, but we should really just fix the tests!
+    pub bounds: LayoutRect,
     pub font_key: font::FontInstanceKey,
     pub color: ColorF,
     pub glyph_options: Option<font::GlyphOptions>,
 } // IMPLICIT: glyphs: Vec<font::GlyphInstance>
 
 #[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
 pub struct NormalBorder {
     pub left: BorderSide,
@@ -390,16 +443,18 @@ pub struct NinePatchBorder {
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub enum BorderDetails {
     Normal(NormalBorder),
     NinePatch(NinePatchBorder),
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct BorderDisplayItem {
+    pub common: CommonItemProperties,
+    pub bounds: LayoutRect,
     pub widths: LayoutSideOffsets,
     pub details: BorderDetails,
 }
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub enum BorderRadiusKind {
     Uniform,
@@ -447,25 +502,32 @@ impl BorderStyle {
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
 pub enum BoxShadowClipMode {
     Outset = 0,
     Inset = 1,
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct BoxShadowDisplayItem {
+    pub common: CommonItemProperties,
     pub box_bounds: LayoutRect,
     pub offset: LayoutVector2D,
     pub color: ColorF,
     pub blur_radius: f32,
     pub spread_radius: f32,
     pub border_radius: BorderRadius,
     pub clip_mode: BoxShadowClipMode,
 }
 
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct PushShadowDisplayItem {
+    pub space_and_clip: SpaceAndClipInfo,
+    pub shadow: Shadow,
+}
+
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct Shadow {
     pub offset: LayoutVector2D,
     pub color: ColorF,
     pub blur_radius: f32,
     pub should_inflate: bool,
 }
@@ -479,21 +541,30 @@ pub enum ExtendMode {
 
 #[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>
 
+/// The area
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct GradientDisplayItem {
-    pub gradient: Gradient,
+    /// NOTE: common.clip_rect is the area the gradient covers
+    pub common: CommonItemProperties,
+    /// The area to tile the gradient over (first tile starts at origin of this rect)
+    // FIXME: this should ideally just be `tile_origin` here, with the clip_rect
+    // defining the bounds of the item. Needs non-trivial backend changes.
+    pub bounds: LayoutRect,
+    /// How big a tile of the of the gradient should be (common case: bounds.size)
     pub tile_size: LayoutSize,
+    /// The space between tiles of the gradient (common case: 0)
     pub tile_spacing: LayoutSize,
+    pub gradient: Gradient,
 }
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
 pub struct GradientStop {
     pub offset: f32,
     pub color: ColorF,
 }
@@ -502,31 +573,39 @@ pub struct GradientStop {
 pub struct RadialGradient {
     pub center: LayoutPoint,
     pub radius: LayoutSize,
     pub start_offset: f32,
     pub end_offset: f32,
     pub extend_mode: ExtendMode,
 } // IMPLICIT stops: Vec<GradientStop>
 
+/// Just an abstraction for bundling up a bunch of clips into a "super clip".
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct ClipChainItem {
     pub id: ClipChainId,
     pub parent: Option<ClipChainId>,
-} // IMPLICIT stops: Vec<ClipId>
+} // IMPLICIT clip_ids: Vec<ClipId>
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct RadialGradientDisplayItem {
+    pub common: CommonItemProperties,
+    /// The area to tile the gradient over (first tile starts at origin of this rect)
+    // FIXME: this should ideally just be `tile_origin` here, with the clip_rect
+    // defining the bounds of the item. Needs non-trivial backend changes.
+    pub bounds: LayoutRect,
     pub gradient: RadialGradient,
     pub tile_size: LayoutSize,
     pub tile_spacing: LayoutSize,
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct ReferenceFrameDisplayListItem {
+    pub origin: LayoutPoint,
+    pub parent_spatial_id: SpatialId,
     pub reference_frame: ReferenceFrame,
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub enum ReferenceFrameKind {
     Transform,
     Perspective {
         scrolling_relative_to: Option<ExternalScrollId>,
@@ -540,16 +619,19 @@ pub struct ReferenceFrame {
     /// The transform matrix, either the perspective matrix or the transform
     /// matrix.
     pub transform: PropertyBinding<LayoutTransform>,
     pub id: SpatialId,
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct PushStackingContextDisplayItem {
+    pub origin: LayoutPoint,
+    pub spatial_id: SpatialId,
+    pub is_backface_visible: bool,
     pub stacking_context: StackingContext,
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct StackingContext {
     pub transform_style: TransformStyle,
     pub mix_blend_mode: MixBlendMode,
     pub clip_id: Option<ClipId>,
@@ -749,27 +831,40 @@ impl FilterData {
         self.func_g_type == ComponentTransferFuncType::Identity &&
         self.func_b_type == ComponentTransferFuncType::Identity &&
         self.func_a_type == ComponentTransferFuncType::Identity
     }
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct IframeDisplayItem {
+    pub bounds: LayoutRect,
+    pub clip_rect: LayoutRect,
+    pub space_and_clip: SpaceAndClipInfo,
     pub pipeline_id: PipelineId,
     pub ignore_missing_pipeline: bool,
 }
 
+/// This describes an image or, more generally, a background-image and its tiling.
+/// (A background-image repeats in a grid to fill the specified area).
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct ImageDisplayItem {
-    pub image_key: ImageKey,
+    pub common: CommonItemProperties,
+    /// The area to tile the image over (first tile starts at origin of this rect)
+    // FIXME: this should ideally just be `tile_origin` here, with the clip_rect
+    // defining the bounds of the item. Needs non-trivial backend changes.
+    pub bounds: LayoutRect,
+    /// How large to make a single tile of the image (common case: bounds.size)
     pub stretch_size: LayoutSize,
+    /// The space between tiles (common case: 0)
     pub tile_spacing: LayoutSize,
+    pub image_key: ImageKey,
     pub image_rendering: ImageRendering,
     pub alpha_type: AlphaType,
+    /// A hack used by gecko to color a simple bitmap font used for tofu glyphs
     pub color: ColorF,
 }
 
 #[repr(u8)]
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
 pub enum ImageRendering {
     Auto = 0,
     CrispEdges = 1,
@@ -779,16 +874,18 @@ pub enum ImageRendering {
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
 pub enum AlphaType {
     Alpha = 0,
     PremultipliedAlpha = 1,
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct YuvImageDisplayItem {
+    pub common: CommonItemProperties,
+    pub bounds: LayoutRect,
     pub yuv_data: YuvData,
     pub color_depth: ColorDepth,
     pub color_space: YuvColorSpace,
     pub image_rendering: ImageRendering,
 }
 
 #[repr(u8)]
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
@@ -1056,38 +1153,39 @@ impl ExternalScrollId {
         self.1
     }
 
     pub fn is_root(&self) -> bool {
         self.0 == 0
     }
 }
 
-impl SpecificDisplayItem {
+impl DisplayItem {
     pub fn debug_name(&self) -> &'static str {
         match *self {
-            SpecificDisplayItem::Border(..) => "border",
-            SpecificDisplayItem::BoxShadow(..) => "box_shadow",
-            SpecificDisplayItem::ClearRectangle => "clear_rectangle",
-            SpecificDisplayItem::Clip(..) => "clip",
-            SpecificDisplayItem::ClipChain(..) => "clip_chain",
-            SpecificDisplayItem::Gradient(..) => "gradient",
-            SpecificDisplayItem::Iframe(..) => "iframe",
-            SpecificDisplayItem::Image(..) => "image",
-            SpecificDisplayItem::Line(..) => "line",
-            SpecificDisplayItem::PopAllShadows => "pop_all_shadows",
-            SpecificDisplayItem::PopReferenceFrame => "pop_reference_frame",
-            SpecificDisplayItem::PopStackingContext => "pop_stacking_context",
-            SpecificDisplayItem::PushShadow(..) => "push_shadow",
-            SpecificDisplayItem::PushReferenceFrame(..) => "push_reference_frame",
-            SpecificDisplayItem::PushStackingContext(..) => "push_stacking_context",
-            SpecificDisplayItem::SetFilterOps => "set_filter_ops",
-            SpecificDisplayItem::SetFilterData => "set_filter_data",
-            SpecificDisplayItem::RadialGradient(..) => "radial_gradient",
-            SpecificDisplayItem::Rectangle(..) => "rectangle",
-            SpecificDisplayItem::ScrollFrame(..) => "scroll_frame",
-            SpecificDisplayItem::SetGradientStops => "set_gradient_stops",
-            SpecificDisplayItem::StickyFrame(..) => "sticky_frame",
-            SpecificDisplayItem::Text(..) => "text",
-            SpecificDisplayItem::YuvImage(..) => "yuv_image",
+            DisplayItem::Border(..) => "border",
+            DisplayItem::BoxShadow(..) => "box_shadow",
+            DisplayItem::ClearRectangle(..) => "clear_rectangle",
+            DisplayItem::HitTest(..) => "hit_test",
+            DisplayItem::Clip(..) => "clip",
+            DisplayItem::ClipChain(..) => "clip_chain",
+            DisplayItem::Gradient(..) => "gradient",
+            DisplayItem::Iframe(..) => "iframe",
+            DisplayItem::Image(..) => "image",
+            DisplayItem::Line(..) => "line",
+            DisplayItem::PopAllShadows => "pop_all_shadows",
+            DisplayItem::PopReferenceFrame => "pop_reference_frame",
+            DisplayItem::PopStackingContext => "pop_stacking_context",
+            DisplayItem::PushShadow(..) => "push_shadow",
+            DisplayItem::PushReferenceFrame(..) => "push_reference_frame",
+            DisplayItem::PushStackingContext(..) => "push_stacking_context",
+            DisplayItem::SetFilterOps => "set_filter_ops",
+            DisplayItem::SetFilterData => "set_filter_data",
+            DisplayItem::RadialGradient(..) => "radial_gradient",
+            DisplayItem::Rectangle(..) => "rectangle",
+            DisplayItem::ScrollFrame(..) => "scroll_frame",
+            DisplayItem::SetGradientStops => "set_gradient_stops",
+            DisplayItem::StickyFrame(..) => "sticky_frame",
+            DisplayItem::Text(..) => "text",
+            DisplayItem::YuvImage(..) => "yuv_image",
         }
     }
 }
--- a/gfx/wr/webrender_api/src/display_list.rs
+++ b/gfx/wr/webrender_api/src/display_list.rs
@@ -116,16 +116,17 @@ pub struct BuiltDisplayListIter<'a> {
     cur_clip_chain_items: ItemRange<di::ClipId>,
     cur_complex_clip: (ItemRange<di::ComplexClipRegion>, usize),
     peeking: Peek,
     /// Should just be initialized but never populated in release builds
     debug_stats: DebugStats,
 }
 
 /// Internal info used for more detailed analysis of serialized display lists
+#[allow(dead_code)]
 struct DebugStats {
     /// Last address in the buffer we pointed to, for computing serialized sizes
     last_addr: usize,
     stats: HashMap<&'static str, ItemStats>,
 }
 
 /// Stats for an individual item
 #[derive(Copy, Clone, Debug, Default)]
@@ -237,22 +238,17 @@ impl<'a> BuiltDisplayListIter<'a> {
     pub fn new(list: &'a BuiltDisplayList) -> Self {
         Self::new_with_list_and_data(list, list.item_slice())
     }
 
     pub fn new_with_list_and_data(list: &'a BuiltDisplayList, data: &'a [u8]) -> Self {
         BuiltDisplayListIter {
             list,
             data,
-            cur_item: di::DisplayItem {
-                // Dummy data, will be overwritten by `next`
-                item: di::SpecificDisplayItem::PopStackingContext,
-                layout: di::LayoutPrimitiveInfo::new(LayoutRect::zero()),
-                space_and_clip: di::SpaceAndClipInfo::root_scroll(PipelineId::dummy())
-            },
+            cur_item: di::DisplayItem::PopStackingContext,
             cur_stops: ItemRange::default(),
             cur_glyphs: ItemRange::default(),
             cur_filters: ItemRange::default(),
             cur_filter_data: Vec::new(),
             cur_clip_chain_items: ItemRange::default(),
             cur_complex_clip: (ItemRange::default(), 0),
             peeking: Peek::NotPeeking,
             debug_stats: DebugStats {
@@ -262,17 +258,17 @@ impl<'a> BuiltDisplayListIter<'a> {
         }
     }
 
     pub fn display_list(&self) -> &'a BuiltDisplayList {
         self.list
     }
 
     pub fn next<'b>(&'b mut self) -> Option<DisplayItemRef<'a, 'b>> {
-        use SpecificDisplayItem::*;
+        use DisplayItem::*;
 
         match self.peeking {
             Peek::IsPeeking => {
                 self.peeking = Peek::NotPeeking;
                 return Some(self.as_ref());
             }
             Peek::StartPeeking => {
                 self.peeking = Peek::IsPeeking;
@@ -282,54 +278,51 @@ impl<'a> BuiltDisplayListIter<'a> {
 
         // Don't let these bleed into another item
         self.cur_stops = ItemRange::default();
         self.cur_complex_clip = (ItemRange::default(), 0);
         self.cur_clip_chain_items = ItemRange::default();
 
         loop {
             self.next_raw()?;
-            if let SetGradientStops = self.cur_item.item {
-                // SetGradientStops is a dummy item that most consumers should ignore
-                continue;
+            match self.cur_item {
+                SetGradientStops |
+                SetFilterOps |
+                SetFilterData => {
+                    // These are marker items for populating other display items, don't yield them.
+                    continue;
+                }
+                _ => {
+                    break;
+                }
             }
-            if let SetFilterOps = self.cur_item.item {
-                // SetFilterOps is a dummy item that most consumers should ignore
-                continue;
-            }
-            if let SetFilterData = self.cur_item.item {
-                // SetFilterData is a dummy item that most consumers should ignore
-                continue;
-            }
-
-            break;
         }
 
         Some(self.as_ref())
     }
 
     /// Gets the next display item, even if it's a dummy. Also doesn't handle peeking
     /// and may leave irrelevant ranges live (so a Clip may have GradientStops if
     /// for some reason you ask).
     pub fn next_raw<'b>(&'b mut self) -> Option<DisplayItemRef<'a, 'b>> {
-        use SpecificDisplayItem::*;
+        use DisplayItem::*;
 
         if self.data.is_empty() {
             return None;
         }
 
         {
             let reader = bincode::IoReader::new(UnsafeReader::new(&mut self.data));
             bincode::deserialize_in_place(reader, &mut self.cur_item)
                 .expect("MEH: malicious process?");
         }
 
         self.log_item_stats();
 
-        match self.cur_item.item {
+        match self.cur_item {
             SetGradientStops => {
                 self.cur_stops = skip_slice::<di::GradientStop>(self.list, &mut self.data).0;
                 let temp = self.cur_stops;
                 self.log_slice_stats("set_gradient_stops.stops", temp);
             }
             SetFilterOps => {
                 self.cur_filters = skip_slice::<di::FilterOp>(self.list, &mut self.data).0;
                 let temp = self.cur_filters;
@@ -354,17 +347,17 @@ impl<'a> BuiltDisplayListIter<'a> {
             ClipChain(_) => {
                 self.cur_clip_chain_items = skip_slice::<di::ClipId>(self.list, &mut self.data).0;
                 let temp = self.cur_clip_chain_items;
                 self.log_slice_stats("clip_chain.clip_ids", temp);
             }
             Clip(_) | ScrollFrame(_) => {
                 self.cur_complex_clip = self.skip_slice::<di::ComplexClipRegion>();
 
-                let name = if let Clip(_) = self.cur_item.item {
+                let name = if let Clip(_) = self.cur_item {
                     "clip.complex_clips"
                 } else {
                     "scroll_frame.complex_clips"
                 };
                 let temp = self.cur_complex_clip.0;
                 self.log_slice_stats(name, temp);
             }
             Text(_) => {
@@ -385,28 +378,28 @@ impl<'a> BuiltDisplayListIter<'a> {
     pub fn as_ref<'b>(&'b self) -> DisplayItemRef<'a, 'b> {
         DisplayItemRef { iter: self }
     }
 
     pub fn skip_current_stacking_context(&mut self) {
         let mut depth = 0;
         while let Some(item) = self.next() {
             match *item.item() {
-                di::SpecificDisplayItem::PushStackingContext(..) => depth += 1,
-                di::SpecificDisplayItem::PopStackingContext if depth == 0 => return,
-                di::SpecificDisplayItem::PopStackingContext => depth -= 1,
+                di::DisplayItem::PushStackingContext(..) => depth += 1,
+                di::DisplayItem::PopStackingContext if depth == 0 => return,
+                di::DisplayItem::PopStackingContext => depth -= 1,
                 _ => {}
             }
             debug_assert!(depth >= 0);
         }
     }
 
     pub fn current_stacking_context_empty(&mut self) -> bool {
         match self.peek() {
-            Some(item) => *item.item() == di::SpecificDisplayItem::PopStackingContext,
+            Some(item) => *item.item() == di::DisplayItem::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()
@@ -434,17 +427,17 @@ impl<'a> BuiltDisplayListIter<'a> {
         }
     }
 
     /// Logs stats for the last deserialized display item
     #[cfg(feature = "display_list_stats")]
     fn log_item_stats(&mut self) {
         let num_bytes = self.debug_num_bytes();
 
-        let item_name = self.cur_item.item.debug_name();
+        let item_name = self.cur_item.debug_name();
         let entry = self.debug_stats.stats.entry(item_name).or_default();
 
         entry.total_count += 1;
         entry.num_bytes += num_bytes;
     }
 
     /// Logs the stats for the given serialized slice
     #[cfg(feature = "display_list_stats")]
@@ -475,46 +468,20 @@ impl<'a> BuiltDisplayListIter<'a> {
     #[cfg(not(feature = "display_list_stats"))]
     fn log_item_stats(&mut self) { /* no-op */ }
     #[cfg(not(feature = "display_list_stats"))]
     fn log_slice_stats<T>(&mut self, _slice_name: &str, _range: ItemRange<T>) { /* no-op */ }
 }
 
 // Some of these might just become ItemRanges
 impl<'a, 'b> DisplayItemRef<'a, 'b> {
-    pub fn display_item(&self) -> &di::DisplayItem {
+    pub fn item(&self) -> &di::DisplayItem {
         &self.iter.cur_item
     }
 
-    pub fn rect(&self) -> LayoutRect {
-        self.iter.cur_item.layout.rect
-    }
-
-    pub fn get_layout_primitive_info(&self, offset: &LayoutVector2D) -> di::LayoutPrimitiveInfo {
-        let layout = self.iter.cur_item.layout;
-        di::LayoutPrimitiveInfo {
-            rect: layout.rect.translate(offset),
-            clip_rect: layout.clip_rect.translate(offset),
-            is_backface_visible: layout.is_backface_visible,
-            tag: layout.tag,
-        }
-    }
-
-    pub fn clip_rect(&self) -> &LayoutRect {
-        &self.iter.cur_item.layout.clip_rect
-    }
-
-    pub fn space_and_clip_info(&self) -> &di::SpaceAndClipInfo {
-        &self.iter.cur_item.space_and_clip
-    }
-
-    pub fn item(&self) -> &di::SpecificDisplayItem {
-        &self.iter.cur_item.item
-    }
-
     pub fn complex_clip(&self) -> (ItemRange<di::ComplexClipRegion>, usize) {
         self.iter.cur_complex_clip
     }
 
     pub fn gradient_stops(&self) -> ItemRange<di::GradientStop> {
         self.iter.cur_stops
     }
 
@@ -533,20 +500,16 @@ impl<'a, 'b> DisplayItemRef<'a, 'b> {
     pub fn clip_chain_items(&self) -> ItemRange<di::ClipId> {
         self.iter.cur_clip_chain_items
     }
 
     pub fn display_list(&self) -> &BuiltDisplayList {
         self.iter.display_list()
     }
 
-    pub fn is_backface_visible(&self) -> bool {
-        self.iter.cur_item.layout.is_backface_visible
-    }
-
     // 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 {
@@ -585,85 +548,82 @@ impl<'a, T: for<'de> Deserialize<'de>> I
 }
 
 impl<'a, T: for<'de> Deserialize<'de>> ::std::iter::ExactSizeIterator for AuxIter<'a, T> {}
 
 
 #[cfg(feature = "serialize")]
 impl Serialize for BuiltDisplayList {
     fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
-        use display_item::CompletelySpecificDisplayItem::*;
-        use display_item::GenericDisplayItem;
+        use display_item::DisplayItem as Real;
+        use display_item::DebugDisplayItem as Debug;
 
         let mut seq = serializer.serialize_seq(None)?;
         let mut traversal = self.iter();
         while let Some(item) = traversal.next_raw() {
-            let display_item = item.display_item();
-            let serial_di = GenericDisplayItem {
-                item: match display_item.item {
-                    di::SpecificDisplayItem::Clip(v) => Clip(
-                        v,
-                        item.iter.list.get(item.iter.cur_complex_clip.0).collect()
-                    ),
-                    di::SpecificDisplayItem::ClipChain(v) => ClipChain(
-                        v,
-                        item.iter.list.get(item.iter.cur_clip_chain_items).collect(),
-                    ),
-                    di::SpecificDisplayItem::ScrollFrame(v) => ScrollFrame(
-                        v,
-                        item.iter.list.get(item.iter.cur_complex_clip.0).collect()
-                    ),
-                    di::SpecificDisplayItem::StickyFrame(v) => StickyFrame(v),
-                    di::SpecificDisplayItem::Rectangle(v) => Rectangle(v),
-                    di::SpecificDisplayItem::ClearRectangle => ClearRectangle,
-                    di::SpecificDisplayItem::Line(v) => Line(v),
-                    di::SpecificDisplayItem::Text(v) => Text(
-                        v,
-                        item.iter.list.get(item.iter.cur_glyphs).collect()
-                    ),
-                    di::SpecificDisplayItem::Image(v) => Image(v),
-                    di::SpecificDisplayItem::YuvImage(v) => YuvImage(v),
-                    di::SpecificDisplayItem::Border(v) => Border(v),
-                    di::SpecificDisplayItem::BoxShadow(v) => BoxShadow(v),
-                    di::SpecificDisplayItem::Gradient(v) => Gradient(v),
-                    di::SpecificDisplayItem::RadialGradient(v) => RadialGradient(v),
-                    di::SpecificDisplayItem::Iframe(v) => Iframe(v),
-                    di::SpecificDisplayItem::PushReferenceFrame(v) => PushReferenceFrame(v),
-                    di::SpecificDisplayItem::PopReferenceFrame => PopReferenceFrame,
-                    di::SpecificDisplayItem::PushStackingContext(v) => PushStackingContext(v),
-                    di::SpecificDisplayItem::PopStackingContext => PopStackingContext,
-                    di::SpecificDisplayItem::SetFilterOps => SetFilterOps(
-                        item.iter.list.get(item.iter.cur_filters).collect()
-                    ),
-                    di::SpecificDisplayItem::SetFilterData => {
-                        debug_assert!(!item.iter.cur_filter_data.is_empty());
-                        let temp_filter_data = &item.iter.cur_filter_data[item.iter.cur_filter_data.len()-1];
+            let serial_di = match *item.item() {
+                Real::Clip(v) => Debug::Clip(
+                    v,
+                    item.iter.list.get(item.iter.cur_complex_clip.0).collect()
+                ),
+                Real::ClipChain(v) => Debug::ClipChain(
+                    v,
+                    item.iter.list.get(item.iter.cur_clip_chain_items).collect(),
+                ),
+                Real::ScrollFrame(v) => Debug::ScrollFrame(
+                    v,
+                    item.iter.list.get(item.iter.cur_complex_clip.0).collect()
+                ),
+                Real::Text(v) => Debug::Text(
+                    v,
+                    item.iter.list.get(item.iter.cur_glyphs).collect()
+                ),
+                Real::SetFilterOps => Debug::SetFilterOps(
+                    item.iter.list.get(item.iter.cur_filters).collect()
+                ),
+                Real::SetFilterData => {
+                    debug_assert!(!item.iter.cur_filter_data.is_empty());
+                    let temp_filter_data = &item.iter.cur_filter_data[item.iter.cur_filter_data.len()-1];
 
-                        let func_types: Vec<di::ComponentTransferFuncType> =
-                            item.iter.list.get(temp_filter_data.func_types).collect();
-                        debug_assert!(func_types.len() == 4);
-                        SetFilterData(di::FilterData {
-                            func_r_type: func_types[0],
-                            r_values: item.iter.list.get(temp_filter_data.r_values).collect(),
-                            func_g_type: func_types[1],
-                            g_values: item.iter.list.get(temp_filter_data.g_values).collect(),
-                            func_b_type: func_types[2],
-                            b_values: item.iter.list.get(temp_filter_data.b_values).collect(),
-                            func_a_type: func_types[3],
-                            a_values: item.iter.list.get(temp_filter_data.a_values).collect(),
-                        })
-                    },
-                    di::SpecificDisplayItem::SetGradientStops => SetGradientStops(
-                        item.iter.list.get(item.iter.cur_stops).collect()
-                    ),
-                    di::SpecificDisplayItem::PushShadow(v) => PushShadow(v),
-                    di::SpecificDisplayItem::PopAllShadows => PopAllShadows,
+                    let func_types: Vec<di::ComponentTransferFuncType> =
+                        item.iter.list.get(temp_filter_data.func_types).collect();
+                    debug_assert!(func_types.len() == 4);
+                    Debug::SetFilterData(di::FilterData {
+                        func_r_type: func_types[0],
+                        r_values: item.iter.list.get(temp_filter_data.r_values).collect(),
+                        func_g_type: func_types[1],
+                        g_values: item.iter.list.get(temp_filter_data.g_values).collect(),
+                        func_b_type: func_types[2],
+                        b_values: item.iter.list.get(temp_filter_data.b_values).collect(),
+                        func_a_type: func_types[3],
+                        a_values: item.iter.list.get(temp_filter_data.a_values).collect(),
+                    })
                 },
-                layout: display_item.layout,
-                space_and_clip: display_item.space_and_clip,
+                Real::SetGradientStops => Debug::SetGradientStops(
+                    item.iter.list.get(item.iter.cur_stops).collect()
+                ),
+                Real::StickyFrame(v) => Debug::StickyFrame(v),
+                Real::Rectangle(v) => Debug::Rectangle(v),
+                Real::ClearRectangle(v) => Debug::ClearRectangle(v),
+                Real::HitTest(v) => Debug::HitTest(v),
+                Real::Line(v) => Debug::Line(v),
+                Real::Image(v) => Debug::Image(v),
+                Real::YuvImage(v) => Debug::YuvImage(v),
+                Real::Border(v) => Debug::Border(v),
+                Real::BoxShadow(v) => Debug::BoxShadow(v),
+                Real::Gradient(v) => Debug::Gradient(v),
+                Real::RadialGradient(v) => Debug::RadialGradient(v),
+                Real::Iframe(v) => Debug::Iframe(v),
+                Real::PushReferenceFrame(v) => Debug::PushReferenceFrame(v),
+                Real::PushStackingContext(v) => Debug::PushStackingContext(v),
+                Real::PushShadow(v) => Debug::PushShadow(v),
+
+                Real::PopReferenceFrame => Debug::PopReferenceFrame,
+                Real::PopStackingContext => Debug::PopStackingContext,
+                Real::PopAllShadows => Debug::PopAllShadows,
             };
             seq.serialize_element(&serial_di)?
         }
         seq.end()
     }
 }
 
 // The purpose of this implementation is to deserialize
@@ -671,101 +631,96 @@ impl Serialize for BuiltDisplayList {
 // serialize then into a "built" `Vec<u8>`.
 
 #[cfg(feature = "deserialize")]
 impl<'de> Deserialize<'de> for BuiltDisplayList {
     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     where
         D: Deserializer<'de>,
     {
-        use display_item::CompletelySpecificDisplayItem::*;
-        use display_item::{CompletelySpecificDisplayItem, GenericDisplayItem};
+        use display_item::DisplayItem as Real;
+        use display_item::DebugDisplayItem as Debug;
 
-        let list = Vec::<GenericDisplayItem<CompletelySpecificDisplayItem>>
-            ::deserialize(deserializer)?;
+        let list = Vec::<Debug>::deserialize(deserializer)?;
 
         let mut data = Vec::new();
         let mut temp = Vec::new();
         let mut total_clip_nodes = FIRST_CLIP_NODE_INDEX;
         let mut total_spatial_nodes = FIRST_SPATIAL_NODE_INDEX;
         for complete in list {
-            let item = di::DisplayItem {
-                item: match complete.item {
-                    Clip(specific_item, complex_clips) => {
-                        total_clip_nodes += 1;
-                        DisplayListBuilder::push_iter_impl(&mut temp, complex_clips);
-                        di::SpecificDisplayItem::Clip(specific_item)
-                    },
-                    ClipChain(specific_item, clip_chain_ids) => {
-                        DisplayListBuilder::push_iter_impl(&mut temp, clip_chain_ids);
-                        di::SpecificDisplayItem::ClipChain(specific_item)
-                    }
-                    ScrollFrame(specific_item, complex_clips) => {
-                        total_spatial_nodes += 1;
-                        total_clip_nodes += 1;
-                        DisplayListBuilder::push_iter_impl(&mut temp, complex_clips);
-                        di::SpecificDisplayItem::ScrollFrame(specific_item)
-                    }
-                    StickyFrame(specific_item) => {
-                        total_spatial_nodes += 1;
-                        di::SpecificDisplayItem::StickyFrame(specific_item)
-                    }
-                    Rectangle(specific_item) => di::SpecificDisplayItem::Rectangle(specific_item),
-                    ClearRectangle => di::SpecificDisplayItem::ClearRectangle,
-                    Line(specific_item) => di::SpecificDisplayItem::Line(specific_item),
-                    Text(specific_item, glyphs) => {
-                        DisplayListBuilder::push_iter_impl(&mut temp, glyphs);
-                        di::SpecificDisplayItem::Text(specific_item)
-                    },
-                    Image(specific_item) => di::SpecificDisplayItem::Image(specific_item),
-                    YuvImage(specific_item) => di::SpecificDisplayItem::YuvImage(specific_item),
-                    Border(specific_item) => di::SpecificDisplayItem::Border(specific_item),
-                    BoxShadow(specific_item) => di::SpecificDisplayItem::BoxShadow(specific_item),
-                    Gradient(specific_item) => di::SpecificDisplayItem::Gradient(specific_item),
-                    RadialGradient(specific_item) =>
-                        di::SpecificDisplayItem::RadialGradient(specific_item),
-                    Iframe(specific_item) => {
-                        total_clip_nodes += 1;
-                        di::SpecificDisplayItem::Iframe(specific_item)
-                    }
-                    PushReferenceFrame(v) => {
-                        total_spatial_nodes += 1;
-                        di::SpecificDisplayItem::PushReferenceFrame(v)
-                    }
-                    PopReferenceFrame => di::SpecificDisplayItem::PopReferenceFrame,
-                    PushStackingContext(specific_item) => {
-                        di::SpecificDisplayItem::PushStackingContext(specific_item)
-                    },
-                    SetFilterOps(filters) => {
-                        DisplayListBuilder::push_iter_impl(&mut temp, filters);
-                        di::SpecificDisplayItem::SetFilterOps
-                    },
-                    SetFilterData(filter_data) => {
-                        let func_types: Vec<di::ComponentTransferFuncType> =
-                            [filter_data.func_r_type,
-                             filter_data.func_g_type,
-                             filter_data.func_b_type,
-                             filter_data.func_a_type].to_vec();
-                        DisplayListBuilder::push_iter_impl(&mut temp, func_types);
-                        DisplayListBuilder::push_iter_impl(&mut temp, filter_data.r_values);
-                        DisplayListBuilder::push_iter_impl(&mut temp, filter_data.g_values);
-                        DisplayListBuilder::push_iter_impl(&mut temp, filter_data.b_values);
-                        DisplayListBuilder::push_iter_impl(&mut temp, filter_data.a_values);
-                        di::SpecificDisplayItem::SetFilterData
-                    },
-                    PopStackingContext => di::SpecificDisplayItem::PopStackingContext,
-                    SetGradientStops(stops) => {
-                        DisplayListBuilder::push_iter_impl(&mut temp, stops);
-                        di::SpecificDisplayItem::SetGradientStops
-                    },
-                    PushShadow(specific_item) => di::SpecificDisplayItem::PushShadow(specific_item),
-                    PopAllShadows => di::SpecificDisplayItem::PopAllShadows,
+            let item = match complete {
+                Debug::Clip(v, complex_clips) => {
+                    total_clip_nodes += 1;
+                    DisplayListBuilder::push_iter_impl(&mut temp, complex_clips);
+                    Real::Clip(v)
+                },
+                Debug::ClipChain(v, clip_chain_ids) => {
+                    DisplayListBuilder::push_iter_impl(&mut temp, clip_chain_ids);
+                    Real::ClipChain(v)
+                }
+                Debug::ScrollFrame(v, complex_clips) => {
+                    total_spatial_nodes += 1;
+                    total_clip_nodes += 1;
+                    DisplayListBuilder::push_iter_impl(&mut temp, complex_clips);
+                    Real::ScrollFrame(v)
+                }
+                Debug::StickyFrame(v) => {
+                    total_spatial_nodes += 1;
+                    Real::StickyFrame(v)
+                }
+                Debug::Text(v, glyphs) => {
+                    DisplayListBuilder::push_iter_impl(&mut temp, glyphs);
+                    Real::Text(v)
                 },
-                layout: complete.layout,
-                space_and_clip: complete.space_and_clip,
+                Debug::Iframe(v) => {
+                    total_clip_nodes += 1;
+                    Real::Iframe(v)
+                }
+                Debug::PushReferenceFrame(v) => {
+                    total_spatial_nodes += 1;
+                    Real::PushReferenceFrame(v)
+                }
+                Debug::SetFilterOps(filters) => {
+                    DisplayListBuilder::push_iter_impl(&mut temp, filters);
+                    Real::SetFilterOps
+                },
+                Debug::SetFilterData(filter_data) => {
+                    let func_types: Vec<di::ComponentTransferFuncType> =
+                        [filter_data.func_r_type,
+                         filter_data.func_g_type,
+                         filter_data.func_b_type,
+                         filter_data.func_a_type].to_vec();
+                    DisplayListBuilder::push_iter_impl(&mut temp, func_types);
+                    DisplayListBuilder::push_iter_impl(&mut temp, filter_data.r_values);
+                    DisplayListBuilder::push_iter_impl(&mut temp, filter_data.g_values);
+                    DisplayListBuilder::push_iter_impl(&mut temp, filter_data.b_values);
+                    DisplayListBuilder::push_iter_impl(&mut temp, filter_data.a_values);
+                    Real::SetFilterData
+                },
+                Debug::SetGradientStops(stops) => {
+                    DisplayListBuilder::push_iter_impl(&mut temp, stops);
+                    Real::SetGradientStops
+                },
+
+                Debug::Rectangle(v) => Real::Rectangle(v),
+                Debug::ClearRectangle(v) => Real::ClearRectangle(v),
+                Debug::HitTest(v) => Real::HitTest(v),
+                Debug::Line(v) => Real::Line(v),
+                Debug::Image(v) => Real::Image(v),
+                Debug::YuvImage(v) => Real::YuvImage(v),
+                Debug::Border(v) => Real::Border(v),
+                Debug::BoxShadow(v) => Real::BoxShadow(v),
+                Debug::Gradient(v) => Real::Gradient(v),
+                Debug::RadialGradient(v) => Real::RadialGradient(v),
+                Debug::PushStackingContext(v) => Real::PushStackingContext(v),
+                Debug::PushShadow(v) => Real::PushShadow(v),
+
+                Debug::PopStackingContext => Real::PopStackingContext,
+                Debug::PopReferenceFrame => Real::PopReferenceFrame,
+                Debug::PopAllShadows => Real::PopAllShadows,
             };
             serialize_fast(&mut data, &item);
             // the aux data is serialized after the item, hence the temporary
             data.extend(temp.drain(..));
         }
 
         Ok(BuiltDisplayList {
             data,
@@ -1108,56 +1063,34 @@ impl DisplayListBuilder {
         let mut temp = BuiltDisplayList::default();
         mem::swap(&mut temp.data, &mut self.data);
 
         let mut index: usize = 0;
         {
             let mut iter = BuiltDisplayListIter::new(&temp);
             while let Some(item) = iter.next_raw() {
                 if index >= range.start.unwrap_or(0) && range.end.map_or(true, |e| index < e) {
-                    writeln!(sink, "{}{:?}", "  ".repeat(indent), item.display_item()).unwrap();
+                    writeln!(sink, "{}{:?}", "  ".repeat(indent), item.item()).unwrap();
                 }
                 index += 1;
             }
         }
 
         self.data = temp.data;
         index
     }
 
     /// Add an item to the display list.
     ///
     /// NOTE: It is usually preferable to use the specialized methods to push
     /// display items. Pushing unexpected or invalid items here may
     /// result in WebRender panicking or behaving in unexpected ways.
     #[inline]
-    pub fn push_item(
-        &mut self,
-        item: &di::SpecificDisplayItem,
-        layout: &di::LayoutPrimitiveInfo,
-        space_and_clip: &di::SpaceAndClipInfo,
-    ) {
-        serialize_fast(
-            &mut self.data,
-            di::SerializedDisplayItem {
-                item,
-                layout,
-                space_and_clip,
-            },
-        )
-    }
-
-    #[inline]
-    fn push_new_empty_item(&mut self, item: &di::SpecificDisplayItem) {
-        let pipeline_id = self.pipeline_id;
-        self.push_item(
-            item,
-            &di::LayoutPrimitiveInfo::new(LayoutRect::zero()),
-            &di::SpaceAndClipInfo::root_scroll(pipeline_id),
-        )
+    pub fn push_item(&mut self, item: &di::DisplayItem) {
+        serialize_fast(&mut self.data, item);
     }
 
     fn push_iter_impl<I>(data: &mut Vec<u8>, iter_source: I)
     where
         I: IntoIterator,
         I::IntoIter: ExactSizeIterator + Clone,
         I::Item: Serialize,
     {
@@ -1197,110 +1130,132 @@ impl DisplayListBuilder {
         I::IntoIter: ExactSizeIterator + Clone,
         I::Item: Serialize,
     {
         Self::push_iter_impl(&mut self.data, iter);
     }
 
     pub fn push_rect(
         &mut self,
-        layout: &di::LayoutPrimitiveInfo,
-        space_and_clip: &di::SpaceAndClipInfo,
+        common: &di::CommonItemProperties,
         color: ColorF,
     ) {
-        let item = di::SpecificDisplayItem::Rectangle(di::RectangleDisplayItem { color });
-        self.push_item(&item, layout, space_and_clip);
+        let item = di::DisplayItem::Rectangle(di::RectangleDisplayItem {
+            common: *common,
+            color
+        });
+        self.push_item(&item);
     }
 
     pub fn push_clear_rect(
         &mut self,
-        layout: &di::LayoutPrimitiveInfo,
-        space_and_clip: &di::SpaceAndClipInfo,
+        common: &di::CommonItemProperties,
     ) {
-        self.push_item(&di::SpecificDisplayItem::ClearRectangle, layout, space_and_clip);
+        let item = di::DisplayItem::ClearRectangle(di::ClearRectangleDisplayItem {
+            common: *common,
+        });
+        self.push_item(&item);
+    }
+
+    pub fn push_hit_test(
+        &mut self,
+        common: &di::CommonItemProperties,
+    ) {
+        let item = di::DisplayItem::HitTest(di::HitTestDisplayItem {
+            common: *common,
+        });
+        self.push_item(&item);
     }
 
     pub fn push_line(
         &mut self,
-        layout: &di::LayoutPrimitiveInfo,
-        space_and_clip: &di::SpaceAndClipInfo,
+        common: &di::CommonItemProperties,
+        area: &LayoutRect,
         wavy_line_thickness: f32,
         orientation: di::LineOrientation,
         color: &ColorF,
         style: di::LineStyle,
     ) {
-        let item = di::SpecificDisplayItem::Line(di::LineDisplayItem {
+        let item = di::DisplayItem::Line(di::LineDisplayItem {
+            common: *common,
+            area: *area,
             wavy_line_thickness,
             orientation,
             color: *color,
             style,
         });
 
-        self.push_item(&item, layout, space_and_clip);
+        self.push_item(&item);
     }
 
     pub fn push_image(
         &mut self,
-        layout: &di::LayoutPrimitiveInfo,
-        space_and_clip: &di::SpaceAndClipInfo,
+        common: &di::CommonItemProperties,
+        bounds: LayoutRect,
         stretch_size: LayoutSize,
         tile_spacing: LayoutSize,
         image_rendering: di::ImageRendering,
         alpha_type: di::AlphaType,
         key: ImageKey,
         color: ColorF,
     ) {
-        let item = di::SpecificDisplayItem::Image(di::ImageDisplayItem {
+        let item = di::DisplayItem::Image(di::ImageDisplayItem {
+            common: *common,
+            bounds,
             image_key: key,
             stretch_size,
             tile_spacing,
             image_rendering,
             alpha_type,
             color,
         });
 
-        self.push_item(&item, layout, space_and_clip);
+        self.push_item(&item);
     }
 
     /// Push a yuv image. All planar data in yuv image should use the same buffer type.
     pub fn push_yuv_image(
         &mut self,
-        layout: &di::LayoutPrimitiveInfo,
-        space_and_clip: &di::SpaceAndClipInfo,
+        common: &di::CommonItemProperties,
+        bounds: LayoutRect,
         yuv_data: di::YuvData,
         color_depth: ColorDepth,
         color_space: di::YuvColorSpace,
         image_rendering: di::ImageRendering,
     ) {
-        let item = di::SpecificDisplayItem::YuvImage(di::YuvImageDisplayItem {
+        let item = di::DisplayItem::YuvImage(di::YuvImageDisplayItem {
+            common: *common,
+            bounds,
             yuv_data,
             color_depth,
             color_space,
             image_rendering,
         });
-        self.push_item(&item, layout, space_and_clip);
+        self.push_item(&item);
     }
 
     pub fn push_text(
         &mut self,
-        layout: &di::LayoutPrimitiveInfo,
-        space_and_clip: &di::SpaceAndClipInfo,
+        common: &di::CommonItemProperties,
+        bounds: LayoutRect,
         glyphs: &[GlyphInstance],
         font_key: FontInstanceKey,
         color: ColorF,
         glyph_options: Option<GlyphOptions>,
     ) {
-        let item = di::SpecificDisplayItem::Text(di::TextDisplayItem {
+        let item = di::DisplayItem::Text(di::TextDisplayItem {
+            common: *common,
+            bounds: bounds,
             color,
             font_key,
             glyph_options,
         });
 
         for split_glyphs in glyphs.chunks(MAX_TEXT_RUN_LENGTH) {
-            self.push_item(&item, layout, space_and_clip);
+            self.push_item(&item);
             self.push_iter(split_glyphs);
         }
     }
 
     /// 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,
@@ -1327,49 +1282,54 @@ impl DisplayListBuilder {
         let mut builder = GradientBuilder::with_stops(stops);
         let gradient = builder.radial_gradient(center, radius, extend_mode);
         self.push_stops(builder.stops());
         gradient
     }
 
     pub fn push_border(
         &mut self,
-        layout: &di::LayoutPrimitiveInfo,
-        space_and_clip: &di::SpaceAndClipInfo,
+        common: &di::CommonItemProperties,
+        bounds: LayoutRect,
         widths: LayoutSideOffsets,
         details: di::BorderDetails,
     ) {
-        let item = di::SpecificDisplayItem::Border(di::BorderDisplayItem { details, widths });
+        let item = di::DisplayItem::Border(di::BorderDisplayItem {
+            common: *common,
+            bounds,
+            details,
+            widths,
+        });
 
-        self.push_item(&item, layout, space_and_clip);
+        self.push_item(&item);
     }
 
     pub fn push_box_shadow(
         &mut self,
-        layout: &di::LayoutPrimitiveInfo,
-        space_and_clip: &di::SpaceAndClipInfo,
+        common: &di::CommonItemProperties,
         box_bounds: LayoutRect,
         offset: LayoutVector2D,
         color: ColorF,
         blur_radius: f32,
         spread_radius: f32,
         border_radius: di::BorderRadius,
         clip_mode: di::BoxShadowClipMode,
     ) {
-        let item = di::SpecificDisplayItem::BoxShadow(di::BoxShadowDisplayItem {
+        let item = di::DisplayItem::BoxShadow(di::BoxShadowDisplayItem {
+            common: *common,
             box_bounds,
             offset,
             color,
             blur_radius,
             spread_radius,
             border_radius,
             clip_mode,
         });
 
-        self.push_item(&item, layout, space_and_clip);
+        self.push_item(&item);
     }
 
     /// Pushes a linear gradient to be displayed.
     ///
     /// The gradient itself is described in the
     /// `gradient` parameter. It is drawn on
     /// a "tile" with the dimensions from `tile_size`.
     /// These tiles are now repeated to the right and
@@ -1378,167 +1338,172 @@ impl DisplayListBuilder {
     /// are inserted between the tiles as seams.
     ///
     /// The origin of the tiles is given in `layout.rect.origin`.
     /// If the gradient should only be displayed once limit
     /// the `layout.rect.size` to a single tile.
     /// The gradient is only visible within the local clip.
     pub fn push_gradient(
         &mut self,
-        layout: &di::LayoutPrimitiveInfo,
-        space_and_clip: &di::SpaceAndClipInfo,
+        common: &di::CommonItemProperties,
+        bounds: LayoutRect,
         gradient: di::Gradient,
         tile_size: LayoutSize,
         tile_spacing: LayoutSize,
     ) {
-        let item = di::SpecificDisplayItem::Gradient(di::GradientDisplayItem {
+        let item = di::DisplayItem::Gradient(di::GradientDisplayItem {
+            common: *common,
+            bounds,
             gradient,
             tile_size,
             tile_spacing,
         });
 
-        self.push_item(&item, layout, space_and_clip);
+        self.push_item(&item);
     }
 
     /// Pushes a radial gradient to be displayed.
     ///
     /// See [`push_gradient`](#method.push_gradient) for explanation.
     pub fn push_radial_gradient(
         &mut self,
-        layout: &di::LayoutPrimitiveInfo,
-        space_and_clip: &di::SpaceAndClipInfo,
+        common: &di::CommonItemProperties,
+        bounds: LayoutRect,
         gradient: di::RadialGradient,
         tile_size: LayoutSize,
         tile_spacing: LayoutSize,
     ) {
-        let item = di::SpecificDisplayItem::RadialGradient(di::RadialGradientDisplayItem {
+        let item = di::DisplayItem::RadialGradient(di::RadialGradientDisplayItem {
+            common: *common,
+            bounds,
             gradient,
             tile_size,
             tile_spacing,
         });
 
-        self.push_item(&item, layout, space_and_clip);
+        self.push_item(&item);
     }
 
     pub fn push_reference_frame(
         &mut self,
-        rect: &LayoutRect,
-        parent: di::SpatialId,
+        origin: LayoutPoint,
+        parent_spatial_id: di::SpatialId,
         transform_style: di::TransformStyle,
         transform: PropertyBinding<LayoutTransform>,
         kind: di::ReferenceFrameKind,
     ) -> di::SpatialId {
         let id = self.generate_spatial_index();
 
-        let item = di::SpecificDisplayItem::PushReferenceFrame(di::ReferenceFrameDisplayListItem {
+        let item = di::DisplayItem::PushReferenceFrame(di::ReferenceFrameDisplayListItem {
+            parent_spatial_id,
+            origin,
             reference_frame: di::ReferenceFrame {
                 transform_style,
                 transform,
                 kind,
                 id,
             },
         });
 
-        let layout = di::LayoutPrimitiveInfo::new(*rect);
-        self.push_item(&item, &layout, &di::SpaceAndClipInfo {
-            spatial_id: parent,
-            clip_id: di::ClipId::invalid(),
-        });
+        self.push_item(&item);
         id
     }
 
     pub fn pop_reference_frame(&mut self) {
-        self.push_new_empty_item(&di::SpecificDisplayItem::PopReferenceFrame);
+        self.push_item(&di::DisplayItem::PopReferenceFrame);
     }
 
     pub fn push_stacking_context(
         &mut self,
-        layout: &di::LayoutPrimitiveInfo,
+        origin: LayoutPoint,
         spatial_id: di::SpatialId,
+        is_backface_visible: bool,
         clip_id: Option<di::ClipId>,
         transform_style: di::TransformStyle,
         mix_blend_mode: di::MixBlendMode,
         filters: &[di::FilterOp],
         filter_datas: &[di::FilterData],
         raster_space: di::RasterSpace,
         cache_tiles: bool,
     ) {
-
         if filters.len() > 0 {
-            self.push_new_empty_item(&di::SpecificDisplayItem::SetFilterOps);
+            self.push_item(&di::DisplayItem::SetFilterOps);
             self.push_iter(filters);
         }
 
         for filter_data in filter_datas {
             let func_types = [
                 filter_data.func_r_type, filter_data.func_g_type,
                 filter_data.func_b_type, filter_data.func_a_type];
-            self.push_new_empty_item(&di::SpecificDisplayItem::SetFilterData);
+            self.push_item(&di::DisplayItem::SetFilterData);
             self.push_iter(&func_types);
             self.push_iter(&filter_data.r_values);
             self.push_iter(&filter_data.g_values);
             self.push_iter(&filter_data.b_values);
             self.push_iter(&filter_data.a_values);
         }
 
-        let item = di::SpecificDisplayItem::PushStackingContext(di::PushStackingContextDisplayItem {
+        let item = di::DisplayItem::PushStackingContext(di::PushStackingContextDisplayItem {
+            origin,
+            spatial_id,
+            is_backface_visible,
             stacking_context: di::StackingContext {
                 transform_style,
                 mix_blend_mode,
                 clip_id,
                 raster_space,
                 cache_tiles,
             },
         });
 
-        self.push_item(&item, layout, &di::SpaceAndClipInfo {
-            spatial_id,
-            clip_id: di::ClipId::invalid(),
-        });
+        self.push_item(&item);
     }
 
     /// Helper for examples/ code.
     pub fn push_simple_stacking_context(
         &mut self,
-        layout: &di::LayoutPrimitiveInfo,
+        origin: LayoutPoint,
         spatial_id: di::SpatialId,
+        is_backface_visible: bool,
     ) {
-        self.push_simple_stacking_context_with_filters(layout, spatial_id, &[], &[]);
+        self.push_simple_stacking_context_with_filters(origin, spatial_id, is_backface_visible, &[], &[]);
     }
 
     /// Helper for examples/ code.
     pub fn push_simple_stacking_context_with_filters(
         &mut self,
-        layout: &di::LayoutPrimitiveInfo,
+        origin: LayoutPoint,
         spatial_id: di::SpatialId,
+        is_backface_visible: bool,
         filters: &[di::FilterOp],
         filter_datas: &[di::FilterData],
     ) {
         self.push_stacking_context(
-            layout,
+            origin,
             spatial_id,
+            is_backface_visible,
             None,
             di::TransformStyle::Flat,
             di::MixBlendMode::Normal,
             filters,
             filter_datas,
             di::RasterSpace::Screen,
             /* cache_tiles = */ false,
         );
     }
 
     pub fn pop_stacking_context(&mut self) {
-        self.push_new_empty_item(&di::SpecificDisplayItem::PopStackingContext);
+        self.push_item(&di::DisplayItem::PopStackingContext);
     }
 
     pub fn push_stops(&mut self, stops: &[di::GradientStop]) {
         if stops.is_empty() {
             return;
         }
-        self.push_new_empty_item(&di::SpecificDisplayItem::SetGradientStops);
+        self.push_item(&di::DisplayItem::SetGradientStops);
         self.push_iter(stops);
     }
 
     fn generate_clip_index(&mut self) -> di::ClipId {
         self.next_clip_index += 1;
         di::ClipId::Clip(self.next_clip_index - 1, self.pipeline_id)
     }
 
@@ -1564,30 +1529,29 @@ impl DisplayListBuilder {
         external_scroll_offset: LayoutVector2D,
     ) -> di::SpaceAndClipInfo
     where
         I: IntoIterator<Item = di::ComplexClipRegion>,
         I::IntoIter: ExactSizeIterator + Clone,
     {
         let clip_id = self.generate_clip_index();
         let scroll_frame_id = self.generate_spatial_index();
-        let item = di::SpecificDisplayItem::ScrollFrame(di::ScrollFrameDisplayItem {
+        let item = di::DisplayItem::ScrollFrame(di::ScrollFrameDisplayItem {
+            content_rect,
+            clip_rect,
+            parent_space_and_clip: *parent_space_and_clip,
             clip_id,
             scroll_frame_id,
             external_id,
             image_mask,
             scroll_sensitivity,
             external_scroll_offset,
         });
 
-        self.push_item(
-            &item,
-            &di::LayoutPrimitiveInfo::with_clip_rect(content_rect, clip_rect),
-            parent_space_and_clip,
-        );
+        self.push_item(&item);
         self.push_iter(complex_clips);
 
         di::SpaceAndClipInfo {
             spatial_id: scroll_frame_id,
             clip_id,
         }
     }
 
@@ -1596,101 +1560,101 @@ impl DisplayListBuilder {
         parent: Option<di::ClipChainId>,
         clips: I,
     ) -> di::ClipChainId
     where
         I: IntoIterator<Item = di::ClipId>,
         I::IntoIter: ExactSizeIterator + Clone,
     {
         let id = self.generate_clip_chain_id();
-        self.push_new_empty_item(&di::SpecificDisplayItem::ClipChain(di::ClipChainItem { id, parent }));
+        self.push_item(&di::DisplayItem::ClipChain(di::ClipChainItem { id, parent }));
         self.push_iter(clips);
         id
     }
 
     pub fn define_clip<I>(
         &mut self,
         parent_space_and_clip: &di::SpaceAndClipInfo,
         clip_rect: LayoutRect,
         complex_clips: I,
         image_mask: Option<di::ImageMask>,
     ) -> di::ClipId
     where
         I: IntoIterator<Item = di::ComplexClipRegion>,
         I::IntoIter: ExactSizeIterator + Clone,
     {
         let id = self.generate_clip_index();
-        let item = di::SpecificDisplayItem::Clip(di::ClipDisplayItem {
+        let item = di::DisplayItem::Clip(di::ClipDisplayItem {
             id,
+            parent_space_and_clip: *parent_space_and_clip,
+            clip_rect,
             image_mask,
         });
 
-        self.push_item(
-            &item,
-            &di::LayoutPrimitiveInfo::new(clip_rect),
-            parent_space_and_clip,
-        );
+        self.push_item(&item);
         self.push_iter(complex_clips);
         id
     }
 
     pub fn define_sticky_frame(
         &mut self,
         parent_spatial_id: di::SpatialId,
         frame_rect: LayoutRect,
         margins: SideOffsets2D<Option<f32>>,
         vertical_offset_bounds: di::StickyOffsetBounds,
         horizontal_offset_bounds: di::StickyOffsetBounds,
         previously_applied_offset: LayoutVector2D,
     ) -> di::SpatialId {
         let id = self.generate_spatial_index();
-        let item = di::SpecificDisplayItem::StickyFrame(di::StickyFrameDisplayItem {
+        let item = di::DisplayItem::StickyFrame(di::StickyFrameDisplayItem {
+            parent_spatial_id,
             id,
+            bounds: frame_rect,
             margins,
             vertical_offset_bounds,
             horizontal_offset_bounds,
             previously_applied_offset,
         });
 
-        self.push_item(
-            &item,
-            &di::LayoutPrimitiveInfo::new(frame_rect),
-            &di::SpaceAndClipInfo {
-                spatial_id: parent_spatial_id,
-                clip_id: di::ClipId::invalid(),
-            },
-        );
+        self.push_item(&item);
         id
     }
 
     pub fn push_iframe(
         &mut self,
-        layout: &di::LayoutPrimitiveInfo,
+        bounds: LayoutRect,
+        clip_rect: LayoutRect,
         space_and_clip: &di::SpaceAndClipInfo,
         pipeline_id: PipelineId,
         ignore_missing_pipeline: bool
     ) {
-        let item = di::SpecificDisplayItem::Iframe(di::IframeDisplayItem {
+        let item = di::DisplayItem::Iframe(di::IframeDisplayItem {
+            bounds,
+            clip_rect,
+            space_and_clip: *space_and_clip,
             pipeline_id,
             ignore_missing_pipeline,
         });
-        self.push_item(&item, layout, space_and_clip);
+        self.push_item(&item);
     }
 
     pub fn push_shadow(
         &mut self,
-        layout: &di::LayoutPrimitiveInfo,
         space_and_clip: &di::SpaceAndClipInfo,
         shadow: di::Shadow,
     ) {
-        self.push_item(&di::SpecificDisplayItem::PushShadow(shadow), layout, space_and_clip);
+        let item = di::DisplayItem::PushShadow(di::PushShadowDisplayItem {
+            space_and_clip: *space_and_clip,
+            shadow,
+        });
+        self.push_item(&item);
     }
 
     pub fn pop_all_shadows(&mut self) {
-        self.push_new_empty_item(&di::SpecificDisplayItem::PopAllShadows);
+        self.push_item(&di::DisplayItem::PopAllShadows);
     }
 
     pub fn finalize(self) -> (PipelineId, LayoutSize, BuiltDisplayList) {
         assert!(self.save_state.is_none(), "Finalized DisplayListBuilder with a pending save");
 
         let end_time = precise_time_ns();
 
         (
--- a/gfx/wr/wrench/src/rawtest.rs
+++ b/gfx/wr/wrench/src/rawtest.rs
@@ -73,16 +73,42 @@ impl<'a> RawtestHarness<'a> {
             false,
         );
         epoch.0 += 1;
 
         txn.generate_frame();
         self.wrench.api.send_transaction(self.wrench.document_id, txn);
     }
 
+    fn make_common_properties(&self, clip_rect: LayoutRect) -> CommonItemProperties {
+        let space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id);
+        CommonItemProperties {
+            clip_rect,
+            clip_id: space_and_clip.clip_id,
+            spatial_id: space_and_clip.spatial_id,
+            is_backface_visible: true,
+            hit_info: None,
+        }
+    }
+
+    fn make_common_properties_with_clip_and_spatial(
+        &self,
+        clip_rect: LayoutRect,
+        clip_id: ClipId,
+        spatial_id: SpatialId
+    ) -> CommonItemProperties {
+        CommonItemProperties {
+            clip_rect,
+            clip_id,
+            spatial_id,
+            is_backface_visible: true,
+            hit_info: None,
+        }
+    }
+
     fn test_resize_image(&mut self) {
         println!("\tresize image...");
         // This test changes the size of an image to make it go switch back and forth
         // between tiled and non-tiled.
         // The resource cache should be able to handle this without crashing.
 
         let layout_size = LayoutSize::new(800., 800.);
 
@@ -93,21 +119,21 @@ impl<'a> RawtestHarness<'a> {
         txn.add_image(
             img,
             ImageDescriptor::new(64, 64, ImageFormat::BGRA8, true, false),
             ImageData::new(vec![255; 64 * 64 * 4]),
             None,
         );
 
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
-        let info = LayoutPrimitiveInfo::new(rect(0.0, 0.0, 64.0, 64.0));
+        let info = self.make_common_properties(rect(0.0, 0.0, 64.0, 64.0));
 
         builder.push_image(
             &info,
-            &SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id),
+            info.clip_rect,
             size2(64.0, 64.0),
             size2(64.0, 64.0),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             img,
             ColorF::WHITE,
         );
 
@@ -122,21 +148,21 @@ impl<'a> RawtestHarness<'a> {
         txn.update_image(
             img,
             ImageDescriptor::new(8200, 32, ImageFormat::BGRA8, true, false),
             ImageData::new(vec![255; 8200 * 32 * 4]),
             &DirtyRect::All,
         );
 
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
-        let info = LayoutPrimitiveInfo::new(rect(0.0, 0.0, 1024.0, 1024.0));
+        let info = self.make_common_properties(rect(0.0, 0.0, 1024.0, 1024.0));
 
         builder.push_image(
             &info,
-            &SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id),
+            info.clip_rect,
             size2(1024.0, 1024.0),
             size2(1024.0, 1024.0),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             img,
             ColorF::WHITE,
         );
 
@@ -148,21 +174,21 @@ impl<'a> RawtestHarness<'a> {
         txn.update_image(
             img,
             ImageDescriptor::new(64, 64, ImageFormat::BGRA8, true, false),
             ImageData::new(vec![64; 64 * 64 * 4]),
             &DirtyRect::All,
         );
 
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
-        let info = LayoutPrimitiveInfo::new(rect(0.0, 0.0, 1024.0, 1024.0));
+        let info = self.make_common_properties(rect(0.0, 0.0, 1024.0, 1024.0));
 
         builder.push_image(
             &info,
-            &SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id),
+            info.clip_rect,
             size2(1024.0, 1024.0),
             size2(1024.0, 1024.0),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             img,
             ColorF::WHITE,
         );
 
@@ -186,22 +212,22 @@ impl<'a> RawtestHarness<'a> {
             blob_img,
             ImageDescriptor::new(151, 56, ImageFormat::BGRA8, true, false),
             blob::serialize_blob(ColorU::new(50, 50, 150, 255)),
             Some(128),
         );
 
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
 
-        let info = LayoutPrimitiveInfo::new(rect(448.899994, 74.0, 151.000031, 56.));
+        let info = self.make_common_properties(rect(448.899994, 74.0, 151.000031, 56.));
 
         // setup some malicious image size parameters
         builder.push_image(
             &info,
-            &SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id),
+            info.clip_rect,
             size2(151., 56.0),
             size2(151.0, 56.0),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img.as_image(),
             ColorF::WHITE,
         );
 
@@ -249,32 +275,38 @@ impl<'a> RawtestHarness<'a> {
         let called_inner = Arc::clone(&called);
 
         self.wrench.callbacks.lock().unwrap().request = Box::new(move |_| {
             called_inner.fetch_add(1, Ordering::SeqCst);
         });
 
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
 
-        let info = LayoutPrimitiveInfo::new(rect(0., -9600.0, 1510.000031, 111256.));
-
         let image_size = size2(1510., 111256.);
 
         let root_space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id);
         let clip_id = builder.define_clip(
             &root_space_and_clip,
             rect(40., 41., 200., 201.),
             vec![],
             None,
         );
 
+        let info = CommonItemProperties {
+            clip_rect: rect(0., -9600.0, 1510.000031, 111256.),
+            clip_id,
+            spatial_id: root_space_and_clip.spatial_id,
+            is_backface_visible: true,
+            hit_info: None,
+        };
+
         // setup some malicious image size parameters
         builder.push_image(
             &info,
-            &SpaceAndClipInfo { clip_id, spatial_id: root_space_and_clip.spatial_id },
+            info.clip_rect,
             image_size * 2.,
             image_size,
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img.as_image(),
             ColorF::WHITE,
         );
         txn.set_blob_image_visible_area(
@@ -347,18 +379,17 @@ impl<'a> RawtestHarness<'a> {
         let window_size = self.window.get_inner_size();
         let test_size = FramebufferIntSize::new(800, 800);
         let window_rect = FramebufferIntRect::new(
             point2(0, window_size.height - test_size.height),
             test_size,
         );
         let layout_size = LayoutSize::new(800.0, 800.0);
         let image_size = size2(800.0, 800.0);
-        let info = LayoutPrimitiveInfo::new(rect(0.0, 0.0, 800.0, 800.0));
-        let space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id);
+        let info = self.make_common_properties(rect(0.0, 0.0, 800.0, 800.0));
 
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
         let mut txn = Transaction::new();
 
         let blob_img1 = self.wrench.api.generate_blob_image_key();
         txn.add_blob_image(
             blob_img1,
             ImageDescriptor::new(
@@ -369,17 +400,17 @@ impl<'a> RawtestHarness<'a> {
                 false
             ),
             blob::serialize_blob(ColorU::new(50, 50, 150, 255)),
             Some(100),
         );
 
         builder.push_image(
             &info,
-            &space_and_clip,
+            info.clip_rect,
             image_size,
             image_size,
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img1.as_image(),
             ColorF::WHITE,
         );
 
@@ -406,17 +437,17 @@ impl<'a> RawtestHarness<'a> {
         // This will force sync rasterization of the missing tiles during frame building.
         txn.set_blob_image_visible_area(blob_img2, DeviceIntRect {
             origin: point2(200, 200),
             size: size2(80, 80),
         });
 
         builder.push_image(
             &info,
-            &space_and_clip,
+            info.clip_rect,
             image_size,
             image_size,
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img2.as_image(),
             ColorF::WHITE,
         );
 
@@ -438,40 +469,39 @@ impl<'a> RawtestHarness<'a> {
 
         let window_size = self.window.get_inner_size();
 
         let test_size = FramebufferIntSize::new(800, 800);
         let window_rect = FramebufferIntRect::new(
             point2(0, window_size.height - test_size.height),
             test_size,
         );
-        let space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id);
 
         // This exposes a crash in tile decomposition
         let mut txn = Transaction::new();
         let layout_size = LayoutSize::new(800., 800.);
 
         let blob_img = self.wrench.api.generate_blob_image_key();
         txn.add_blob_image(
             blob_img,
             ImageDescriptor::new(1510, 1510, ImageFormat::BGRA8, false, false),
             blob::serialize_blob(ColorU::new(50, 50, 150, 255)),
             None,
         );
 
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
 
-        let info = LayoutPrimitiveInfo::new(rect(0., 0.0, 1510., 1510.));
+        let info = self.make_common_properties(rect(0., 0.0, 1510., 1510.));
 
         let image_size = size2(1510., 1510.);
 
         // setup some malicious image size parameters
         builder.push_image(
             &info,
-            &space_and_clip,
+            info.clip_rect,
             image_size,
             image_size,
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img.as_image(),
             ColorF::WHITE,
         );
 
@@ -480,24 +510,24 @@ impl<'a> RawtestHarness<'a> {
         self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
 
         let original_pixels = self.render_and_get_pixels(window_rect);
 
         let mut epoch = Epoch(1);
 
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
 
-        let info = LayoutPrimitiveInfo::new(rect(-10000., 0.0, 1510., 1510.));
+        let info = self.make_common_properties(rect(-10000., 0.0, 1510., 1510.));
 
         let image_size = size2(1510., 1510.);
 
         // setup some malicious image size parameters
         builder.push_image(
             &info,
-            &space_and_clip,
+            info.clip_rect,
             image_size,
             image_size,
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img.as_image(),
             ColorF::WHITE,
         );
 
@@ -511,24 +541,24 @@ impl<'a> RawtestHarness<'a> {
             blob_img,
             ImageDescriptor::new(1510, 1510, ImageFormat::BGRA8, false, false),
             blob::serialize_blob(ColorU::new(50, 50, 150, 255)),
             &rect(10, 10, 100, 100).into(),
         );
 
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
 
-        let info = LayoutPrimitiveInfo::new(rect(0., 0.0, 1510., 1510.));
+        let info = self.make_common_properties(rect(0., 0.0, 1510., 1510.));
 
         let image_size = size2(1510., 1510.);
 
         // setup some malicious image size parameters
         builder.push_image(
             &info,
-            &space_and_clip,
+            info.clip_rect,
             image_size,
             image_size,
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img.as_image(),
             ColorF::WHITE,
         );
 
@@ -553,17 +583,16 @@ impl<'a> RawtestHarness<'a> {
         let window_size = self.window.get_inner_size();
 
         let test_size = FramebufferIntSize::new(400, 400);
         let window_rect = FramebufferIntRect::new(
             FramebufferIntPoint::new(0, window_size.height - test_size.height),
             test_size,
         );
         let layout_size = LayoutSize::new(400., 400.);
-        let space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id);
 
         let mut txn = Transaction::new();
         {
             let api = &self.wrench.api;
 
             blob_img = api.generate_blob_image_key();
             txn.add_blob_image(
                 blob_img,
@@ -577,21 +606,21 @@ impl<'a> RawtestHarness<'a> {
         let called_inner = Arc::clone(&called);
 
         self.wrench.callbacks.lock().unwrap().request = Box::new(move |_| {
             assert_eq!(0, called_inner.fetch_add(1, Ordering::SeqCst));
         });
 
         // draw the blob the first time
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
-        let info = LayoutPrimitiveInfo::new(rect(0.0, 60.0, 200.0, 200.0));
+        let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0));
 
         builder.push_image(
             &info,
-            &space_and_clip,
+            info.clip_rect,
             size2(200.0, 200.0),
             size2(0.0, 0.0),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img.as_image(),
             ColorF::WHITE,
         );
 
@@ -602,20 +631,20 @@ impl<'a> RawtestHarness<'a> {
         let pixels_first = self.render_and_get_pixels(window_rect);
 
         assert_eq!(1, called.load(Ordering::SeqCst));
 
         // draw the blob image a second time at a different location
 
         // make a new display list that refers to the first image
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
-        let info = LayoutPrimitiveInfo::new(rect(1.0, 60.0, 200.0, 200.0));
+        let info = self.make_common_properties(rect(1.0, 60.0, 200.0, 200.0));
         builder.push_image(
             &info,
-            &space_and_clip,
+            info.clip_rect,
             size2(200.0, 200.0),
             size2(0.0, 0.0),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img.as_image(),
             ColorF::WHITE,
         );
 
@@ -644,17 +673,16 @@ impl<'a> RawtestHarness<'a> {
         let window_size = self.window.get_inner_size();
 
         let test_size = FramebufferIntSize::new(400, 400);
         let window_rect = FramebufferIntRect::new(
             point2(0, window_size.height - test_size.height),
             test_size,
         );
         let layout_size = LayoutSize::new(400., 400.);
-        let space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id);
 
         let mut txn = Transaction::new();
         let (blob_img, blob_img2) = {
             let api = &self.wrench.api;
 
             blob_img = api.generate_blob_image_key();
             txn.add_blob_image(
                 blob_img,
@@ -687,32 +715,32 @@ impl<'a> RawtestHarness<'a> {
                 if item.request.key == blob_img2 {
                     img2_requested_inner.fetch_add(1, Ordering::SeqCst);
                 }
             }
         });
 
         // create two blob images and draw them
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
-        let info = LayoutPrimitiveInfo::new(rect(0.0, 60.0, 200.0, 200.0));
-        let info2 = LayoutPrimitiveInfo::new(rect(200.0, 60.0, 200.0, 200.0));
+        let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0));
+        let info2 = self.make_common_properties(rect(200.0, 60.0, 200.0, 200.0));
         let push_images = |builder: &mut DisplayListBuilder| {
             builder.push_image(
                 &info,
-                &space_and_clip,
+                info.clip_rect,
                 size2(200.0, 200.0),
                 size2(0.0, 0.0),
                 ImageRendering::Auto,
                 AlphaType::PremultipliedAlpha,
                 blob_img.as_image(),
                 ColorF::WHITE,
             );
             builder.push_image(
                 &info2,
-                &space_and_clip,
+                info2.clip_rect,
                 size2(200.0, 200.0),
                 size2(0.0, 0.0),
                 ImageRendering::Auto,
                 AlphaType::PremultipliedAlpha,
                 blob_img2.as_image(),
                 ColorF::WHITE,
             );
         };
@@ -774,37 +802,36 @@ impl<'a> RawtestHarness<'a> {
         let window_size = self.window.get_inner_size();
 
         let test_size = FramebufferIntSize::new(400, 400);
         let window_rect = FramebufferIntRect::new(
             point2(0, window_size.height - test_size.height),
             test_size,
         );
         let layout_size = LayoutSize::new(400., 400.);
-        let space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id);
         let mut txn = Transaction::new();
 
         let blob_img = {
             let img = self.wrench.api.generate_blob_image_key();
             txn.add_blob_image(
                 img,
                 ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
                 blob::serialize_blob(ColorU::new(50, 50, 150, 255)),
                 None,
             );
             img
         };
 
         // draw the blobs the first time
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
-        let info = LayoutPrimitiveInfo::new(rect(0.0, 60.0, 200.0, 200.0));
+        let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0));
 
         builder.push_image(
             &info,
-            &space_and_clip,
+            info.clip_rect,
             size2(200.0, 200.0),
             size2(0.0, 0.0),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img.as_image(),
             ColorF::WHITE,
         );
 
@@ -819,20 +846,20 @@ impl<'a> RawtestHarness<'a> {
             blob_img,
             ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
             blob::serialize_blob(ColorU::new(50, 50, 150, 255)),
             &rect(100, 100, 100, 100).into(),
         );
 
         // make a new display list that refers to the first image
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
-        let info = LayoutPrimitiveInfo::new(rect(0.0, 60.0, 200.0, 200.0));
+        let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0));
         builder.push_image(
             &info,
-            &space_and_clip,
+            info.clip_rect,
             size2(200.0, 200.0),
             size2(0.0, 0.0),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img.as_image(),
             ColorF::WHITE,
         );
 
@@ -845,20 +872,20 @@ impl<'a> RawtestHarness<'a> {
             blob_img,
             ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
             blob::serialize_blob(ColorU::new(50, 150, 150, 255)),
             &rect(200, 200, 100, 100).into(),
         );
 
         // make a new display list that refers to the first image
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
-        let info = LayoutPrimitiveInfo::new(rect(0.0, 60.0, 200.0, 200.0));
+        let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0));
         builder.push_image(
             &info,
-            &space_and_clip,
+            info.clip_rect,
             size2(200.0, 200.0),
             size2(0.0, 0.0),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img.as_image(),
             ColorF::WHITE,
         );
 
@@ -887,69 +914,81 @@ impl<'a> RawtestHarness<'a> {
             let spatial_id = SpatialId::root_scroll_node(self.wrench.root_pipeline_id);
             let clip_id = builder.define_clip(
                 &SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id),
                 rect(110., 120., 200., 200.),
                 None::<ComplexClipRegion>,
                 None
             );
             builder.push_rect(
-                &PrimitiveInfo::new(rect(100., 100., 100., 100.)),
-                &SpaceAndClipInfo { spatial_id, clip_id },
+                &self.make_common_properties_with_clip_and_spatial(
+                    rect(100., 100., 100., 100.),
+                    clip_id,
+                    spatial_id),
                 ColorF::new(0.0, 0.0, 1.0, 1.0),
             );
 
             if should_try_and_fail {
                 builder.save();
                 let clip_id = builder.define_clip(
                     &SpaceAndClipInfo { spatial_id, clip_id },
                     rect(80., 80., 90., 90.),
                     None::<ComplexClipRegion>,
                     None
                 );
                 let space_and_clip = SpaceAndClipInfo {
                     spatial_id,
                     clip_id
                 };
                 builder.push_rect(
-                    &PrimitiveInfo::new(rect(110., 110., 50., 50.)),
-                    &space_and_clip,
+                    &self.make_common_properties_with_clip_and_spatial(
+                        rect(110., 110., 50., 50.),
+                        clip_id,
+                        spatial_id),
                     ColorF::new(0.0, 1.0, 0.0, 1.0),
                 );
                 builder.push_shadow(
-                    &PrimitiveInfo::new(rect(100., 100., 100., 100.)),
                     &space_and_clip,
                     Shadow {
                         offset: LayoutVector2D::new(1.0, 1.0),
                         blur_radius: 1.0,
                         color: ColorF::new(0.0, 0.0, 0.0, 1.0),
                         should_inflate: true,
                     },
                 );
+                let info = CommonItemProperties {
+                    clip_rect: rect(110., 110., 50., 2.),
+                    clip_id,
+                    spatial_id,
+                    is_backface_visible: true,
+                    hit_info: None,
+                };
                 builder.push_line(
-                    &PrimitiveInfo::new(rect(110., 110., 50., 2.)),
-                    &space_and_clip,
+                    &info,
+                    &info.clip_rect,
                     0.0, LineOrientation::Horizontal,
                     &ColorF::new(0.0, 0.0, 0.0, 1.0),
                     LineStyle::Solid,
                 );
                 builder.restore();
             }
 
             {
                 builder.save();
                 let clip_id = builder.define_clip(
                     &SpaceAndClipInfo { spatial_id, clip_id },
                     rect(80., 80., 100., 100.),
                     None::<ComplexClipRegion>,
                     None
                 );
                 builder.push_rect(
-                    &PrimitiveInfo::new(rect(150., 150., 100., 100.)),
-                    &SpaceAndClipInfo { spatial_id, clip_id },
+                    &self.make_common_properties_with_clip_and_spatial(
+                        rect(150., 150., 100., 100.),
+                        clip_id,
+                        spatial_id),
                     ColorF::new(0.0, 0.0, 1.0, 1.0),
                 );
                 builder.clear_save();
             }
 
             let txn = Transaction::new();
 
             self.submit_dl(&mut Epoch(0), layout_size, builder, &txn.resource_updates);
@@ -970,39 +1009,38 @@ impl<'a> RawtestHarness<'a> {
         let window_size = self.window.get_inner_size();
 
         let test_size = FramebufferIntSize::new(400, 400);
         let window_rect = FramebufferIntRect::new(
             point2(0, window_size.height - test_size.height),
             test_size,
         );
         let layout_size = LayoutSize::new(400., 400.);
-        let space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id);
 
         let mut do_test = |shadow_is_red| {
             let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
             let shadow_color = if shadow_is_red {
                 ColorF::new(1.0, 0.0, 0.0, 1.0)
             } else {
                 ColorF::new(0.0, 1.0, 0.0, 1.0)
             };
 
             builder.push_shadow(
-                &PrimitiveInfo::new(rect(100., 100., 100., 100.)),
-                &space_and_clip,
+                &SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id),
                 Shadow {
                     offset: LayoutVector2D::new(1.0, 1.0),
                     blur_radius: 1.0,
                     color: shadow_color,
                     should_inflate: true,
                 },
             );
+            let info = self.make_common_properties(rect(110., 110., 50., 2.));
             builder.push_line(
-                &PrimitiveInfo::new(rect(110., 110., 50., 2.)),
-                &space_and_clip,
+                &info,
+                &info.clip_rect,
                 0.0, LineOrientation::Horizontal,
                 &ColorF::new(0.0, 0.0, 0.0, 1.0),
                 LineStyle::Solid,
             );
             builder.pop_all_shadows();
 
             let txn = Transaction::new();
             self.submit_dl(&mut Epoch(0), layout_size, builder, &txn.resource_updates);
@@ -1034,19 +1072,20 @@ impl<'a> RawtestHarness<'a> {
             image,
             ImageDescriptor::new(1, 1, ImageFormat::BGRA8, true, false),
             ImageData::new(vec![0xFF, 0, 0, 0xFF]),
             None,
         );
 
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
 
+        let info = self.make_common_properties(rect(300.0, 70.0, 150.0, 50.0));
         builder.push_image(
-            &LayoutPrimitiveInfo::new(rect(300.0, 70.0, 150.0, 50.0)),
-            &SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id),
+            &info,
+            info.clip_rect,
             size2(150.0, 50.0),
             size2(0.0, 0.0),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             image,
             ColorF::WHITE,
         );
 
@@ -1103,21 +1142,20 @@ impl<'a> RawtestHarness<'a> {
     fn test_zero_height_window(&mut self) {
         println!("\tzero height test...");
 
         let layout_size = LayoutSize::new(120.0, 0.0);
         let window_size = FramebufferIntSize::new(layout_size.width as i32, layout_size.height as i32);
         let doc_id = self.wrench.api.add_document(window_size, 1);
 
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
-        let info = LayoutPrimitiveInfo::new(LayoutRect::new(LayoutPoint::zero(),
+        let info = self.make_common_properties(LayoutRect::new(LayoutPoint::zero(),
                                                             LayoutSize::new(100.0, 100.0)));
         builder.push_rect(
             &info,
-            &SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id),
             ColorF::new(0.0, 1.0, 0.0, 1.0),
         );
 
         let mut txn = Transaction::new();
         txn.set_root_pipeline(self.wrench.root_pipeline_id);
         txn.set_display_list(
             Epoch(1),
             Some(ColorF::new(1.0, 0.0, 0.0, 1.0)),
@@ -1135,30 +1173,30 @@ impl<'a> RawtestHarness<'a> {
 
 
     fn test_hit_testing(&mut self) {
         println!("\thit testing test...");
 
         let layout_size = LayoutSize::new(400., 400.);
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
 
-        let space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id);
-
         // Add a rectangle that covers the entire scene.
-        let mut info = LayoutPrimitiveInfo::new(LayoutRect::new(LayoutPoint::zero(), layout_size));
-        info.tag = Some((0, 1));
-        builder.push_rect(&info, &space_and_clip, ColorF::new(1.0, 1.0, 1.0, 1.0));
+        let mut info = self.make_common_properties(LayoutRect::new(LayoutPoint::zero(), layout_size));
+        info.hit_info = Some((0, 1));
+        builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 1.0));
 
         // Add a simple 100x100 rectangle at 100,0.
-        let mut info = LayoutPrimitiveInfo::new(LayoutRect::new(
+        let mut info = self.make_common_properties(LayoutRect::new(
             LayoutPoint::new(100., 0.),
             LayoutSize::new(100., 100.)
         ));
-        info.tag = Some((0, 2));
-        builder.push_rect(&info, &space_and_clip, ColorF::new(1.0, 1.0, 1.0, 1.0));
+        info.hit_info = Some((0, 2));
+        builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 1.0));
+
+        let space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id);
 
         let make_rounded_complex_clip = |rect: &LayoutRect, radius: f32| -> ComplexClipRegion {
             ComplexClipRegion::new(
                 *rect,
                 BorderRadius::uniform_size(LayoutSize::new(radius, radius)),
                 ClipMode::Clip
             )
         };
@@ -1167,44 +1205,42 @@ impl<'a> RawtestHarness<'a> {
         let rect = LayoutRect::new(LayoutPoint::new(100., 100.), LayoutSize::new(100., 100.));
         let temp_clip_id = builder.define_clip(
             &space_and_clip,
             rect,
             vec![make_rounded_complex_clip(&rect, 20.)],
             None,
         );
         builder.push_rect(
-            &LayoutPrimitiveInfo {
-                tag: Some((0, 4)),
-                .. LayoutPrimitiveInfo::new(rect)
-            },
-            &SpaceAndClipInfo {
+            &CommonItemProperties {
+                hit_info: Some((0, 4)),
+                clip_rect: rect,
                 clip_id: temp_clip_id,
                 spatial_id: space_and_clip.spatial_id,
+                is_backface_visible: true,
             },
             ColorF::new(1.0, 1.0, 1.0, 1.0),
         );
 
         // Add a rectangle that is clipped by a ClipChain containing a rounded rect.
         let rect = LayoutRect::new(LayoutPoint::new(200., 100.), LayoutSize::new(100., 100.));
         let clip_id = builder.define_clip(
             &space_and_clip,
             rect,
             vec![make_rounded_complex_clip(&rect, 20.)],
             None,
         );
         let clip_chain_id = builder.define_clip_chain(None, vec![clip_id]);
         builder.push_rect(
-            &LayoutPrimitiveInfo {
-                tag: Some((0, 5)),
-                .. LayoutPrimitiveInfo::new(rect)
-            },
-            &SpaceAndClipInfo {
+            &CommonItemProperties {
+                hit_info: Some((0, 5)),
+                clip_rect: rect,
                 clip_id: ClipId::ClipChain(clip_chain_id),
                 spatial_id: space_and_clip.spatial_id,
+                is_backface_visible: true,
             },
             ColorF::new(1.0, 1.0, 1.0, 1.0),
         );
 
         let mut epoch = Epoch(0);
         let txn = Transaction::new();
         self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
 
--- a/gfx/wr/wrench/src/yaml_frame_reader.rs
+++ b/gfx/wr/wrench/src/yaml_frame_reader.rs
@@ -15,16 +15,27 @@ use std::path::{Path, PathBuf};
 use std::usize;
 use webrender::api::*;
 use webrender::api::units::*;
 use wrench::{FontDescriptor, Wrench, WrenchThing};
 use yaml_helper::{StringEnum, YamlHelper, make_perspective};
 use yaml_rust::{Yaml, YamlLoader};
 use PLATFORM_DEFAULT_FACE_NAME;
 
+macro_rules! try_intersect {
+    ($first: expr, $second: expr) => {
+        if let Some(rect) = ($first).intersection($second) {
+            rect
+        } else {
+            warn!("skipping item with non-intersecting bounds and clip_rect");
+            return;
+        };
+    }
+}
+
 fn rsrc_path(item: &Yaml, aux_dir: &PathBuf) -> PathBuf {
     let filename = item.as_str().unwrap();
     let mut file = aux_dir.clone();
     file.push(filename);
     file
 }
 
 impl FontDescriptor {
@@ -346,17 +357,23 @@ impl YamlFrameReader {
         self.user_spatial_id_map.clear();
         self.clip_id_stack.clear();
         self.clip_id_stack.push(ClipId::root(pipeline_id));
         self.spatial_id_stack.clear();
         self.spatial_id_stack.push(SpatialId::root_scroll_node(pipeline_id));
 
         let content_size = self.get_root_size_from_yaml(wrench, yaml);
         let mut builder = DisplayListBuilder::new(pipeline_id, content_size);
-        let mut info = LayoutPrimitiveInfo::new(LayoutRect::zero());
+        let mut info = CommonItemProperties {
+            clip_rect: LayoutRect::zero(),
+            clip_id: ClipId::invalid(),
+            spatial_id: SpatialId::new(0, PipelineId::dummy()),
+            is_backface_visible: true,
+            hit_info: None,
+        };
         self.add_stacking_context_from_yaml(&mut builder, wrench, yaml, true, &mut info);
         self.display_lists.push(builder.finalize());
 
         assert_eq!(self.clip_id_stack.len(), 1);
         assert_eq!(self.spatial_id_stack.len(), 1);
     }
 
     fn to_complex_clip_region(&mut self, item: &Yaml) -> ComplexClipRegion {
@@ -761,45 +778,65 @@ impl YamlFrameReader {
 
         dl.create_radial_gradient(center, radius, stops, extend_mode)
     }
 
     fn handle_rect(
         &mut self,
         dl: &mut DisplayListBuilder,
         item: &Yaml,
-        info: &mut LayoutPrimitiveInfo,
+        info: &mut CommonItemProperties,
     ) {
         let bounds_key = if item["type"].is_badvalue() {
             "rect"
         } else {
             "bounds"
         };
-        info.rect = self.resolve_rect(&item[bounds_key]);
+
+        info.clip_rect = try_intersect!(
+            self.resolve_rect(&item[bounds_key]),
+            &info.clip_rect
+        );
+
         let color = self.resolve_colorf(&item["color"], ColorF::BLACK);
-        dl.push_rect(&info, &self.top_space_and_clip(), color);
+        dl.push_rect(&info, color);
     }
 
     fn handle_clear_rect(
         &mut self,
         dl: &mut DisplayListBuilder,
         item: &Yaml,
-        info: &mut LayoutPrimitiveInfo,
+        info: &mut CommonItemProperties,
     ) {
-        info.rect = item["bounds"]
-            .as_rect()
-            .expect("clear-rect type must have bounds");
-        dl.push_clear_rect(&info, &self.top_space_and_clip());
+        info.clip_rect = try_intersect!(
+            item["bounds"].as_rect().expect("clear-rect type must have bounds"),
+            &info.clip_rect
+        );
+        dl.push_clear_rect(&info);
+    }
+
+    fn handle_hit_test(
+        &mut self,
+        dl: &mut DisplayListBuilder,
+        item: &Yaml,
+        info: &mut CommonItemProperties,
+    ) {
+        info.clip_rect = try_intersect!(
+            item["bounds"].as_rect().expect("hit-test type must have bounds"),
+            &info.clip_rect
+        );
+
+        dl.push_hit_test(&info);
     }
 
     fn handle_line(
         &mut self,
         dl: &mut DisplayListBuilder,
         item: &Yaml,
-        info: &mut LayoutPrimitiveInfo,
+        info: &mut CommonItemProperties,
     ) {
         let color = item["color"].as_colorf().unwrap_or(ColorF::BLACK);
         let orientation = item["orientation"]
             .as_str()
             .and_then(LineOrientation::from_str)
             .expect("line must have orientation");
         let style = item["style"]
             .as_str()
@@ -807,120 +844,124 @@ impl YamlFrameReader {
             .expect("line must have style");
 
         let wavy_line_thickness = if let LineStyle::Wavy = style {
             item["thickness"].as_f32().expect("wavy lines must have a thickness")
         } else {
             0.0
         };
 
+        let area;
         if item["baseline"].is_badvalue() {
             let bounds_key = if item["type"].is_badvalue() {
                 "rect"
             } else {
                 "bounds"
             };
 
-            info.rect = item[bounds_key]
+            area = item[bounds_key]
                 .as_rect()
                 .expect("line type must have bounds");
         } else {
             // Legacy line representation
             let baseline = item["baseline"].as_f32().expect("line must have baseline");
             let start = item["start"].as_f32().expect("line must have start");
             let end = item["end"].as_f32().expect("line must have end");
             let width = item["width"].as_f32().expect("line must have width");
 
-            info.rect = match orientation {
+            area = match orientation {
                 LineOrientation::Horizontal => {
                     LayoutRect::new(LayoutPoint::new(start, baseline),
                                     LayoutSize::new(end - start, width))
                 }
                 LineOrientation::Vertical => {
                     LayoutRect::new(LayoutPoint::new(baseline, start),
                                     LayoutSize::new(width, end - start))
                 }
             };
         }
 
         dl.push_line(
             &info,
-            &self.top_space_and_clip(),
+            &area,
             wavy_line_thickness,
             orientation,
             &color,
             style,
         );
     }
 
     fn handle_gradient(
         &mut self,
         dl: &mut DisplayListBuilder,
         item: &Yaml,
-        info: &mut LayoutPrimitiveInfo,
+        info: &mut CommonItemProperties,
     ) {
         let bounds_key = if item["type"].is_badvalue() {
             "gradient"
         } else {
             "bounds"
         };
         let bounds = item[bounds_key]
             .as_rect()
             .expect("gradient must have bounds");
-        info.rect = bounds;
+
         let gradient = self.to_gradient(dl, item);
         let tile_size = item["tile-size"].as_size().unwrap_or(bounds.size);
         let tile_spacing = item["tile-spacing"].as_size().unwrap_or(LayoutSize::zero());
 
-        dl.push_gradient(&info, &self.top_space_and_clip(), gradient, tile_size, tile_spacing);
+        dl.push_gradient(
+            &info,
+            bounds,
+            gradient,
+            tile_size,
+            tile_spacing
+        );
     }
 
     fn handle_radial_gradient(
         &mut self,
         dl: &mut DisplayListBuilder,
         item: &Yaml,
-        info: &mut LayoutPrimitiveInfo,
+        info: &mut CommonItemProperties,
     ) {
         let bounds_key = if item["type"].is_badvalue() {
             "radial-gradient"
         } else {
             "bounds"
         };
         let bounds = item[bounds_key]
             .as_rect()
             .expect("radial gradient must have bounds");
-        info.rect = bounds;
         let gradient = self.to_radial_gradient(dl, item);
         let tile_size = item["tile-size"].as_size().unwrap_or(bounds.size);
         let tile_spacing = item["tile-spacing"].as_size().unwrap_or(LayoutSize::zero());
 
         dl.push_radial_gradient(
             &info,
-            &self.top_space_and_clip(),
+            bounds,
             gradient,
             tile_size,
             tile_spacing,
         );
     }
 
     fn handle_border(
         &mut self,
         dl: &mut DisplayListBuilder,
         wrench: &mut Wrench,
         item: &Yaml,
-        info: &mut LayoutPrimitiveInfo,
+        info: &mut CommonItemProperties,
     ) {
         let bounds_key = if item["type"].is_badvalue() {
             "border"
         } else {
             "bounds"
         };
-        info.rect = item[bounds_key]
-            .as_rect()
-            .expect("borders must have bounds");
+        let bounds = item[bounds_key].as_rect().expect("borders must have bounds");
         let widths = item["width"]
             .as_vec_f32()
             .expect("borders must have width(s)");
         let widths = broadcast(&widths, 4);
         let widths = LayoutSideOffsets::new(widths[0], widths[3], widths[2], widths[1]);
         let border_details = if let Some(border_type) = item["border-type"].as_str() {
             match border_type {
                 "normal" => {
@@ -979,20 +1020,20 @@ impl YamlFrameReader {
                         right,
                         radius,
                         do_aa,
                     }))
                 }
                 "image" | "gradient" | "radial-gradient" => {
                     let image_width = item["image-width"]
                         .as_i64()
-                        .unwrap_or(info.rect.size.width as i64);
+                        .unwrap_or(bounds.size.width as i64);
                     let image_height = item["image-height"]
                         .as_i64()
-                        .unwrap_or(info.rect.size.height as i64);
+                        .unwrap_or(bounds.size.height as i64);
                     let fill = item["fill"].as_bool().unwrap_or(false);
 
                     let slice = item["slice"].as_vec_u32();
                     let slice = match slice {
                         Some(slice) => broadcast(&slice, 4),
                         None => vec![widths.top as u32, widths.left as u32, widths.bottom as u32, widths.right as u32],
                     };
 
@@ -1055,35 +1096,34 @@ impl YamlFrameReader {
                     None
                 }
             }
         } else {
             println!("Unable to parse border {:?}", item);
             None
         };
         if let Some(details) = border_details {
-            dl.push_border(&info, &self.top_space_and_clip(), widths, details);
+            dl.push_border(&info, bounds, widths, details);
         }
     }
 
     fn handle_box_shadow(
         &mut self,
         dl: &mut DisplayListBuilder,
         item: &Yaml,
-        info: &mut LayoutPrimitiveInfo,
+        info: &mut CommonItemProperties,
     ) {
         let bounds_key = if item["type"].is_badvalue() {
             "box-shadow"
         } else {
             "bounds"
         };
         let bounds = item[bounds_key]
             .as_rect()
             .expect("box shadow must have bounds");
-        info.rect = bounds;
         let box_bounds = item["box-bounds"].as_rect().unwrap_or(bounds);
         let offset = self.resolve_vector(&item["offset"], LayoutVector2D::zero());
         let color = item["color"]
             .as_colorf()
             .unwrap_or(ColorF::new(0.0, 0.0, 0.0, 1.0));
         let blur_radius = item["blur-radius"].as_force_f32().unwrap_or(0.0);
         let spread_radius = item["spread-radius"].as_force_f32().unwrap_or(0.0);
         let border_radius = item["border-radius"]
@@ -1096,33 +1136,32 @@ impl YamlFrameReader {
                 s => panic!("Unknown box shadow clip mode {}", s),
             }
         } else {
             BoxShadowClipMode::Outset
         };
 
         dl.push_box_shadow(
             &info,
-            &self.top_space_and_clip(),
             box_bounds,
             offset,
             color,
             blur_radius,
             spread_radius,
             border_radius,
             clip_mode,
         );
     }
 
     fn handle_yuv_image(
         &mut self,
         dl: &mut DisplayListBuilder,
         wrench: &mut Wrench,
         item: &Yaml,
-        info: &mut LayoutPrimitiveInfo,
+        info: &mut CommonItemProperties,
     ) {
         // TODO(gw): Support other YUV color depth and spaces.
         let color_depth = ColorDepth::Color8;
         let color_space = YuvColorSpace::Rec709;
 
         let yuv_data = match item["format"].as_str().expect("no format supplied") {
             "planar" => {
                 let y_path = rsrc_path(&item["src-y"], &self.aux_dir);
@@ -1152,63 +1191,62 @@ impl YamlFrameReader {
                 YuvData::InterleavedYCbCr(yuv_key)
             }
             _ => {
                 panic!("unexpected yuv format");
             }
         };
 
         let bounds = item["bounds"].as_vec_f32().unwrap();
-        info.rect = LayoutRect::new(
+        let bounds = LayoutRect::new(
             LayoutPoint::new(bounds[0], bounds[1]),
             LayoutSize::new(bounds[2], bounds[3]),
         );
 
         dl.push_yuv_image(
             &info,
-            &self.top_space_and_clip(),
+            bounds,
             yuv_data,
             color_depth,
             color_space,
             ImageRendering::Auto,
         );
     }
 
     fn handle_image(
         &mut self,
         dl: &mut DisplayListBuilder,
         wrench: &mut Wrench,
         item: &Yaml,
-        info: &mut LayoutPrimitiveInfo,
+        info: &mut CommonItemProperties,
     ) {
         let filename = &item[if item["type"].is_badvalue() {
                                  "image"
                              } else {
                                  "src"
                              }];
         let tiling = item["tile-size"].as_i64();
         let file = rsrc_path(filename, &self.aux_dir);
         let (image_key, image_dims) =
             self.add_or_get_image(&file, tiling, wrench);
 
         let bounds_raws = item["bounds"].as_vec_f32().unwrap();
-        info.rect = if bounds_raws.len() == 2 {
+        let bounds = if bounds_raws.len() == 2 {
             LayoutRect::new(LayoutPoint::new(bounds_raws[0], bounds_raws[1]), image_dims)
         } else if bounds_raws.len() == 4 {
             LayoutRect::new(
                 LayoutPoint::new(bounds_raws[0], bounds_raws[1]),
                 LayoutSize::new(bounds_raws[2], bounds_raws[3]),
             )
         } else {
             panic!(
                 "image expected 2 or 4 values in bounds, got '{:?}'",
                 item["bounds"]
             );
         };
-
         let stretch_size = item["stretch-size"].as_size().unwrap_or(image_dims);
         let tile_spacing = item["tile-spacing"]
             .as_size()
             .unwrap_or(LayoutSize::new(0.0, 0.0));
         let rendering = match item["rendering"].as_str() {
             Some("auto") | None => ImageRendering::Auto,
             Some("crisp-edges") => ImageRendering::CrispEdges,
             Some("pixelated") => ImageRendering::Pixelated,
@@ -1222,32 +1260,32 @@ impl YamlFrameReader {
             Some("alpha") => AlphaType::Alpha,
             Some(_) => panic!(
                 "AlphaType can be premultiplied-alpha or alpha -- got {:?}",
                 item
             ),
         };
         dl.push_image(
             &info,
-            &self.top_space_and_clip(),
+            bounds,
             stretch_size,
             tile_spacing,
             rendering,
             alpha_type,
             image_key,
             ColorF::WHITE,
         );
     }
 
     fn handle_text(
         &mut self,
         dl: &mut DisplayListBuilder,
         wrench: &mut Wrench,
         item: &Yaml,
-        info: &mut LayoutPrimitiveInfo,
+        info: &mut CommonItemProperties,
     ) {
         let size = item["size"].as_pt_to_au().unwrap_or(Au::from_f32_px(16.0));
         let color = item["color"].as_colorf().unwrap_or(ColorF::BLACK);
         let bg_color = item["bg-color"].as_colorf().map(|c| c.into());
         let synthetic_italics = if let Some(angle) = item["synthetic-italics"].as_f32() {
             SyntheticItalics::from_degrees(angle)
         } else if item["synthetic-italics"].as_bool().unwrap_or(false) {
             SyntheticItalics::enabled()
@@ -1302,16 +1340,17 @@ impl YamlFrameReader {
             assert_eq!(glyph_offsets.len(), glyph_indices.len() * 2);
 
             let glyphs = glyph_indices
                 .iter()
                 .enumerate()
                 .map(|k| {
                     GlyphInstance {
                         index: *k.1,
+                        // In the future we want to change the API to be relative, eliminating this
                         point: LayoutPoint::new(
                             origin.x + glyph_offsets[k.0 * 2],
                             origin.y + glyph_offsets[k.0 * 2 + 1],
                         ),
                     }
                 })
                 .collect::<Vec<_>>();
             // TODO(gw): We could optionally use the WR API to query glyph dimensions
@@ -1342,38 +1381,46 @@ impl YamlFrameReader {
                         index: *arg.0 as u32,
                         point: arg.1,
                     };
                     gi
                 })
                 .collect::<Vec<_>>();
             (glyphs, bounds)
         };
-        info.rect = rect;
 
         dl.push_text(
             &info,
-            &self.top_space_and_clip(),
+            rect,
             &glyphs,
             font_instance_key,
             color,
             None,
         );
     }
 
     fn handle_iframe(
         &mut self,
         dl: &mut DisplayListBuilder,
         item: &Yaml,
-        info: &mut LayoutPrimitiveInfo,
+        info: &mut CommonItemProperties,
     ) {
-        info.rect = item["bounds"].as_rect().expect("iframe must have bounds");
+        let bounds = item["bounds"].as_rect().expect("iframe must have bounds");
         let pipeline_id = item["id"].as_pipeline_id().unwrap();
         let ignore = item["ignore_missing_pipeline"].as_bool().unwrap_or(true);
-        dl.push_iframe(&info, &self.top_space_and_clip(), pipeline_id, ignore);
+        dl.push_iframe(
+            bounds,
+            info.clip_rect,
+            &SpaceAndClipInfo {
+                spatial_id: info.spatial_id,
+                clip_id: info.clip_id
+            },
+            pipeline_id,
+            ignore
+        );
     }
 
     fn get_complex_clip_for_item(&mut self, yaml: &Yaml) -> Option<ComplexClipRegion> {
         let complex_clip = &yaml["complex-clip"];
         if complex_clip.is_badvalue() {
             return None;
         }
         Some(self.to_complex_clip_region(complex_clip))
@@ -1449,22 +1496,28 @@ impl YamlFrameReader {
                             None,
                         );
                         self.clip_id_stack.push(id);
                         pushed_clip = true;
                     }
                 }
             }
 
-            let mut info = LayoutPrimitiveInfo::with_clip_rect(LayoutRect::zero(), clip_rect);
-            info.is_backface_visible = item["backface-visible"].as_bool().unwrap_or(true);;
-            info.tag = self.to_hit_testing_tag(&item["hit-testing-tag"]);
+            let space_and_clip = self.top_space_and_clip();
+            let mut info = CommonItemProperties {
+                clip_rect,
+                clip_id: space_and_clip.clip_id,
+                spatial_id: space_and_clip.spatial_id,
+                hit_info: self.to_hit_testing_tag(&item["hit-testing-tag"]),
+                is_backface_visible: item["backface-visible"].as_bool().unwrap_or(true),
+            };
 
             match item_type {
                 "rect" => self.handle_rect(dl, item, &mut info),
+                "hit-test" => self.handle_hit_test(dl, item, &mut info),
                 "clear-rect" => self.handle_clear_rect(dl, item, &mut info),
                 "line" => self.handle_line(dl, item, &mut info),
                 "image" => self.handle_image(dl, wrench, item, &mut info),
                 "yuv-image" => self.handle_yuv_image(dl, wrench, item, &mut info),
                 "text" | "glyphs" => self.handle_text(dl, wrench, item, &mut info),
                 "scroll-frame" => self.handle_scroll_frame(dl, wrench, item),
                 "sticky-frame" => self.handle_sticky_frame(dl, wrench, item),
                 "clip" => self.handle_clip(dl, wrench, item),
@@ -1623,25 +1676,24 @@ impl YamlFrameReader {
             .as_vector()
             .unwrap_or(default)
     }
 
     fn handle_push_shadow(
         &mut self,
         dl: &mut DisplayListBuilder,
         yaml: &Yaml,
-        info: &mut LayoutPrimitiveInfo,
+        info: &mut CommonItemProperties,
     ) {
         let blur_radius = yaml["blur-radius"].as_f32().unwrap_or(0.0);
         let offset = yaml["offset"].as_vector().unwrap_or(LayoutVector2D::zero());
         let color = yaml["color"].as_colorf().unwrap_or(ColorF::BLACK);
 
         dl.push_shadow(
-            &info,
-            &self.top_space_and_clip(),
+            &SpaceAndClipInfo { spatial_id: info.spatial_id, clip_id: info.clip_id },
             Shadow {
                 blur_radius,
                 offset,
                 color,
                 should_inflate: true,
             },
         );
     }
@@ -1746,17 +1798,17 @@ impl YamlFrameReader {
             Some(value) if value != 0.0 => {
                 Some(make_perspective(perspective_origin, value as f32))
             }
             Some(..) => None,
             _ => yaml["perspective"].as_matrix4d(),
         };
 
         let reference_frame_id = dl.push_reference_frame(
-            &bounds,
+            bounds.origin,
             *self.spatial_id_stack.last().unwrap(),
             transform_style,
             transform.or_else(|| perspective).unwrap_or_default().into(),
             reference_frame_kind,
         );
 
         let numeric_id = yaml["id"].as_i64();
         if let Some(numeric_id) = numeric_id {
@@ -1784,32 +1836,31 @@ impl YamlFrameReader {
     }
 
     fn add_stacking_context_from_yaml(
         &mut self,
         dl: &mut DisplayListBuilder,
         wrench: &mut Wrench,
         yaml: &Yaml,
         is_root: bool,
-        info: &mut LayoutPrimitiveInfo,
+        info: &mut CommonItemProperties,
     ) {
         let default_bounds = LayoutRect::new(LayoutPoint::zero(), wrench.window_size_f32());
         let mut bounds = yaml["bounds"].as_rect().unwrap_or(default_bounds);
 
         let reference_frame_id = if !yaml["transform"].is_badvalue() ||
             !yaml["perspective"].is_badvalue() {
             let reference_frame_id = self.push_reference_frame(dl, wrench, yaml);
             self.spatial_id_stack.push(reference_frame_id);
             bounds.origin = LayoutPoint::zero();
             Some(reference_frame_id)
         } else {
             None
         };
 
-        // note: this API is deprecated, use the standard clip-and-scroll field instead
         let clip_node_id = self.to_clip_id(&yaml["clip-node"], dl.pipeline_id);
 
         let transform_style = yaml["transform-style"]
             .as_transform_style()
             .unwrap_or(TransformStyle::Flat);
         let mix_blend_mode = yaml["mix-blend-mode"]
             .as_mix_blend_mode()
             .unwrap_or(MixBlendMode::Normal);
@@ -1823,22 +1874,20 @@ impl YamlFrameReader {
                 let external_id = ExternalScrollId(0, dl.pipeline_id);
                 self.scroll_offsets.insert(external_id, LayoutPoint::new(size.x, size.y));
             }
         }
 
         let filters = yaml["filters"].as_vec_filter_op().unwrap_or(vec![]);
         let filter_datas = yaml["filter-datas"].as_vec_filter_data().unwrap_or(vec![]);
 
-        info.rect = bounds;
-        info.clip_rect = bounds;
-
         dl.push_stacking_context(
-            &info,
+            bounds.origin,
             *self.spatial_id_stack.last().unwrap(),
+            info.is_backface_visible,
             clip_node_id,
             transform_style,
             mix_blend_mode,
             &filters,
             &filter_datas,
             raster_space,
             cache_tiles,
         );
--- a/gfx/wr/wrench/src/yaml_frame_writer.rs
+++ b/gfx/wr/wrench/src/yaml_frame_writer.rs
@@ -11,17 +11,16 @@ use scene::{Scene, SceneProperties};
 use std::collections::HashMap;
 use std::io::Write;
 use std::path::{Path, PathBuf};
 use std::{fmt, fs};
 use super::CURRENT_FRAME_NUMBER;
 use time;
 use webrender;
 use webrender::api::*;
-use webrender::api::SpecificDisplayItem as Sdi;
 use webrender::api::channel::Payload;
 use webrender::api::units::*;
 use yaml_helper::StringEnum;
 use yaml_rust::{Yaml, YamlEmitter};
 
 type Table = yaml_rust::yaml::Hash;
 
 fn array_elements_are_same<T: PartialEq>(v: &[T]) -> bool {
@@ -185,16 +184,43 @@ fn maybe_radius_yaml(radius: &BorderRadi
         size_node(&mut table, "top-left", &radius.top_left);
         size_node(&mut table, "top-right", &radius.top_right);
         size_node(&mut table, "bottom-left", &radius.bottom_left);
         size_node(&mut table, "bottom-right", &radius.bottom_right);
         Some(Yaml::Hash(table))
     }
 }
 
+fn common_node(v: &mut Table, clip_id_mapper: &mut ClipIdMapper, info: &CommonItemProperties) {
+    rect_node(v, "clip-rect", &info.clip_rect);
+    bool_node(v, "backface-visible", info.is_backface_visible);
+
+    clip_and_scroll_node(v, clip_id_mapper, info.clip_id, info.spatial_id);
+
+    if let Some(tag) = info.hit_info {
+        yaml_node(
+            v,
+            "hit-testing-tag",
+             Yaml::Array(vec![Yaml::Integer(tag.0 as i64), Yaml::Integer(tag.1 as i64)])
+        );
+    }
+}
+
+fn clip_and_scroll_node(
+    v: &mut Table,
+    clip_id_mapper: &mut ClipIdMapper,
+    clip_id: ClipId,
+    spatial_id: SpatialId
+) {
+    let clip_id = if clip_id.is_root() { None } else { Some(clip_id) };
+    yaml_node(v, "clip-and-scroll",
+        clip_id_mapper.map_clip_and_scroll_ids(clip_id, spatial_id)
+    );
+}
+
 fn write_reference_frame(
     parent: &mut Table,
     reference_frame: &ReferenceFrame,
     properties: &SceneProperties,
     clip_id_mapper: &mut ClipIdMapper,
 ) {
     // FIXME: This ignores the scrolling_relative_to member in
     // ReferenceFrameKind::Perspective, but it's a bit annoying to fix since the
@@ -559,17 +585,17 @@ impl YamlFrameWriter {
                 &mut root_dl_table,
                 "id",
                 &[root_pipeline_id.0, root_pipeline_id.1],
             );
 
             let mut referenced_pipeline_ids = vec![];
             let mut traversal = dl.iter();
             while let Some(item) = traversal.next() {
-                if let &SpecificDisplayItem::Iframe(k) = item.item() {
+                if let &DisplayItem::Iframe(k) = item.item() {
                     referenced_pipeline_ids.push(k.pipeline_id);
                 }
             }
 
             let mut pipelines = vec![];
             for pipeline_id in referenced_pipeline_ids {
                 if !scene.display_lists.contains_key(&pipeline_id) {
                     continue;
@@ -820,61 +846,52 @@ impl YamlFrameWriter {
                 *list_iterator = traversal;
             }
             let base = match list_iterator.next() {
                 Some(base) => base,
                 None => break,
             };
 
             let mut v = new_table();
-            let info = base.get_layout_primitive_info(&LayoutVector2D::zero());
-            rect_node(&mut v, "bounds", &info.rect);
-            rect_node(&mut v, "clip-rect", &info.clip_rect);
-
-            if let Some(tag) = info.tag {
-                yaml_node(
-                    &mut v,
-                    "hit-testing-tag",
-                     Yaml::Array(vec![Yaml::Integer(tag.0 as i64), Yaml::Integer(tag.1 as i64)])
-                );
-            }
-
-            let space_and_clip = base.space_and_clip_info();
-            let clip_id = if space_and_clip.clip_id.is_root() { None } else { Some(space_and_clip.clip_id) };
-            yaml_node(&mut v, "clip-and-scroll",
-                clip_id_mapper.map_clip_and_scroll_ids(clip_id, space_and_clip.spatial_id)
-            );
-            bool_node(&mut v, "backface-visible", base.is_backface_visible());
-
             match *base.item() {
-                Sdi::Rectangle(item) => {
+                DisplayItem::Rectangle(item) => {
                     str_node(&mut v, "type", "rect");
+                    common_node(&mut v, clip_id_mapper, &item.common);
                     color_node(&mut v, "color", item.color);
                 }
-                Sdi::ClearRectangle => {
-                    str_node(&mut v, "type", "clear-rect");;
+                DisplayItem::HitTest(item) => {
+                    str_node(&mut v, "type", "hit-test");
+                    common_node(&mut v, clip_id_mapper, &item.common);
                 }
-                Sdi::Line(item) => {
+                DisplayItem::ClearRectangle(item) => {
+                    str_node(&mut v, "type", "clear-rect");
+                    common_node(&mut v, clip_id_mapper, &item.common);
+                }
+                DisplayItem::Line(item) => {
                     str_node(&mut v, "type", "line");
+                    common_node(&mut v, clip_id_mapper, &item.common);
+                    rect_node(&mut v, "bounds", &item.area);
                     if let LineStyle::Wavy = item.style {
                         f32_node(&mut v, "thickness", item.wavy_line_thickness);
                     }
                     str_node(&mut v, "orientation", item.orientation.as_str());
                     color_node(&mut v, "color", item.color);
                     str_node(&mut v, "style", item.style.as_str());
                 }
-                Sdi::Text(item) => {
+                DisplayItem::Text(item) => {
                     let gi = display_list.get(base.glyphs());
                     let mut indices: Vec<u32> = vec![];
                     let mut offsets: Vec<f32> = vec![];
                     for g in gi {
                         indices.push(g.index);
                         offsets.push(g.point.x);
                         offsets.push(g.point.y);
                     }
+                    common_node(&mut v, clip_id_mapper, &item.common);
+                    rect_node(&mut v, "bounds", &item.bounds);
                     u32_vec_node(&mut v, "glyphs", &indices);
                     f32_vec_node(&mut v, "offsets", &offsets);
 
                     let instance = self.font_instances.entry(item.font_key).or_insert_with(|| {
                         println!("Warning: font instance key not found in font instances table!");
                         CachedFontInstance {
                             font_key: FontKey::new(IdNamespace(0), 0),
                             glyph_size: Au::from_px(16),
@@ -910,17 +927,19 @@ impl YamlFrameWriter {
 
                             path_node(&mut v, "font", path_opt.as_ref().unwrap());
                             if index != 0 {
                                 u32_node(&mut v, "font-index", index);
                             }
                         }
                     }
                 }
-                Sdi::Image(item) => {
+                DisplayItem::Image(item) => {
+                    common_node(&mut v, clip_id_mapper, &item.common);
+                    rect_node(&mut v, "bounds", &item.bounds);
                     if let Some(path) = self.path_for_image(item.image_key) {
                         path_node(&mut v, "image", &path);
                     }
                     if let Some(&CachedImage {
                         tiling: Some(tile_size),
                         ..
                     }) = self.images.get(&item.image_key)
                     {
@@ -933,23 +952,26 @@ impl YamlFrameWriter {
                         ImageRendering::CrispEdges => str_node(&mut v, "rendering", "crisp-edges"),
                         ImageRendering::Pixelated => str_node(&mut v, "rendering", "pixelated"),
                     };
                     match item.alpha_type {
                         AlphaType::PremultipliedAlpha => str_node(&mut v, "alpha-type", "premultiplied-alpha"),
                         AlphaType::Alpha => str_node(&mut v, "alpha-type", "alpha"),
                     };
                 }
-                Sdi::YuvImage(_) => {
+                DisplayItem::YuvImage(item) => {
                     str_node(&mut v, "type", "yuv-image");
+                    common_node(&mut v, clip_id_mapper, &item.common);
                     // TODO
                     println!("TODO YAML YuvImage");
                 }
-                Sdi::Border(item) => {
+                DisplayItem::Border(item) => {
                     str_node(&mut v, "type", "border");
+                    rect_node(&mut v, "bounds", &item.bounds);
+                    common_node(&mut v, clip_id_mapper, &item.common);
                     match item.details {
                         BorderDetails::Normal(ref details) => {
                             let trbl =
                                 vec![&details.top, &details.right, &details.bottom, &details.left];
                             let widths: Vec<f32> = vec![
                                 item.widths.top,
                                 item.widths.right,
                                 item.widths.bottom,
@@ -1053,152 +1075,179 @@ impl YamlFrameWriter {
                                 }
                                 RepeatMode::Repeat => str_node(&mut v, "repeat-vertical", "repeat"),
                                 RepeatMode::Round => str_node(&mut v, "repeat-vertical", "round"),
                                 RepeatMode::Space => str_node(&mut v, "repeat-vertical", "space"),
                             };
                         }
                     }
                 }
-                Sdi::BoxShadow(item) => {
+                DisplayItem::BoxShadow(item) => {
                     str_node(&mut v, "type", "box-shadow");
+                    common_node(&mut v, clip_id_mapper, &item.common);
                     rect_node(&mut v, "box-bounds", &item.box_bounds);
                     vector_node(&mut v, "offset", &item.offset);
                     color_node(&mut v, "color", item.color);
                     f32_node(&mut v, "blur-radius", item.blur_radius);
                     f32_node(&mut v, "spread-radius", item.spread_radius);
                     if let Some(radius_node) = maybe_radius_yaml(&item.border_radius) {
                         yaml_node(&mut v, "border-radius", radius_node);
                     }
                     let clip_mode = match item.clip_mode {
                         BoxShadowClipMode::Outset => "outset",
                         BoxShadowClipMode::Inset => "inset",
                     };
                     str_node(&mut v, "clip-mode", clip_mode);
                 }
-                Sdi::Gradient(item) => {
+                DisplayItem::Gradient(item) => {
                     str_node(&mut v, "type", "gradient");
+                    rect_node(&mut v, "bounds", &item.bounds);
+                    common_node(&mut v, clip_id_mapper, &item.common);
                     point_node(&mut v, "start", &item.gradient.start_point);
                     point_node(&mut v, "end", &item.gradient.end_point);
                     size_node(&mut v, "tile-size", &item.tile_size);
                     size_node(&mut v, "tile-spacing", &item.tile_spacing);
                     let mut stops = vec![];
                     for stop in display_list.get(base.gradient_stops()) {
                         stops.push(Yaml::Real(stop.offset.to_string()));
                         stops.push(Yaml::String(color_to_string(stop.color)));
                     }
                     yaml_node(&mut v, "stops", Yaml::Array(stops));
                     bool_node(
                         &mut v,
                         "repeat",
                         item.gradient.extend_mode == ExtendMode::Repeat,
                     );
                 }
-                Sdi::RadialGradient(item) => {
+                DisplayItem::RadialGradient(item) => {
                     str_node(&mut v, "type", "radial-gradient");
+                    rect_node(&mut v, "bounds", &item.bounds);
+                    common_node(&mut v, clip_id_mapper, &item.common);
                     size_node(&mut v, "tile-size", &item.tile_size);
                     size_node(&mut v, "tile-spacing", &item.tile_spacing);
                     radial_gradient_to_yaml(
                         &mut v,
                         &item.gradient,
                         base.gradient_stops(),
                         display_list
                     );
                 }
-                Sdi::Iframe(item) => {
+                DisplayItem::Iframe(item) => {
                     str_node(&mut v, "type", "iframe");
+                    rect_node(&mut v, "bounds", &item.bounds);
+                    rect_node(&mut v, "clip_rect", &item.clip_rect);
+                    clip_and_scroll_node(
+                        &mut v,
+                        clip_id_mapper,
+                        item.space_and_clip.clip_id,
+                        item.space_and_clip.spatial_id
+                    );
                     u32_vec_node(&mut v, "id", &[item.pipeline_id.0, item.pipeline_id.1]);
                     bool_node(&mut v, "ignore_missing_pipeline", item.ignore_missing_pipeline);
                 }
-                Sdi::PushStackingContext(item) => {
+                DisplayItem::PushStackingContext(item) => {
                     str_node(&mut v, "type", "stacking-context");
+                    clip_and_scroll_node(
+                        &mut v,
+                        clip_id_mapper,
+                        item.stacking_context.clip_id.unwrap_or(ClipId::invalid()),
+                        item.spatial_id
+                    );
+                    point_node(&mut v, "origin", &item.origin);
+                    bool_node(&mut v, "backface-visible", item.is_backface_visible);
                     let filters = display_list.get(base.filters());
                     write_stacking_context(
                         &mut v,
                         &item.stacking_context,
                         &scene.properties,
                         filters,
                         base.filter_datas(),
                         display_list,
                     );
 
                     let mut sub_iter = base.sub_iter();
                     self.write_display_list(&mut v, display_list, scene, &mut sub_iter, clip_id_mapper);
                     continue_traversal = Some(sub_iter);
                 }
-                Sdi::PushReferenceFrame(item) => {
+                DisplayItem::PushReferenceFrame(item) => {
                     str_node(&mut v, "type", "reference-frame");
                     write_reference_frame(
                         &mut v,
                         &item.reference_frame,
                         &scene.properties,
                         clip_id_mapper,
                     );
 
                     let mut sub_iter = base.sub_iter();
                     self.write_display_list(&mut v, display_list, scene, &mut sub_iter, clip_id_mapper);
                     continue_traversal = Some(sub_iter);
                 }
-                Sdi::Clip(item) => {
+                DisplayItem::Clip(item) => {
                     str_node(&mut v, "type", "clip");
+                    clip_and_scroll_node(
+                        &mut v,
+                        clip_id_mapper,
+                        item.parent_space_and_clip.clip_id,
+                        item.parent_space_and_clip.spatial_id);
+                    rect_node(&mut v, "clip-rect", &item.clip_rect);
                     usize_node(&mut v, "id", clip_id_mapper.add_clip_id(item.id));
 
                     let (complex_clips, complex_clip_count) = base.complex_clip();
                     if let Some(complex) = self.make_complex_clips_node(
                         complex_clip_count,
                         complex_clips,
                         display_list,
                     ) {
                         yaml_node(&mut v, "complex", complex);
                     }
 
                     if let Some(mask_yaml) = self.make_clip_mask_image_node(&item.image_mask) {
                         yaml_node(&mut v, "image-mask", mask_yaml);
                     }
                 }
-                Sdi::ClipChain(item) => {
+                DisplayItem::ClipChain(item) => {
                     str_node(&mut v, "type", "clip-chain");
 
                     let id = ClipId::ClipChain(item.id);
                     u32_node(&mut v, "id", clip_id_mapper.add_clip_id(id) as u32);
 
                     let clip_ids = display_list.get(base.clip_chain_items()).map(|clip_id| {
                         clip_id_mapper.map_clip_id(&clip_id)
                     }).collect();
                     yaml_node(&mut v, "clips", Yaml::Array(clip_ids));
 
                     if let Some(parent) = item.parent {
                         let parent = ClipId::ClipChain(parent);
                         yaml_node(&mut v, "parent", clip_id_mapper.map_clip_id(&parent));
                     }
                 }
-                Sdi::ScrollFrame(item) => {
+                DisplayItem::ScrollFrame(item) => {
                     str_node(&mut v, "type", "scroll-frame");
                     usize_node(&mut v, "id", clip_id_mapper.add_spatial_id(item.scroll_frame_id));
-                    size_node(&mut v, "content-size", &base.rect().size);
-                    rect_node(&mut v, "bounds", &base.clip_rect());
+                    size_node(&mut v, "content-size", &item.content_rect.size);
+                    rect_node(&mut v, "bounds", &item.clip_rect);
                     vector_node(&mut v, "external-scroll-offset", &item.external_scroll_offset);
 
                     let (complex_clips, complex_clip_count) = base.complex_clip();
                     if let Some(complex) = self.make_complex_clips_node(
                         complex_clip_count,
                         complex_clips,
                         display_list,
                     ) {
                         yaml_node(&mut v, "complex", complex);
                     }
 
                     if let Some(mask_yaml) = self.make_clip_mask_image_node(&item.image_mask) {
                         yaml_node(&mut v, "image-mask", mask_yaml);
                     }
                 }
-                Sdi::StickyFrame(item) => {
+                DisplayItem::StickyFrame(item) => {
                     str_node(&mut v, "type", "sticky-frame");
                     usize_node(&mut v, "id", clip_id_mapper.add_spatial_id(item.id));
-                    rect_node(&mut v, "bounds", &base.clip_rect());
+                    rect_node(&mut v, "bounds", &item.bounds);
 
                     if let Some(margin) = item.margins.top {
                         f32_node(&mut v, "margin-top", margin);
                     }
                     if let Some(margin) = item.margins.bottom {
                         f32_node(&mut v, "margin-bottom", margin);
                     }
                     if let Some(margin) = item.margins.left {
@@ -1222,34 +1271,29 @@ impl YamlFrameWriter {
 
                     let applied = vec![
                         Yaml::Real(item.previously_applied_offset.x.to_string()),
                         Yaml::Real(item.previously_applied_offset.y.to_string()),
                     ];
                     yaml_node(&mut v, "previously-applied-offset", Yaml::Array(applied));
                 }
 
-                Sdi::PopReferenceFrame |
-                Sdi::PopStackingContext => return,
-
-                Sdi::PopCacheMarker => return,
-                Sdi::PushCacheMarker(_) => {
-                    str_node(&mut v, "type", "cache-marker");
-                }
+                DisplayItem::PopReferenceFrame |
+                DisplayItem::PopStackingContext => return,
 
-                Sdi::SetGradientStops => panic!("dummy item yielded?"),
-                Sdi::SetFilterOps => panic!("dummy item yielded?"),
-                Sdi::SetFilterData => panic!("dummy item yielded?"),
-                Sdi::PushShadow(shadow) => {
+                DisplayItem::SetGradientStops => panic!("dummy item yielded?"),
+                DisplayItem::SetFilterOps => panic!("dummy item yielded?"),
+                DisplayItem::SetFilterData => panic!("dummy item yielded?"),
+                DisplayItem::PushShadow(item) => {
                     str_node(&mut v, "type", "shadow");
-                    vector_node(&mut v, "offset", &shadow.offset);
-                    color_node(&mut v, "color", shadow.color);
-                    f32_node(&mut v, "blur-radius", shadow.blur_radius);
+                    vector_node(&mut v, "offset", &item.shadow.offset);
+                    color_node(&mut v, "color", item.shadow.color);
+                    f32_node(&mut v, "blur-radius", item.shadow.blur_radius);
                 }
-                Sdi::PopAllShadows => {
+                DisplayItem::PopAllShadows => {
                     str_node(&mut v, "type", "pop-all-shadows");
                 }
             }
             if !v.is_empty() {
                 list.push(Yaml::Hash(v));
             }
         }
     }
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -5102,18 +5102,17 @@ bool nsDisplayCompositorHitTestInfo::Cre
   // Insert a transparent rectangle with the hit-test info
   aBuilder.SetHitTestInfo(scrollId, HitTestFlags());
 
   const LayoutDeviceRect devRect =
       LayoutDeviceRect::FromAppUnits(HitTestArea(), mAppUnitsPerDevPixel);
 
   const wr::LayoutRect rect = wr::ToRoundedLayoutRect(devRect);
 
-  aBuilder.PushRect(rect, rect, !BackfaceIsHidden(),
-                    wr::ToColorF(gfx::Color()));
+  aBuilder.PushHitTest(rect, rect, !BackfaceIsHidden());
   aBuilder.ClearHitTestInfo();
 
   return true;
 }
 
 uint16_t nsDisplayCompositorHitTestInfo::CalculatePerFrameKey() const {
   return mIndex;
 }