servo: Merge #10815 - style: Support anonymous box pseudo-elements (from emilio:anonbox-gcs); r=SimonSapin,bholley
authorEmilio Cobos Álvarez <ecoal95@gmail.com>
Fri, 29 Apr 2016 14:27:16 -0700
changeset 338666 be5d2d5fb6f1adcba0bccac5c9db2fd82ffaef12
parent 338665 12016f86538dd10c321463117e8b2bec314fa7f8
child 338667 0f2c1af44a4956a40a0282a105effa47a801366a
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersSimonSapin, bholley
servo: Merge #10815 - style: Support anonymous box pseudo-elements (from emilio:anonbox-gcs); r=SimonSapin,bholley This is a work-in-progress that: * Adds support for some pseudo-elements to skip the cascade entirely, in an analogous way to Gecko's anonymous box pseudo-elements. * Takes rid of `StylistWrapper`, and uses `Arc::get_mut` instead. * Uses the first bullet to precompute the `-servo-details-content` pseudo's style. I'd like @bholley to take a look before following, do you think that the aproach is the correct? Also, @SimonSapin could want to put some eyes on it. Depends on https://github.com/servo/rust-selectors/pull/81 Source-Repo: https://github.com/servo/servo Source-Revision: 407f991c8aba5dcf5312bb2c34a3dd4fe12e5471
servo/components/layout/construct.rs
servo/components/layout/context.rs
servo/components/layout/data.rs
servo/components/layout/flow.rs
servo/components/layout/fragment.rs
servo/components/layout/layout_thread.rs
servo/components/layout/lib.rs
servo/components/layout/query.rs
servo/components/layout/wrapper.rs
servo/components/servo/Cargo.lock
servo/components/style/context.rs
servo/components/style/dom.rs
servo/components/style/restyle_hints.rs
servo/components/style/selector_impl.rs
servo/components/style/selector_matching.rs
servo/components/style/servo.rs
servo/components/style/traversal.rs
servo/ports/cef/Cargo.lock
servo/ports/geckolib/Cargo.lock
servo/ports/geckolib/bindings.rs
servo/ports/geckolib/data.rs
servo/ports/geckolib/gecko_style_structs.rs
servo/ports/geckolib/glue.rs
servo/ports/geckolib/selector_impl.rs
servo/ports/geckolib/tools/regen_bindings.sh
servo/ports/geckolib/tools/regen_style_structs.sh
servo/ports/geckolib/traversal.rs
servo/ports/gonk/Cargo.lock
servo/resources/servo.css
servo/tests/unit/layout/size_of.rs
servo/tests/unit/style/Cargo.toml
servo/tests/unit/style/stylesheets.rs
--- a/servo/components/layout/construct.rs
+++ b/servo/components/layout/construct.rs
@@ -42,16 +42,17 @@ use std::collections::LinkedList;
 use std::marker::PhantomData;
 use std::mem;
 use std::sync::Arc;
 use std::sync::atomic::Ordering;
 use style::computed_values::content::ContentItem;
 use style::computed_values::{caption_side, display, empty_cells, float, list_style_position};
 use style::computed_values::{position};
 use style::properties::{self, ComputedValues, ServoComputedValues};
+use style::servo::SharedStyleContext;
 use table::TableFlow;
 use table_caption::TableCaptionFlow;
 use table_cell::TableCellFlow;
 use table_colgroup::TableColGroupFlow;
 use table_row::TableRowFlow;
 use table_rowgroup::TableRowGroupFlow;
 use table_wrapper::TableWrapperFlow;
 use text::TextRunScanner;
@@ -205,25 +206,25 @@ impl InlineFragmentsAccumulator {
         InlineFragmentsAccumulator {
             fragments: IntermediateInlineFragments::new(),
             enclosing_node: None,
             bidi_control_chars: None,
             restyle_damage: RestyleDamage::empty(),
         }
     }
 
-    fn from_inline_node<N>(node: &N) -> InlineFragmentsAccumulator
+    fn from_inline_node<N>(node: &N, style_context: &SharedStyleContext) -> InlineFragmentsAccumulator
             where N: ThreadSafeLayoutNode {
         InlineFragmentsAccumulator {
             fragments: IntermediateInlineFragments::new(),
             enclosing_node: Some(InlineFragmentNodeInfo {
                 address: node.opaque(),
                 pseudo: node.get_pseudo_element_type().strip(),
-                style: node.style().clone(),
-                selected_style: node.selected_style().clone(),
+                style: node.style(style_context).clone(),
+                selected_style: node.selected_style(style_context).clone(),
                 flags: InlineFragmentNodeFlags::empty(),
             }),
             bidi_control_chars: None,
             restyle_damage: node.restyle_damage(),
         }
     }
 
     fn push(&mut self, fragment: Fragment) {
@@ -283,16 +284,21 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
     pub fn new(layout_context: &'a LayoutContext<'a>) -> Self {
         FlowConstructor {
             layout_context: layout_context,
             phantom2: PhantomData,
         }
     }
 
     #[inline]
+    fn style_context(&self) -> &SharedStyleContext {
+        self.layout_context.style_context()
+    }
+
+    #[inline]
     fn set_flow_construction_result(&self,
                                     node: &ConcreteThreadSafeLayoutNode,
                                     result: ConstructionResult) {
         node.set_flow_construction_result(result);
     }
 
     /// Builds the fragment for the given block or subclass thereof.
     fn build_fragment_for_block(&mut self, node: &ConcreteThreadSafeLayoutNode) -> Fragment {
@@ -331,72 +337,73 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
                         HTMLElementTypeId::HTMLTableRowElement))) |
             Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
                         HTMLElementTypeId::HTMLTableSectionElement))) => {
                 SpecificFragmentInfo::TableRow
             }
             Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
                         HTMLElementTypeId::HTMLCanvasElement))) => {
                 let data = node.canvas_data().unwrap();
-                SpecificFragmentInfo::Canvas(box CanvasFragmentInfo::new(node, data))
+                SpecificFragmentInfo::Canvas(box CanvasFragmentInfo::new(node, data, self.layout_context))
             }
             _ => {
                 // This includes pseudo-elements.
                 SpecificFragmentInfo::Generic
             }
         };
 
-        Fragment::new(node, specific_fragment_info)
+        Fragment::new(node, specific_fragment_info, self.layout_context)
     }
 
     /// Generates anonymous table objects per CSS 2.1 § 17.2.1.
     fn generate_anonymous_table_flows_if_necessary(&mut self,
                                                    flow: &mut FlowRef,
                                                    child: &mut FlowRef,
                                                    child_node: &ConcreteThreadSafeLayoutNode) {
         if !flow.is_block_flow() {
             return
         }
 
+        let style_context = self.style_context();
         if child.is_table_cell() {
-            let mut style = child_node.style().clone();
+            let mut style = child_node.style(style_context).clone();
             properties::modify_style_for_anonymous_table_object(&mut style, display::T::table_row);
             let fragment = Fragment::from_opaque_node_and_style(child_node.opaque(),
                                                                 PseudoElementType::Normal,
                                                                 style,
-                                                                child_node.selected_style().clone(),
+                                                                child_node.selected_style(style_context).clone(),
                                                                 child_node.restyle_damage(),
                                                                 SpecificFragmentInfo::TableRow);
             let mut new_child: FlowRef = Arc::new(TableRowFlow::from_fragment(fragment));
             new_child.add_new_child(child.clone());
             child.finish();
             *child = new_child
         }
         if child.is_table_row() || child.is_table_rowgroup() {
-            let mut style = child_node.style().clone();
+            let mut style = child_node.style(style_context).clone();
             properties::modify_style_for_anonymous_table_object(&mut style, display::T::table);
             let fragment = Fragment::from_opaque_node_and_style(child_node.opaque(),
                                                                 PseudoElementType::Normal,
                                                                 style,
-                                                                child_node.selected_style().clone(),
+                                                                child_node.selected_style(style_context).clone(),
                                                                 child_node.restyle_damage(),
                                                                 SpecificFragmentInfo::Table);
             let mut new_child: FlowRef = Arc::new(TableFlow::from_fragment(fragment));
             new_child.add_new_child(child.clone());
             child.finish();
             *child = new_child
         }
         if child.is_table() {
-            let mut style = child_node.style().clone();
+            let mut style = child_node.style(style_context).clone();
             properties::modify_style_for_anonymous_table_object(&mut style, display::T::table);
             let fragment =
                 Fragment::from_opaque_node_and_style(child_node.opaque(),
                                                      PseudoElementType::Normal,
                                                      style,
-                                                     child_node.selected_style().clone(),
+                                                     child_node.selected_style(style_context).clone(),
                                                      child_node.restyle_damage(),
                                                      SpecificFragmentInfo::TableWrapper);
             let mut new_child: FlowRef = Arc::new(TableWrapperFlow::from_fragment(fragment, None));
             new_child.add_new_child(child.clone());
             child.finish();
             *child = new_child
         }
     }
@@ -444,17 +451,17 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
 
         // We must scan for runs before computing minimum ascent and descent because scanning
         // for runs might collapse so much whitespace away that only hypothetical fragments
         // remain. In that case the inline flow will compute its ascent and descent to be zero.
         let scanned_fragments =
             TextRunScanner::new().scan_for_runs(&mut self.layout_context.font_context(),
                                                 fragments.fragments);
         let mut inline_flow_ref: FlowRef = Arc::new(
-            InlineFlow::from_fragments(scanned_fragments, node.style().writing_mode));
+            InlineFlow::from_fragments(scanned_fragments, node.style(self.style_context()).writing_mode));
 
         // Add all the inline-block fragments as children of the inline flow.
         for inline_block_flow in &inline_block_flows {
             inline_flow_ref.add_new_child(inline_block_flow.clone());
         }
 
         // Set up absolute descendants as necessary.
         //
@@ -474,17 +481,17 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
         {
             // FIXME(#6503): Use Arc::get_mut().unwrap() here.
             let inline_flow = flow_ref::deref_mut(&mut inline_flow_ref).as_mut_inline();
 
 
             let (ascent, descent) =
                 inline_flow.compute_minimum_ascent_and_descent(&mut self.layout_context
                                                                         .font_context(),
-                                                               &**node.style());
+                                                               &**node.style(self.style_context()));
             inline_flow.minimum_block_size_above_baseline = ascent;
             inline_flow.minimum_depth_below_baseline = descent;
         }
 
         inline_flow_ref.finish();
 
         if flow.need_anonymous_flow(&*inline_flow_ref) {
             flow_list.push(inline_flow_ref)
@@ -582,20 +589,21 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
                     mut whitespace_style,
                     whitespace_damage)) => {
                 // Add whitespace results. They will be stripped out later on when
                 // between block elements, and retained when between inline elements.
                 let fragment_info = SpecificFragmentInfo::UnscannedText(
                     box UnscannedTextFragmentInfo::new(" ".to_owned(), None));
                 properties::modify_style_for_replaced_content(&mut whitespace_style);
                 properties::modify_style_for_text(&mut whitespace_style);
+                let style_context = self.style_context();
                 let fragment = Fragment::from_opaque_node_and_style(whitespace_node,
                                                                     whitespace_pseudo,
                                                                     whitespace_style,
-                                                                    node.selected_style().clone(),
+                                                                    node.selected_style(style_context).clone(),
                                                                     whitespace_damage,
                                                                     fragment_info);
                 inline_fragment_accumulator.fragments.fragments.push_back(fragment);
             }
             ConstructionResult::ConstructionItem(ConstructionItem::TableColumnFragment(_)) => {
                 // TODO: Implement anonymous table objects for missing parents
                 // CSS 2.1 § 17.2.1, step 3-2
             }
@@ -691,17 +699,17 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
             // box, so don't construct them.
             if node.type_id() == Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
                         HTMLElementTypeId::HTMLTextAreaElement))) {
                 for kid in node.children() {
                     self.set_flow_construction_result(&kid, ConstructionResult::None)
                 }
             }
 
-            let mut style = node.style().clone();
+            let mut style = node.style(self.style_context()).clone();
             if node_is_input_or_text_area {
                 properties::modify_style_for_input_text(&mut style);
             }
 
             self.create_fragments_for_node_text_content(&mut initial_fragments, node, &style)
         }
 
         self.build_flow_for_block_starting_with_fragments(flow, node, initial_fragments)
@@ -717,17 +725,17 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
         if text_content.is_empty() {
             return
         }
 
         let selection = node.selection();
         let mut style = (*style).clone();
         properties::modify_style_for_text(&mut style);
 
-        let selected_style = node.selected_style();
+        let selected_style = node.selected_style(self.style_context());
 
         match text_content {
             TextContent::Text(string) => {
                 let info = box UnscannedTextFragmentInfo::new(string, selection);
                 let specific_fragment_info = SpecificFragmentInfo::UnscannedText(info);
                 fragments.fragments.push_back(Fragment::from_opaque_node_and_style(
                         node.opaque(),
                         node.get_pseudo_element_type().strip(),
@@ -760,17 +768,17 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
         }
     }
 
     /// Builds a flow for a node with `display: block`. This yields a `BlockFlow` with possibly
     /// other `BlockFlow`s or `InlineFlow`s underneath it, depending on whether {ib} splits needed
     /// to happen.
     fn build_flow_for_block(&mut self, node: &ConcreteThreadSafeLayoutNode, float_kind: Option<FloatKind>)
                             -> ConstructionResult {
-        if node.style().is_multicol() {
+        if node.style(self.style_context()).is_multicol() {
             return self.build_flow_for_multicol(node, float_kind)
         }
 
         let flow: FlowRef = Arc::new(
             BlockFlow::from_fragment(self.build_fragment_for_block(node), float_kind));
         self.build_flow_for_block_like(flow, node)
     }
 
@@ -786,32 +794,32 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
                 flow: kid_flow
             } = split;
             fragment_accumulator.push_all(predecessors);
 
             let split = InlineBlockSplit {
                 predecessors: mem::replace(
                     fragment_accumulator,
                     InlineFragmentsAccumulator::from_inline_node(
-                        node)).to_intermediate_inline_fragments(),
+                        node, self.style_context())).to_intermediate_inline_fragments(),
                 flow: kid_flow,
             };
             opt_inline_block_splits.push_back(split)
         }
     }
 
     /// Concatenates the fragments of kids, adding in our own borders/padding/margins if necessary.
     /// Returns the `InlineFragmentsConstructionResult`, if any. There will be no
     /// `InlineFragmentsConstructionResult` if this node consisted entirely of ignorable
     /// whitespace.
     fn build_fragments_for_nonreplaced_inline_content(&mut self, node: &ConcreteThreadSafeLayoutNode)
                                                       -> ConstructionResult {
         let mut opt_inline_block_splits: LinkedList<InlineBlockSplit> = LinkedList::new();
-        let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node);
-        fragment_accumulator.bidi_control_chars = bidi_control_chars(&*node.style());
+        let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node, self.style_context());
+        fragment_accumulator.bidi_control_chars = bidi_control_chars(&*node.style(self.style_context()));
 
         let mut abs_descendants = AbsoluteDescendants::new();
 
         // Concatenate all the fragments of our kids, creating {ib} splits as necessary.
         let mut is_empty = true;
         for kid in node.children() {
             is_empty = false;
             if kid.get_pseudo_element_type() != PseudoElementType::Normal {
@@ -823,17 +831,17 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
                     if !flow::base(&*flow).flags.contains(IS_ABSOLUTELY_POSITIONED) {
                         // {ib} split. Flush the accumulator to our new split and make a new
                         // accumulator to hold any subsequent fragments we come across.
                         let split = InlineBlockSplit {
                             predecessors:
                                 mem::replace(
                                     &mut fragment_accumulator,
                                     InlineFragmentsAccumulator::from_inline_node(
-                                        node)).to_intermediate_inline_fragments(),
+                                        node, self.style_context())).to_intermediate_inline_fragments(),
                             flow: flow,
                         };
                         opt_inline_block_splits.push_back(split);
                         abs_descendants.push_descendants(kid_abs_descendants);
                     } else {
                         // Push the absolutely-positioned kid as an inline containing block.
                         let kid_node = flow.as_block().fragment.node;
                         let kid_pseudo = flow.as_block().fragment.pseudo.clone();
@@ -874,55 +882,57 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
                         whitespace_pseudo,
                         mut whitespace_style,
                         whitespace_damage)) => {
                     // Instantiate the whitespace fragment.
                     let fragment_info = SpecificFragmentInfo::UnscannedText(
                         box UnscannedTextFragmentInfo::new(" ".to_owned(), None));
                     properties::modify_style_for_replaced_content(&mut whitespace_style);
                     properties::modify_style_for_text(&mut whitespace_style);
-                    let fragment = Fragment::from_opaque_node_and_style(whitespace_node,
-                                                                        whitespace_pseudo,
-                                                                        whitespace_style,
-                                                                        node.selected_style().clone(),
-                                                                        whitespace_damage,
-                                                                        fragment_info);
+                    let fragment =
+                        Fragment::from_opaque_node_and_style(whitespace_node,
+                                                             whitespace_pseudo,
+                                                             whitespace_style,
+                                                             node.selected_style(self.style_context()).clone(),
+                                                             whitespace_damage,
+                                                             fragment_info);
                     fragment_accumulator.fragments.fragments.push_back(fragment)
                 }
                 ConstructionResult::ConstructionItem(ConstructionItem::TableColumnFragment(_)) => {
                     // TODO: Implement anonymous table objects for missing parents
                     // CSS 2.1 § 17.2.1, step 3-2
                 }
             }
         }
 
-        if is_empty && node.style().has_padding_or_border() {
+        let node_style = node.style(self.style_context());
+        if is_empty && node_style.has_padding_or_border() {
             // An empty inline box needs at least one fragment to draw its background and borders.
             let info = SpecificFragmentInfo::UnscannedText(
                 box UnscannedTextFragmentInfo::new(String::new(), None));
-            let mut modified_style = node.style().clone();
+            let mut modified_style = node_style.clone();
             properties::modify_style_for_replaced_content(&mut modified_style);
             properties::modify_style_for_text(&mut modified_style);
             let fragment = Fragment::from_opaque_node_and_style(node.opaque(),
                                                                 node.get_pseudo_element_type().strip(),
                                                                 modified_style,
-                                                                node.selected_style().clone(),
+                                                                node.selected_style(self.style_context()).clone(),
                                                                 node.restyle_damage(),
                                                                 info);
             fragment_accumulator.fragments.fragments.push_back(fragment)
         }
 
         // Finally, make a new construction result.
         if opt_inline_block_splits.len() > 0 || !fragment_accumulator.fragments.is_empty()
                 || abs_descendants.len() > 0 {
             fragment_accumulator.fragments.absolute_descendants.push_descendants(abs_descendants);
 
             // If the node is positioned, then it's the containing block for all absolutely-
             // positioned descendants.
-            if node.style().get_box().position != position::T::static_ {
+            if node_style.get_box().position != position::T::static_ {
                 fragment_accumulator.fragments
                                     .absolute_descendants
                                     .mark_as_having_reached_containing_block();
             }
 
             let construction_item = ConstructionItem::InlineFragments(
                     InlineFragmentsConstructionResult {
                 splits: opt_inline_block_splits,
@@ -939,27 +949,27 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
     /// `Fragment`.
     fn build_fragments_for_replaced_inline_content(&mut self, node: &ConcreteThreadSafeLayoutNode)
                                                    -> ConstructionResult {
         for kid in node.children() {
             self.set_flow_construction_result(&kid, ConstructionResult::None)
         }
 
         // If this node is ignorable whitespace, bail out now.
-        if node.is_ignorable_whitespace() {
+        if node.is_ignorable_whitespace(self.style_context()) {
             return ConstructionResult::ConstructionItem(ConstructionItem::Whitespace(
                 node.opaque(),
                 node.get_pseudo_element_type().strip(),
-                node.style().clone(),
+                node.style(self.style_context()).clone(),
                 node.restyle_damage()))
         }
 
         // Modify the style as necessary. (See the comment in
         // `properties::modify_style_for_replaced_content()`.)
-        let mut style = (*node.style()).clone();
+        let mut style = (*node.style(self.style_context())).clone();
         properties::modify_style_for_replaced_content(&mut style);
 
         // If this is generated content, then we need to initialize the accumulator with the
         // fragment corresponding to that content. Otherwise, just initialize with the ordinary
         // fragment that needs to be generated for this inline node.
         let mut fragments = IntermediateInlineFragments::new();
         match (node.get_pseudo_element_type(), node.type_id()) {
             (_, Some(NodeTypeId::CharacterData(CharacterDataTypeId::Text))) => {
@@ -982,24 +992,25 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
     fn build_fragment_for_inline_block(&mut self, node: &ConcreteThreadSafeLayoutNode)
                                        -> ConstructionResult {
         let block_flow_result = self.build_flow_for_block(node, None);
         let (block_flow, abs_descendants) = match block_flow_result {
             ConstructionResult::Flow(block_flow, abs_descendants) => (block_flow, abs_descendants),
             _ => unreachable!()
         };
 
-        let mut modified_style = (*node.style()).clone();
+        let style_context = self.style_context();
+        let mut modified_style = (*node.style(self.style_context())).clone();
         properties::modify_style_for_outer_inline_block_fragment(&mut modified_style);
         let fragment_info = SpecificFragmentInfo::InlineBlock(InlineBlockFragmentInfo::new(
                 block_flow));
         let fragment = Fragment::from_opaque_node_and_style(node.opaque(),
                                                             node.get_pseudo_element_type().strip(),
                                                             modified_style,
-                                                            node.selected_style().clone(),
+                                                            node.selected_style(style_context).clone(),
                                                             node.restyle_damage(),
                                                             fragment_info);
 
         let mut fragment_accumulator = InlineFragmentsAccumulator::new();
         fragment_accumulator.fragments.fragments.push_back(fragment);
         fragment_accumulator.fragments.absolute_descendants.push_descendants(abs_descendants);
 
         let construction_item =
@@ -1017,26 +1028,27 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
         let block_flow_result = self.build_flow_for_block(node, None);
         let (block_flow, abs_descendants) = match block_flow_result {
             ConstructionResult::Flow(block_flow, abs_descendants) => (block_flow, abs_descendants),
             _ => unreachable!()
         };
 
         let fragment_info = SpecificFragmentInfo::InlineAbsoluteHypothetical(
             InlineAbsoluteHypotheticalFragmentInfo::new(block_flow));
-        let mut style = node.style().clone();
+        let style_context = self.style_context();
+        let mut style = node.style(style_context).clone();
         properties::modify_style_for_inline_absolute_hypothetical_fragment(&mut style);
         let fragment = Fragment::from_opaque_node_and_style(node.opaque(),
                                                             PseudoElementType::Normal,
                                                             style,
-                                                            node.selected_style().clone(),
+                                                            node.selected_style(style_context).clone(),
                                                             node.restyle_damage(),
                                                             fragment_info);
 
-        let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node);
+        let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node, self.style_context());
         fragment_accumulator.fragments.fragments.push_back(fragment);
         fragment_accumulator.fragments.absolute_descendants.push_descendants(abs_descendants);
 
         let construction_item =
             ConstructionItem::InlineFragments(InlineFragmentsConstructionResult {
                 splits: LinkedList::new(),
                 fragments: fragment_accumulator.to_intermediate_inline_fragments(),
             });
@@ -1082,17 +1094,17 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
     }
 
     /// Generates an anonymous table flow according to CSS 2.1 § 17.2.1, step 2.
     /// If necessary, generate recursively another anonymous table flow.
     fn generate_anonymous_missing_child(&mut self,
                                         child_flows: Vec<FlowRef>,
                                         flow: &mut FlowRef,
                                         node: &ConcreteThreadSafeLayoutNode) {
-        let mut anonymous_flow = flow.generate_missing_child_flow(node);
+        let mut anonymous_flow = flow.generate_missing_child_flow(node, self.layout_context);
         let mut consecutive_siblings = vec!();
         for kid_flow in child_flows {
             if anonymous_flow.need_anonymous_flow(&*kid_flow) {
                 consecutive_siblings.push(kid_flow);
                 continue;
             }
             if !consecutive_siblings.is_empty() {
                 self.generate_anonymous_missing_child(consecutive_siblings,
@@ -1110,20 +1122,20 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
         flow.add_new_child(anonymous_flow);
     }
 
     /// Builds a flow for a node with `column-count` or `column-width` non-`auto`.
     /// This yields a `MulticolFlow` with a single `MulticolColumnFlow` underneath it.
     fn build_flow_for_multicol(&mut self, node: &ConcreteThreadSafeLayoutNode,
                                     float_kind: Option<FloatKind>)
                                     -> ConstructionResult {
-        let fragment = Fragment::new(node, SpecificFragmentInfo::Multicol);
+        let fragment = Fragment::new(node, SpecificFragmentInfo::Multicol, self.layout_context);
         let mut flow: FlowRef = Arc::new(MulticolFlow::from_fragment(fragment, float_kind));
 
-        let column_fragment = Fragment::new(node, SpecificFragmentInfo::MulticolColumn);
+        let column_fragment = Fragment::new(node, SpecificFragmentInfo::MulticolColumn, self.layout_context);
         let column_flow = Arc::new(MulticolColumnFlow::from_fragment(column_fragment));
 
         // First populate the column flow with its children.
         let construction_result = self.build_flow_for_block_like(column_flow, node);
 
         let mut abs_descendants = AbsoluteDescendants::new();
 
         if let ConstructionResult::Flow(column_flow, column_abs_descendants) = construction_result {
@@ -1151,21 +1163,21 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
 
         ConstructionResult::Flow(flow, abs_descendants)
     }
 
     /// Builds a flow for a node with `display: table`. This yields a `TableWrapperFlow` with
     /// possibly other `TableCaptionFlow`s or `TableFlow`s underneath it.
     fn build_flow_for_table_wrapper(&mut self, node: &ConcreteThreadSafeLayoutNode, float_value: float::T)
                                     -> ConstructionResult {
-        let fragment = Fragment::new(node, SpecificFragmentInfo::TableWrapper);
+        let fragment = Fragment::new(node, SpecificFragmentInfo::TableWrapper, self.layout_context);
         let mut wrapper_flow: FlowRef = Arc::new(
             TableWrapperFlow::from_fragment(fragment, FloatKind::from_property(float_value)));
 
-        let table_fragment = Fragment::new(node, SpecificFragmentInfo::Table);
+        let table_fragment = Fragment::new(node, SpecificFragmentInfo::Table, self.layout_context);
         let table_flow = Arc::new(TableFlow::from_fragment(table_fragment));
 
         // First populate the table flow with its children.
         let construction_result = self.build_flow_for_block_like(table_flow, node);
 
         let mut abs_descendants = AbsoluteDescendants::new();
 
         // The order of the caption and the table are not necessarily the same order as in the DOM
@@ -1213,94 +1225,95 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
         let flow = Arc::new(TableCaptionFlow::from_fragment(fragment));
         self.build_flow_for_block_like(flow, node)
     }
 
     /// Builds a flow for a node with `display: table-row-group`. This yields a `TableRowGroupFlow`
     /// with possibly other `TableRowFlow`s underneath it.
     fn build_flow_for_table_rowgroup(&mut self, node: &ConcreteThreadSafeLayoutNode)
                                      -> ConstructionResult {
-        let fragment = Fragment::new(node, SpecificFragmentInfo::TableRow);
+        let fragment = Fragment::new(node, SpecificFragmentInfo::TableRow, self.layout_context);
         let flow = Arc::new(TableRowGroupFlow::from_fragment(fragment));
         self.build_flow_for_block_like(flow, node)
     }
 
     /// Builds a flow for a node with `display: table-row`. This yields a `TableRowFlow` with
     /// possibly other `TableCellFlow`s underneath it.
     fn build_flow_for_table_row(&mut self, node: &ConcreteThreadSafeLayoutNode) -> ConstructionResult {
-        let fragment = Fragment::new(node, SpecificFragmentInfo::TableRow);
+        let fragment = Fragment::new(node, SpecificFragmentInfo::TableRow, self.layout_context);
         let flow = Arc::new(TableRowFlow::from_fragment(fragment));
         self.build_flow_for_block_like(flow, node)
     }
 
     /// Builds a flow for a node with `display: table-cell`. This yields a `TableCellFlow` with
     /// possibly other `BlockFlow`s or `InlineFlow`s underneath it.
     fn build_flow_for_table_cell(&mut self, node: &ConcreteThreadSafeLayoutNode) -> ConstructionResult {
-        let fragment = Fragment::new(node, SpecificFragmentInfo::TableCell);
+        let fragment = Fragment::new(node, SpecificFragmentInfo::TableCell, self.layout_context);
 
         // Determine if the table cell should be hidden. Per CSS 2.1 § 17.6.1.1, this will be true
         // if the cell has any in-flow elements (even empty ones!) and has `empty-cells` set to
         // `hide`.
-        let hide = node.style().get_inheritedtable().empty_cells == empty_cells::T::hide &&
+        let hide = node.style(self.style_context()).get_inheritedtable().empty_cells == empty_cells::T::hide &&
             node.children().all(|kid| {
-                let position = kid.style().get_box().position;
+                let position = kid.style(self.style_context()).get_box().position;
                 !kid.is_content() ||
                 position == position::T::absolute ||
                 position == position::T::fixed
             });
 
         let flow = Arc::new(
             TableCellFlow::from_node_fragment_and_visibility_flag(node, fragment, !hide));
         self.build_flow_for_block_like(flow, node)
     }
 
     /// Builds a flow for a node with `display: list-item`. This yields a `ListItemFlow` with
     /// possibly other `BlockFlow`s or `InlineFlow`s underneath it.
     fn build_flow_for_list_item(&mut self, node: &ConcreteThreadSafeLayoutNode, flotation: float::T)
                                 -> ConstructionResult {
         let flotation = FloatKind::from_property(flotation);
-        let marker_fragments = match node.style().get_list().list_style_image.0 {
+        let marker_fragments = match node.style(self.style_context()).get_list().list_style_image.0 {
             Some(ref url) => {
                 let image_info = box ImageFragmentInfo::new(node,
                                                             Some((*url).clone()),
                                                             &self.layout_context);
-                vec![Fragment::new(node, SpecificFragmentInfo::Image(image_info))]
+                vec![Fragment::new(node, SpecificFragmentInfo::Image(image_info), self.layout_context)]
             }
             None => {
-                match ListStyleTypeContent::from_list_style_type(node.style()
+                match ListStyleTypeContent::from_list_style_type(node.style(self.style_context())
                                                                      .get_list()
                                                                      .list_style_type) {
                     ListStyleTypeContent::None => Vec::new(),
                     ListStyleTypeContent::StaticText(ch) => {
                         let text = format!("{}\u{a0}", ch);
                         let mut unscanned_marker_fragments = LinkedList::new();
                         unscanned_marker_fragments.push_back(Fragment::new(
                             node,
                             SpecificFragmentInfo::UnscannedText(
-                                box UnscannedTextFragmentInfo::new(text, None))));
+                                box UnscannedTextFragmentInfo::new(text, None)),
+                            self.layout_context));
                         let marker_fragments = TextRunScanner::new().scan_for_runs(
                             &mut self.layout_context.font_context(),
                             unscanned_marker_fragments);
                         marker_fragments.fragments
                     }
                     ListStyleTypeContent::GeneratedContent(info) => {
-                        vec![Fragment::new(node, SpecificFragmentInfo::GeneratedContent(info))]
+                        vec![Fragment::new(node, SpecificFragmentInfo::GeneratedContent(info), self.layout_context)]
                     }
                 }
             }
         };
 
         // If the list marker is outside, it becomes the special "outside fragment" that list item
         // flows have. If it's inside, it's just a plain old fragment. Note that this means that
         // we adopt Gecko's behavior rather than WebKit's when the marker causes an {ib} split,
         // which has caused some malaise (Bugzilla #36854) but CSS 2.1 § 12.5.1 lets me do it, so
         // there.
         let mut initial_fragments = IntermediateInlineFragments::new();
         let main_fragment = self.build_fragment_for_block(node);
-        let flow = match node.style().get_list().list_style_position {
+        let flow = match node.style(self.style_context()).get_list().list_style_position {
             list_style_position::T::outside => {
                 Arc::new(ListItemFlow::from_fragments_and_flotation(
                     main_fragment, marker_fragments, flotation))
             }
             list_style_position::T::inside => {
                 for marker_fragment in marker_fragments {
                     initial_fragments.fragments.push_back(marker_fragment)
                 }
@@ -1317,40 +1330,42 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
                                         -> ConstructionResult {
         // CSS 2.1 § 17.2.1. Treat all child fragments of a `table-column` as `display: none`.
         for kid in node.children() {
             self.set_flow_construction_result(&kid, ConstructionResult::None)
         }
 
         let specific = SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node));
         let construction_item = ConstructionItem::TableColumnFragment(Fragment::new(node,
-                                                                                    specific));
+                                                                                    specific,
+                                                                                    self.layout_context));
         ConstructionResult::ConstructionItem(construction_item)
     }
 
     /// Builds a flow for a node with `display: table-column-group`.
     /// This yields a `TableColGroupFlow`.
     fn build_flow_for_table_colgroup(&mut self, node: &ConcreteThreadSafeLayoutNode)
                                      -> ConstructionResult {
         let fragment =
             Fragment::new(node,
-                          SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node)));
+                          SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node)),
+                          self.layout_context);
         let mut col_fragments = vec!();
         for kid in node.children() {
             // CSS 2.1 § 17.2.1. Treat all non-column child fragments of `table-column-group`
             // as `display: none`.
             if let ConstructionResult::ConstructionItem(ConstructionItem::TableColumnFragment(fragment))
                    = kid.swap_out_construction_result() {
                 col_fragments.push(fragment)
             }
         }
         if col_fragments.is_empty() {
             debug!("add SpecificFragmentInfo::TableColumn for empty colgroup");
             let specific = SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node));
-            col_fragments.push(Fragment::new(node, specific));
+            col_fragments.push(Fragment::new(node, specific, self.layout_context));
         }
         let mut flow: FlowRef = Arc::new(TableColGroupFlow::from_fragments(fragment, col_fragments));
         flow.finish();
 
         ConstructionResult::Flow(flow, AbsoluteDescendants::new())
     }
 
     /// Builds a flow for a node with 'display: flex'.
@@ -1382,21 +1397,21 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
         if need_to_reconstruct {
             return false
         }
 
         if node.restyle_damage().contains(RECONSTRUCT_FLOW) {
             return false
         }
 
-        if node.can_be_fragmented() || node.style().is_multicol() {
+        if node.can_be_fragmented() || node.style(self.style_context()).is_multicol() {
             return false
         }
 
-        let mut style = node.style().clone();
+        let mut style = node.style(self.style_context()).clone();
         let mut data = node.mutate_layout_data().unwrap();
         let damage = data.restyle_damage;
         match *node.construction_result_mut(&mut *data) {
             ConstructionResult::None => true,
             ConstructionResult::Flow(ref mut flow, _) => {
                 // The node's flow is of the same type and has the same set of children and can
                 // therefore be repaired by simply propagating damage and style to the flow.
                 if !flow.is_block_flow() {
@@ -1485,28 +1500,30 @@ impl<'a, ConcreteThreadSafeLayoutNode> P
     //
     // TODO: This should actually consult the table in that section to get the
     // final computed value for 'display'.
     fn process(&mut self, node: &ConcreteThreadSafeLayoutNode) -> bool {
         // Get the `display` property for this node, and determine whether this node is floated.
         let (display, float, positioning) = match node.type_id() {
             None => {
                 // Pseudo-element.
-                let style = node.style();
+                let style = node.style(self.style_context());
                 let display = match node.get_pseudo_element_type() {
-                    PseudoElementType::Normal => display::T::inline,
-                    PseudoElementType::Before(display) => display,
-                    PseudoElementType::After(display) => display,
-                    PseudoElementType::DetailsContent(display) => display,
-                    PseudoElementType::DetailsSummary(display) => display,
+                    PseudoElementType::Normal
+                        => display::T::inline,
+                    PseudoElementType::Before(maybe_display) |
+                    PseudoElementType::After(maybe_display) |
+                    PseudoElementType::DetailsContent(maybe_display) |
+                    PseudoElementType::DetailsSummary(maybe_display)
+                        => maybe_display.unwrap_or(style.get_box().display),
                 };
                 (display, style.get_box().float, style.get_box().position)
             }
             Some(NodeTypeId::Element(_)) => {
-                let style = node.style();
+                let style = node.style(self.style_context());
                 let munged_display = if style.get_box()._servo_display_for_hypothetical_box ==
                         display::T::inline {
                     display::T::inline
                 } else {
                     style.get_box().display
                 };
                 (munged_display, style.get_box().float, style.get_box().position)
             }
--- a/servo/components/layout/context.rs
+++ b/servo/components/layout/context.rs
@@ -103,17 +103,17 @@ pub struct SharedLayoutContext {
                                                   BuildHasherDefault<FnvHasher>>>>,
 }
 
 pub struct LayoutContext<'a> {
     pub shared: &'a SharedLayoutContext,
     cached_local_layout_context: Rc<LocalLayoutContext>,
 }
 
-impl<'a> StyleContext<'a, ServoSelectorImpl, ServoComputedValues> for LayoutContext<'a> {
+impl<'a> StyleContext<'a, ServoSelectorImpl> for LayoutContext<'a> {
     fn shared_context(&self) -> &'a SharedStyleContext {
         &self.shared.style_context
     }
 
     fn local_context(&self) -> &LocalStyleContext<ServoComputedValues> {
         &self.cached_local_layout_context.style_context
     }
 }
@@ -125,16 +125,21 @@ impl<'a> LayoutContext<'a> {
 
         LayoutContext {
             shared: shared_layout_context,
             cached_local_layout_context: local_context,
         }
     }
 
     #[inline(always)]
+    pub fn style_context(&self) -> &SharedStyleContext {
+        &self.shared.style_context
+    }
+
+    #[inline(always)]
     pub fn font_context(&self) -> RefMut<FontContext> {
         self.cached_local_layout_context.font_context.borrow_mut()
     }
 
     fn get_or_request_image_synchronously(&self, url: Url, use_placeholder: UsePlaceholder)
                                           -> Option<Arc<Image>> {
         debug_assert!(opts::get().output_file.is_some() || opts::get().exit_after_load);
 
--- a/servo/components/layout/data.rs
+++ b/servo/components/layout/data.rs
@@ -12,18 +12,19 @@ pub struct PrivateLayoutData {
     /// style system is being used standalone, this is all that hangs
     /// off the node. This must be first to permit the various
     /// transmuations between PrivateStyleData PrivateLayoutData.
     pub style_data: PrivateStyleData,
 
     /// Description of how to account for recent style changes.
     pub restyle_damage: RestyleDamage,
 
-    /// The current results of flow construction for this node. This is either a flow or a
-    /// `ConstructionItem`. See comments in `construct.rs` for more details.
+    /// The current results of flow construction for this node. This is either a
+    /// flow or a `ConstructionItem`. See comments in `construct.rs` for more
+    /// details.
     pub flow_construction_result: ConstructionResult,
 
     pub before_flow_construction_result: ConstructionResult,
 
     pub after_flow_construction_result: ConstructionResult,
 
     pub details_summary_flow_construction_result: ConstructionResult,
 
--- a/servo/components/layout/flow.rs
+++ b/servo/components/layout/flow.rs
@@ -478,17 +478,17 @@ pub trait ImmutableFlowUtils {
 
     /// Returns true if this flow is one of table-related flows.
     fn is_table_kind(self) -> bool;
 
     /// Returns true if anonymous flow is needed between this flow and child flow.
     fn need_anonymous_flow(self, child: &Flow) -> bool;
 
     /// Generates missing child flow of this flow.
-    fn generate_missing_child_flow<N: ThreadSafeLayoutNode>(self, node: &N) -> FlowRef;
+    fn generate_missing_child_flow<N: ThreadSafeLayoutNode>(self, node: &N, ctx: &LayoutContext) -> FlowRef;
 
     /// Returns true if this flow contains fragments that are roots of an absolute flow tree.
     fn contains_roots_of_absolute_flow_tree(&self) -> bool;
 
     /// Returns true if this flow has no children.
     fn is_leaf(self) -> bool;
 
     /// Returns the number of children that this flow possesses.
@@ -1270,52 +1270,53 @@ impl<'a> ImmutableFlowUtils for &'a Flow
         }
     }
 
     /// Generates missing child flow of this flow.
     ///
     /// FIXME(pcwalton): This duplicates some logic in
     /// `generate_anonymous_table_flows_if_necessary()`. We should remove this function eventually,
     /// as it's harder to understand.
-    fn generate_missing_child_flow<N: ThreadSafeLayoutNode>(self, node: &N) -> FlowRef {
-        let mut style = node.style().clone();
+    fn generate_missing_child_flow<N: ThreadSafeLayoutNode>(self, node: &N, ctx: &LayoutContext) -> FlowRef {
+        let style_context = ctx.style_context();
+        let mut style = node.style(style_context).clone();
         match self.class() {
             FlowClass::Table | FlowClass::TableRowGroup => {
                 properties::modify_style_for_anonymous_table_object(
                     &mut style,
                     display::T::table_row);
                 let fragment = Fragment::from_opaque_node_and_style(
                     node.opaque(),
                     PseudoElementType::Normal,
                     style,
-                    node.selected_style().clone(),
+                    node.selected_style(style_context).clone(),
                     node.restyle_damage(),
                     SpecificFragmentInfo::TableRow);
                 Arc::new(TableRowFlow::from_fragment(fragment))
             },
             FlowClass::TableRow => {
                 properties::modify_style_for_anonymous_table_object(
                     &mut style,
                     display::T::table_cell);
                 let fragment = Fragment::from_opaque_node_and_style(
                     node.opaque(),
                     PseudoElementType::Normal,
                     style,
-                    node.selected_style().clone(),
+                    node.selected_style(style_context).clone(),
                     node.restyle_damage(),
                     SpecificFragmentInfo::TableCell);
-                let hide = node.style().get_inheritedtable().empty_cells == empty_cells::T::hide;
+                let hide = node.style(style_context).get_inheritedtable().empty_cells == empty_cells::T::hide;
                 Arc::new(TableCellFlow::from_node_fragment_and_visibility_flag(node, fragment, !hide))
             },
             FlowClass::Flex => {
                 let fragment =
                     Fragment::from_opaque_node_and_style(node.opaque(),
                                                          PseudoElementType::Normal,
                                                          style,
-                                                         node.selected_style().clone(),
+                                                         node.selected_style(style_context).clone(),
                                                          node.restyle_damage(),
                                                          SpecificFragmentInfo::Generic);
                 Arc::new(BlockFlow::from_fragment(fragment, None))
             },
             _ => {
                 panic!("no need to generate a missing child")
             }
         }
--- a/servo/components/layout/fragment.rs
+++ b/servo/components/layout/fragment.rs
@@ -314,19 +314,19 @@ impl InlineAbsoluteFragmentInfo {
 pub struct CanvasFragmentInfo {
     pub replaced_image_fragment_info: ReplacedImageFragmentInfo,
     pub ipc_renderer: Option<Arc<Mutex<IpcSender<CanvasMsg>>>>,
     pub dom_width: Au,
     pub dom_height: Au,
 }
 
 impl CanvasFragmentInfo {
-    pub fn new<N: ThreadSafeLayoutNode>(node: &N, data: HTMLCanvasData) -> CanvasFragmentInfo {
+    pub fn new<N: ThreadSafeLayoutNode>(node: &N, data: HTMLCanvasData, ctx: &LayoutContext) -> CanvasFragmentInfo {
         CanvasFragmentInfo {
-            replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node),
+            replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node, ctx),
             ipc_renderer: data.ipc_renderer
                               .map(|renderer| Arc::new(Mutex::new(renderer))),
             dom_width: Au::from_px(data.width as i32),
             dom_height: Au::from_px(data.height as i32),
         }
     }
 
     /// Returns the original inline-size of the canvas.
@@ -377,17 +377,17 @@ impl ImageFragmentInfo {
                 (None, Some(m))
             }
             None => {
                 (None, None)
             }
         };
 
         ImageFragmentInfo {
-            replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node),
+            replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node, layout_context),
             image: image,
             metadata: metadata,
         }
     }
 
     /// Returns the original inline-size of the image.
     pub fn image_inline_size(&mut self) -> Au {
         match self.metadata {
@@ -436,19 +436,19 @@ impl ImageFragmentInfo {
 #[derive(Clone)]
 pub struct ReplacedImageFragmentInfo {
     pub computed_inline_size: Option<Au>,
     pub computed_block_size: Option<Au>,
     pub writing_mode_is_vertical: bool,
 }
 
 impl ReplacedImageFragmentInfo {
-    pub fn new<N>(node: &N) -> ReplacedImageFragmentInfo
+    pub fn new<N>(node: &N, ctx: &LayoutContext) -> ReplacedImageFragmentInfo
             where N: ThreadSafeLayoutNode {
-        let is_vertical = node.style().writing_mode.is_vertical();
+        let is_vertical = node.style(ctx.style_context()).writing_mode.is_vertical();
         ReplacedImageFragmentInfo {
             computed_inline_size: None,
             computed_block_size: None,
             writing_mode_is_vertical: is_vertical,
         }
     }
 
     /// Returns the calculated inline-size of the image, accounting for the inline-size attribute.
@@ -784,27 +784,28 @@ impl TableColumnFragmentInfo {
         TableColumnFragmentInfo {
             span: span,
         }
     }
 }
 
 impl Fragment {
     /// Constructs a new `Fragment` instance.
-    pub fn new<N: ThreadSafeLayoutNode>(node: &N, specific: SpecificFragmentInfo) -> Fragment {
-        let style = node.style().clone();
+    pub fn new<N: ThreadSafeLayoutNode>(node: &N, specific: SpecificFragmentInfo, ctx: &LayoutContext) -> Fragment {
+        let style_context = ctx.style_context();
+        let style = node.style(style_context).clone();
         let writing_mode = style.writing_mode;
 
         let mut restyle_damage = node.restyle_damage();
         restyle_damage.remove(RECONSTRUCT_FLOW);
 
         Fragment {
             node: node.opaque(),
             style: style,
-            selected_style: node.selected_style().clone(),
+            selected_style: node.selected_style(style_context).clone(),
             restyle_damage: restyle_damage,
             border_box: LogicalRect::zero(writing_mode),
             border_padding: LogicalMargin::zero(writing_mode),
             margin: LogicalMargin::zero(writing_mode),
             specific: specific,
             inline_context: None,
             pseudo: node.get_pseudo_element_type().strip(),
             flags: FragmentFlags::empty(),
--- a/servo/components/layout/layout_thread.rs
+++ b/servo/components/layout/layout_thread.rs
@@ -62,24 +62,23 @@ use std::cell::RefCell;
 use std::collections::HashMap;
 use std::hash::BuildHasherDefault;
 use std::ops::{Deref, DerefMut};
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::sync::mpsc::{channel, Sender, Receiver};
 use std::sync::{Arc, Mutex, MutexGuard, RwLock};
 use style::animation::Animation;
 use style::computed_values::{filter, mix_blend_mode};
-use style::context::{ReflowGoal, StylistWrapper};
+use style::context::{ReflowGoal};
 use style::dom::{TDocument, TElement, TNode};
 use style::error_reporting::ParseErrorReporter;
 use style::logical_geometry::LogicalPoint;
 use style::media_queries::{Device, MediaType};
 use style::parallel::WorkQueueData;
 use style::properties::ComputedValues;
-use style::selector_impl::ServoSelectorImpl;
 use style::selector_matching::USER_OR_USER_AGENT_STYLESHEETS;
 use style::servo::{SharedStyleContext, Stylesheet, Stylist};
 use style::stylesheets::CSSRuleIteratorExt;
 use traversal::RecalcStyleAndConstructFlows;
 use url::Url;
 use util::geometry::MAX_RECT;
 use util::ipc::OptionalIpcSender;
 use util::opts;
@@ -102,17 +101,17 @@ const DISPLAY_PORT_THRESHOLD_SIZE_FACTOR
 pub struct LayoutThreadData {
     /// The channel on which messages can be sent to the constellation.
     pub constellation_chan: ConstellationChan<ConstellationMsg>,
 
     /// The root stacking context.
     pub display_list: Option<Arc<DisplayList>>,
 
     /// Performs CSS selector matching and style resolution.
-    pub stylist: Box<Stylist>,
+    pub stylist: Arc<Stylist>,
 
     /// A queued response for the union of the content boxes of a node.
     pub content_box_response: Rect<Au>,
 
     /// A queued response for the content boxes of a node.
     pub content_boxes_response: Vec<Rect<Au>>,
 
     /// A queued response for the client {top, left, width, height} of a node in pixels.
@@ -416,17 +415,17 @@ impl LayoutThread {
         let image_cache_receiver =
             ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_image_cache_receiver);
 
         // Ask the router to proxy IPC messages from the font cache thread to the layout thread.
         let (ipc_font_cache_sender, ipc_font_cache_receiver) = ipc::channel().unwrap();
         let font_cache_receiver =
             ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_font_cache_receiver);
 
-        let stylist = box Stylist::new(device);
+        let stylist = Arc::new(Stylist::new(device));
         let outstanding_web_fonts_counter = Arc::new(AtomicUsize::new(0));
         for stylesheet in &*USER_OR_USER_AGENT_STYLESHEETS {
             add_font_face_rules(stylesheet,
                                 &stylist.device,
                                 &font_cache_thread,
                                 &ipc_font_cache_sender,
                                 &outstanding_web_fonts_counter);
         }
@@ -505,17 +504,17 @@ impl LayoutThread {
                                    screen_size_changed: bool,
                                    url: &Url,
                                    goal: ReflowGoal)
                                    -> SharedLayoutContext {
         SharedLayoutContext {
             style_context: SharedStyleContext {
                 viewport_size: self.viewport_size.clone(),
                 screen_size_changed: screen_size_changed,
-                stylist: StylistWrapper::<ServoSelectorImpl>(&*rw_data.stylist),
+                stylist: rw_data.stylist.clone(),
                 generation: self.generation,
                 goal: goal,
                 new_animations_sender: Mutex::new(self.new_animations_sender.clone()),
                 running_animations: self.running_animations.clone(),
                 expired_animations: self.expired_animations.clone(),
                 error_reporter: self.error_reporter.clone(),
             },
             image_cache_thread: self.image_cache_thread.clone(),
@@ -804,17 +803,17 @@ impl LayoutThread {
         }
 
         possibly_locked_rw_data.block(rw_data);
     }
 
     /// Sets quirks mode for the document, causing the quirks mode stylesheet to be used.
     fn handle_set_quirks_mode<'a, 'b>(&self, possibly_locked_rw_data: &mut RwData<'a, 'b>) {
         let mut rw_data = possibly_locked_rw_data.lock();
-        rw_data.stylist.set_quirks_mode(true);
+        Arc::get_mut(&mut rw_data.stylist).unwrap().set_quirks_mode(true);
         possibly_locked_rw_data.block(rw_data);
     }
 
     fn try_get_layout_root<N: LayoutNode>(&self, node: N) -> Option<FlowRef> {
         let mut data = match node.mutate_layout_data() {
             Some(x) => x,
             None => return None,
         };
@@ -1055,17 +1054,17 @@ impl LayoutThread {
 
         let initial_viewport = data.window_size.initial_viewport;
         let old_viewport_size = self.viewport_size;
         let current_screen_size = Size2D::new(Au::from_f32_px(initial_viewport.width.get()),
                                               Au::from_f32_px(initial_viewport.height.get()));
 
         // Calculate the actual viewport as per DEVICE-ADAPT § 6
         let device = Device::new(MediaType::Screen, initial_viewport);
-        rw_data.stylist.set_device(device, &data.document_stylesheets);
+        Arc::get_mut(&mut rw_data.stylist).unwrap().set_device(device, &data.document_stylesheets);
 
         let constraints = rw_data.stylist.viewport_constraints().clone();
         self.viewport_size = match constraints {
             Some(ref constraints) => {
                 debug!("Viewport constraints: {:?}", constraints);
 
                 // other rules are evaluated against the actual viewport
                 Size2D::new(Au::from_f32_px(constraints.size.width.get()),
@@ -1087,18 +1086,18 @@ impl LayoutThread {
             }
             // FIXME (#10104): Only dirty nodes affected by vh/vw/vmin/vmax styles.
             if data.document_stylesheets.iter().any(|sheet| sheet.dirty_on_viewport_size_change) {
                 needs_dirtying = true;
             }
         }
 
         // If the entire flow tree is invalid, then it will be reflowed anyhow.
-        needs_dirtying |= rw_data.stylist.update(&data.document_stylesheets,
-                                                 data.stylesheets_changed);
+        needs_dirtying |= Arc::get_mut(&mut rw_data.stylist).unwrap().update(&data.document_stylesheets,
+                                                                             data.stylesheets_changed);
         let needs_reflow = viewport_size_changed && !needs_dirtying;
         unsafe {
             if needs_dirtying {
                 LayoutThread::dirty_all_nodes(node);
             }
         }
         if needs_reflow {
             if let Some(mut flow) = self.try_get_layout_root(node) {
--- a/servo/components/layout/lib.rs
+++ b/servo/components/layout/lib.rs
@@ -98,8 +98,9 @@ mod table_rowgroup;
 mod table_wrapper;
 mod text;
 mod traversal;
 mod webrender_helpers;
 mod wrapper;
 
 // For unit tests:
 pub use fragment::Fragment;
+pub use wrapper::ServoThreadSafeLayoutNode;
--- a/servo/components/layout/query.rs
+++ b/servo/components/layout/query.rs
@@ -569,33 +569,33 @@ pub fn process_node_scroll_area_request<
 /// https://drafts.csswg.org/cssom/#resolved-value
 pub fn process_resolved_style_request<N: LayoutNode>(
             requested_node: N, pseudo: &Option<PseudoElement>,
             property: &Atom, layout_root: &mut FlowRef) -> Option<String> {
     let layout_node = requested_node.to_threadsafe();
     let layout_node = match *pseudo {
         Some(PseudoElement::Before) => layout_node.get_before_pseudo(),
         Some(PseudoElement::After) => layout_node.get_after_pseudo(),
-        Some(PseudoElement::DetailsSummary) => layout_node.get_details_summary_pseudo(),
-        Some(PseudoElement::DetailsContent) => layout_node.get_details_content_pseudo(),
+        Some(PseudoElement::DetailsSummary) |
+        Some(PseudoElement::DetailsContent) |
         Some(PseudoElement::Selection) => None,
         _ => Some(layout_node)
     };
 
     let layout_node = match layout_node {
         None => {
             // The pseudo doesn't exist, return nothing.  Chrome seems to query
             // the element itself in this case, Firefox uses the resolved value.
             // https://www.w3.org/Bugs/Public/show_bug.cgi?id=29006
             return None;
         }
         Some(layout_node) => layout_node
     };
 
-    let style = &*layout_node.style();
+    let style = &*layout_node.resolved_style();
 
     let positioned = match style.get_box().position {
         position::computed_value::T::relative |
         /*position::computed_value::T::sticky |*/
         position::computed_value::T::fixed |
         position::computed_value::T::absolute => true,
         _ => false
     };
@@ -706,26 +706,26 @@ pub fn process_offset_parent_query<N: La
         None => {
             OffsetParentResponse::empty()
         }
     }
 }
 
 pub fn process_node_overflow_request<N: LayoutNode>(requested_node: N) -> NodeOverflowResponse {
     let layout_node = requested_node.to_threadsafe();
-    let style = &*layout_node.style();
+    let style = &*layout_node.resolved_style();
     let style_box = style.get_box();
 
     NodeOverflowResponse(Some((Point2D::new(style_box.overflow_x, style_box.overflow_y.0))))
 }
 
 pub fn process_margin_style_query<N: LayoutNode>(requested_node: N)
         -> MarginStyleResponse {
     let layout_node = requested_node.to_threadsafe();
-    let style = &*layout_node.style();
+    let style = &*layout_node.resolved_style();
     let margin = style.get_margin();
 
     MarginStyleResponse {
         top: margin.margin_top,
         right: margin.margin_right,
         bottom: margin.margin_bottom,
         left: margin.margin_left,
     }
--- a/servo/components/layout/wrapper.rs
+++ b/servo/components/layout/wrapper.rs
@@ -67,17 +67,17 @@ use string_cache::{Atom, Namespace};
 use style::computed_values::content::ContentItem;
 use style::computed_values::{content, display};
 use style::dom::{TDocument, TElement, TNode, UnsafeNode};
 use style::element_state::*;
 use style::properties::{ComputedValues, ServoComputedValues};
 use style::properties::{PropertyDeclaration, PropertyDeclarationBlock};
 use style::restyle_hints::ElementSnapshot;
 use style::selector_impl::{NonTSPseudoClass, PseudoElement, ServoSelectorImpl};
-use style::servo::PrivateStyleData;
+use style::servo::{PrivateStyleData, SharedStyleContext};
 use url::Url;
 use util::str::is_whitespace;
 
 pub type NonOpaqueStyleAndLayoutData = *mut RefCell<PrivateLayoutData>;
 
 /// A wrapper so that layout can access only the methods that it should have access to. Layout must
 /// only ever see these and must never see instances of `LayoutJS`.
 
@@ -423,24 +423,24 @@ impl<'le> TElement for ServoLayoutElemen
         where V: VecLike<DeclarationBlock<Vec<PropertyDeclaration>>>
     {
         unsafe {
             self.element.synthesize_presentational_hints_for_legacy_attributes(hints);
         }
     }
 
     #[inline]
-    fn get_attr<'a>(&'a self, namespace: &Namespace, name: &Atom) -> Option<&'a str> {
+    fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&str> {
         unsafe {
             (*self.element.unsafe_get()).get_attr_val_for_layout(namespace, name)
         }
     }
 
     #[inline]
-    fn get_attrs<'a>(&'a self, name: &Atom) -> Vec<&'a str> {
+    fn get_attrs(&self, name: &Atom) -> Vec<&str> {
         unsafe {
             (*self.element.unsafe_get()).get_attr_vals_for_layout(name)
         }
     }
 }
 
 
 impl<'le> ServoLayoutElement<'le> {
@@ -645,28 +645,38 @@ impl<T> PseudoElementType<T> {
         match *self {
             PseudoElementType::Normal => PseudoElementType::Normal,
             PseudoElementType::Before(_) => PseudoElementType::Before(()),
             PseudoElementType::After(_) => PseudoElementType::After(()),
             PseudoElementType::DetailsSummary(_) => PseudoElementType::DetailsSummary(()),
             PseudoElementType::DetailsContent(_) => PseudoElementType::DetailsContent(()),
         }
     }
+
+    pub fn style_pseudo_element(&self) -> PseudoElement {
+        match *self {
+            PseudoElementType::Normal => unreachable!("style_pseudo_element called with PseudoElementType::Normal"),
+            PseudoElementType::Before(_) => PseudoElement::Before,
+            PseudoElementType::After(_) => PseudoElement::After,
+            PseudoElementType::DetailsSummary(_) => PseudoElement::DetailsSummary,
+            PseudoElementType::DetailsContent(_) => PseudoElement::DetailsContent,
+        }
+    }
 }
 
 /// A thread-safe version of `LayoutNode`, used during flow construction. This type of layout
 /// node does not allow any parents or siblings of nodes to be accessed, to avoid races.
 
 pub trait ThreadSafeLayoutNode : Clone + Copy + Sized + PartialEq {
     type ConcreteThreadSafeLayoutElement: ThreadSafeLayoutElement<ConcreteThreadSafeLayoutNode = Self>;
     type ChildrenIterator: Iterator<Item = Self> + Sized;
 
     /// Creates a new `ThreadSafeLayoutNode` for the same `LayoutNode`
     /// with a different pseudo-element type.
-    fn with_pseudo(&self, pseudo: PseudoElementType<display::T>) -> Self;
+    fn with_pseudo(&self, pseudo: PseudoElementType<Option<display::T>>) -> Self;
 
     /// Converts self into an `OpaqueNode`.
     fn opaque(&self) -> OpaqueNode;
 
     /// Returns the type ID of this node.
     /// Returns `None` if this is a pseudo-element; otherwise, returns `Some`.
     fn type_id(&self) -> Option<NodeTypeId>;
 
@@ -680,68 +690,62 @@ pub trait ThreadSafeLayoutNode : Clone +
     #[inline]
     fn is_element(&self) -> bool { if let Some(NodeTypeId::Element(_)) = self.type_id() { true } else { false } }
 
     /// If this is an element, accesses the element data. Fails if this is not an element node.
     #[inline]
     fn as_element(&self) -> Self::ConcreteThreadSafeLayoutElement;
 
     #[inline]
-    fn get_pseudo_element_type(&self) -> PseudoElementType<display::T>;
+    fn get_pseudo_element_type(&self) -> PseudoElementType<Option<display::T>>;
 
     #[inline]
     fn get_before_pseudo(&self) -> Option<Self> {
-        self.borrow_layout_data().unwrap()
-            .style_data.per_pseudo
-            .get(&PseudoElement::Before)
-            .map(|style| {
-                self.with_pseudo(PseudoElementType::Before(style.get_box().display))
-            })
+        if self.borrow_layout_data().unwrap()
+               .style_data.per_pseudo
+               .contains_key(&PseudoElement::Before) {
+            Some(self.with_pseudo(PseudoElementType::Before(None)))
+        } else {
+            None
+        }
     }
 
     #[inline]
     fn get_after_pseudo(&self) -> Option<Self> {
-        self.borrow_layout_data().unwrap()
-            .style_data.per_pseudo
-            .get(&PseudoElement::After)
-            .map(|style| {
-                self.with_pseudo(PseudoElementType::After(style.get_box().display))
-            })
+        if self.borrow_layout_data().unwrap()
+               .style_data.per_pseudo
+               .contains_key(&PseudoElement::After) {
+            Some(self.with_pseudo(PseudoElementType::After(None)))
+        } else {
+            None
+        }
     }
 
-    // TODO(emilio): Since the ::-details-* pseudos are internal, just affecting one element, and
-    // only changing `display` property when the element `open` attribute changes, this should be
-    // eligible for not being cascaded eagerly, reading the display property from layout instead.
     #[inline]
     fn get_details_summary_pseudo(&self) -> Option<Self> {
         if self.is_element() &&
            self.as_element().get_local_name() == &atom!("details") &&
            self.as_element().get_namespace() == &ns!(html) {
-            self.borrow_layout_data().unwrap()
-                .style_data.per_pseudo
-                .get(&PseudoElement::DetailsSummary)
-                .map(|style| {
-                    self.with_pseudo(PseudoElementType::DetailsSummary(style.get_box().display))
-                })
+            Some(self.with_pseudo(PseudoElementType::DetailsSummary(None)))
         } else {
             None
         }
     }
 
     #[inline]
     fn get_details_content_pseudo(&self) -> Option<Self> {
         if self.is_element() &&
            self.as_element().get_local_name() == &atom!("details") &&
            self.as_element().get_namespace() == &ns!(html) {
-            self.borrow_layout_data().unwrap()
-                .style_data.per_pseudo
-                .get(&PseudoElement::DetailsContent)
-                .map(|style| {
-                    self.with_pseudo(PseudoElementType::DetailsContent(style.get_box().display))
-                })
+            let display = if self.as_element().get_attr(&ns!(), &atom!("open")).is_some() {
+                None // Specified by the stylesheet
+            } else {
+                Some(display::T::none)
+            };
+            Some(self.with_pseudo(PseudoElementType::DetailsContent(display)))
         } else {
             None
         }
     }
 
     /// Borrows the layout data immutably. Fails on a conflicting borrow.
     ///
     /// TODO(pcwalton): Make this private. It will let us avoid borrow flag checks in some cases.
@@ -754,63 +758,88 @@ pub trait ThreadSafeLayoutNode : Clone +
     #[inline(always)]
     fn mutate_layout_data(&self) -> Option<RefMut<PrivateLayoutData>>;
 
     /// Returns the style results for the given node. If CSS selector matching
     /// has not yet been performed, fails.
     ///
     /// Unlike the version on TNode, this handles pseudo-elements.
     #[inline]
-    fn style(&self) -> Ref<Arc<ServoComputedValues>> {
+    fn style(&self, context: &SharedStyleContext) -> Ref<Arc<ServoComputedValues>> {
+        match self.get_pseudo_element_type() {
+            PseudoElementType::Normal => {
+                Ref::map(self.borrow_layout_data().unwrap(), |data| {
+                    data.style_data.style.as_ref().unwrap()
+                })
+            },
+            other => {
+                // Precompute non-eagerly-cascaded pseudo-element styles if not
+                // cached before.
+                let style_pseudo = other.style_pseudo_element();
+                if !style_pseudo.is_eagerly_cascaded() &&
+                   !self.borrow_layout_data().unwrap().style_data.per_pseudo.contains_key(&style_pseudo) {
+                    let mut data = self.mutate_layout_data().unwrap();
+                    let new_style = context.stylist
+                                           .computed_values_for_pseudo(&style_pseudo,
+                                                                       data.style_data.style.as_ref());
+                    data.style_data.per_pseudo.insert(style_pseudo.clone(), new_style.unwrap());
+                }
+
+                Ref::map(self.borrow_layout_data().unwrap(), |data| {
+                    data.style_data.per_pseudo.get(&style_pseudo).unwrap()
+                })
+            }
+        }
+    }
+
+    /// Returns the already resolved style of the node.
+    ///
+    /// This differs from `style(ctx)` in that if the pseudo-element has not yet
+    /// been computed it would panic.
+    ///
+    /// This should be used just for querying layout, or when we know the
+    /// element style is precomputed, not from general layout itself.
+    #[inline]
+    fn resolved_style(&self) -> Ref<Arc<ServoComputedValues>> {
         Ref::map(self.borrow_layout_data().unwrap(), |data| {
-            let style = match self.get_pseudo_element_type() {
-                PseudoElementType::Before(_) => data.style_data.per_pseudo.get(&PseudoElement::Before),
-                PseudoElementType::After(_) => data.style_data.per_pseudo.get(&PseudoElement::After),
-                PseudoElementType::DetailsSummary(_) => data.style_data.per_pseudo.get(&PseudoElement::DetailsSummary),
-                PseudoElementType::DetailsContent(_) => data.style_data.per_pseudo.get(&PseudoElement::DetailsContent),
-                PseudoElementType::Normal => data.style_data.style.as_ref(),
-            };
-            style.unwrap()
+            match self.get_pseudo_element_type() {
+                PseudoElementType::Normal
+                    => data.style_data.style.as_ref().unwrap(),
+                other
+                    => data.style_data.per_pseudo.get(&other.style_pseudo_element()).unwrap(),
+            }
         })
     }
 
     #[inline]
-    fn selected_style(&self) -> Ref<Arc<ServoComputedValues>> {
+    fn selected_style(&self, _context: &SharedStyleContext) -> Ref<Arc<ServoComputedValues>> {
         Ref::map(self.borrow_layout_data().unwrap(), |data| {
-            data.style_data.per_pseudo.get(&PseudoElement::Selection).unwrap_or(data.style_data.style.as_ref().unwrap())
+            data.style_data.per_pseudo
+                .get(&PseudoElement::Selection)
+                .unwrap_or(data.style_data.style.as_ref().unwrap())
         })
     }
 
     /// Removes the style from this node.
     ///
     /// Unlike the version on TNode, this handles pseudo-elements.
     fn unstyle(self) {
         let mut data = self.mutate_layout_data().unwrap();
 
         match self.get_pseudo_element_type() {
-            PseudoElementType::Before(_) => {
-                data.style_data.per_pseudo.remove(&PseudoElement::Before);
-            }
-            PseudoElementType::After(_) => {
-                data.style_data.per_pseudo.remove(&PseudoElement::After);
-            }
-            PseudoElementType::DetailsSummary(_) => {
-                data.style_data.per_pseudo.remove(&PseudoElement::DetailsSummary);
-            }
-            PseudoElementType::DetailsContent(_) => {
-                data.style_data.per_pseudo.remove(&PseudoElement::DetailsContent);
-            }
-
             PseudoElementType::Normal => {
                 data.style_data.style = None;
             }
+            other => {
+                data.style_data.per_pseudo.remove(&other.style_pseudo_element());
+            }
         };
     }
 
-    fn is_ignorable_whitespace(&self) -> bool;
+    fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool;
 
     fn restyle_damage(self) -> RestyleDamage;
 
     fn set_restyle_damage(self, damage: RestyleDamage);
 
     /// Returns the layout data flags for this node.
     fn flags(self) -> LayoutDataFlags;
 
@@ -879,31 +908,33 @@ pub trait DangerousThreadSafeLayoutNode 
     unsafe fn dangerous_first_child(&self) -> Option<Self>;
     unsafe fn dangerous_next_sibling(&self) -> Option<Self>;
 }
 
 pub trait ThreadSafeLayoutElement: Clone + Copy + Sized {
     type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<ConcreteThreadSafeLayoutElement = Self>;
 
     #[inline]
-    fn get_attr<'a>(&'a self, namespace: &Namespace, name: &Atom) -> Option<&'a str>;
+    fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&str>;
 
     #[inline]
     fn get_local_name(&self) -> &Atom;
 
     #[inline]
     fn get_namespace(&self) -> &Namespace;
 }
 
 #[derive(Copy, Clone)]
 pub struct ServoThreadSafeLayoutNode<'ln> {
     /// The wrapped node.
     node: ServoLayoutNode<'ln>,
 
-    pseudo: PseudoElementType<display::T>,
+    /// The pseudo-element type, with (optionally),
+    /// an specified display value to override the stylesheet.
+    pseudo: PseudoElementType<Option<display::T>>,
 }
 
 impl<'a> PartialEq for ServoThreadSafeLayoutNode<'a> {
     #[inline]
     fn eq(&self, other: &ServoThreadSafeLayoutNode<'a>) -> bool {
         self.node == other.node
     }
 }
@@ -943,17 +974,18 @@ impl<'ln> ServoThreadSafeLayoutNode<'ln>
         self.node.get_jsmanaged()
     }
 }
 
 impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> {
     type ConcreteThreadSafeLayoutElement = ServoThreadSafeLayoutElement<'ln>;
     type ChildrenIterator = ThreadSafeLayoutNodeChildrenIterator<Self>;
 
-    fn with_pseudo(&self, pseudo: PseudoElementType<display::T>) -> ServoThreadSafeLayoutNode<'ln> {
+    fn with_pseudo(&self,
+                   pseudo: PseudoElementType<Option<display::T>>) -> ServoThreadSafeLayoutNode<'ln> {
         ServoThreadSafeLayoutNode {
             node: self.node.clone(),
             pseudo: pseudo,
         }
     }
 
     fn opaque(&self) -> OpaqueNode {
         OpaqueNodeMethods::from_jsmanaged(unsafe { self.get_jsmanaged() })
@@ -988,46 +1020,46 @@ impl<'ln> ThreadSafeLayoutNode for Servo
             // FIXME(pcwalton): Workaround until Rust gets multiple lifetime parameters on
             // implementations.
             ServoThreadSafeLayoutElement {
                 element: &*element,
             }
         }
     }
 
-    fn get_pseudo_element_type(&self) -> PseudoElementType<display::T> {
+    fn get_pseudo_element_type(&self) -> PseudoElementType<Option<display::T>> {
         self.pseudo
     }
 
     fn borrow_layout_data(&self) -> Option<Ref<PrivateLayoutData>> {
         self.node.borrow_layout_data()
     }
 
     fn mutate_layout_data(&self) -> Option<RefMut<PrivateLayoutData>> {
         self.node.mutate_layout_data()
     }
 
-    fn is_ignorable_whitespace(&self) -> bool {
+    fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool {
         unsafe {
             let text: LayoutJS<Text> = match self.get_jsmanaged().downcast() {
                 Some(text) => text,
                 None => return false
             };
 
             if !is_whitespace(text.upcast().data_for_layout()) {
                 return false
             }
 
             // NB: See the rules for `white-space` here:
             //
             //    http://www.w3.org/TR/CSS21/text.html#propdef-white-space
             //
             // If you implement other values for this property, you will almost certainly
             // want to update this check.
-            !self.style().get_inheritedtext().white_space.preserve_newlines()
+            !self.style(context).get_inheritedtext().white_space.preserve_newlines()
         }
     }
 
     fn restyle_damage(self) -> RestyleDamage {
         self.node.restyle_damage()
     }
 
     fn set_restyle_damage(self, damage: RestyleDamage) {
@@ -1041,23 +1073,17 @@ impl<'ln> ThreadSafeLayoutNode for Servo
     }
 
     fn can_be_fragmented(&self) -> bool {
         self.node.can_be_fragmented()
     }
 
     fn text_content(&self) -> TextContent {
         if self.pseudo.is_replaced_content() {
-            let data = &self.borrow_layout_data().unwrap().style_data;
-
-            let style = if self.pseudo.is_before() {
-                data.per_pseudo.get(&PseudoElement::Before).unwrap()
-            } else {
-                data.per_pseudo.get(&PseudoElement::After).unwrap()
-            };
+            let style = self.resolved_style();
 
             return match style.as_ref().get_counters().content {
                 content::T::Content(ref value) if !value.is_empty() => {
                     TextContent::GeneratedContent((*value).clone())
                 }
                 _ => TextContent::GeneratedContent(vec![]),
             };
         }
@@ -1073,17 +1099,17 @@ impl<'ln> ThreadSafeLayoutNode for Servo
             let data = unsafe { input.value_for_layout() };
             return TextContent::Text(data);
         }
         if let Some(area) = this.downcast::<HTMLTextAreaElement>() {
             let data = unsafe { area.get_value_for_layout() };
             return TextContent::Text(data);
         }
 
-        panic!("not text!")
+        unreachable!("not text!")
     }
 
     fn selection(&self) -> Option<Range<ByteIndex>> {
         let this = unsafe { self.get_jsmanaged() };
 
         let selection = if let Some(area) = this.downcast::<HTMLTextAreaElement>() {
             unsafe { area.selection_for_layout() }
         } else if let Some(input) = this.downcast::<HTMLInputElement>() {
@@ -1159,18 +1185,18 @@ impl<ConcreteNode> Iterator for ThreadSa
         match self.parent_node.get_pseudo_element_type() {
             PseudoElementType::Before(_) | PseudoElementType::After(_) => None,
 
             PseudoElementType::DetailsSummary(_) => {
                 let mut current_node = self.current_node.clone();
                 loop {
                     let next_node = if let Some(ref node) = current_node {
                         if node.is_element() &&
-                                node.as_element().get_local_name() == &atom!("summary") &&
-                                node.as_element().get_namespace() == &ns!(html) {
+                           node.as_element().get_local_name() == &atom!("summary") &&
+                           node.as_element().get_namespace() == &ns!(html) {
                             self.current_node = None;
                             return Some(node.clone());
                         }
                         unsafe { node.dangerous_next_sibling() }
                     } else {
                         self.current_node = None;
                         return None
                     };
--- a/servo/components/servo/Cargo.lock
+++ b/servo/components/servo/Cargo.lock
@@ -154,16 +154,21 @@ version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "bitflags"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "bitflags"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "block"
 version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "brotli"
 version = "0.3.20"
 source = "git+https://github.com/ende76/brotli-rs#c243045b88b2d2924c35269586fa9b770184c74c"
@@ -722,17 +727,17 @@ dependencies = [
  "range 0.0.1",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo-fontconfig 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo-skia 0.20130412.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "simd 0.1.0 (git+https://github.com/huonw/simd)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "style 0.0.1",
  "style_traits 0.0.1",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-script 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
  "webrender_traits 0.1.0 (git+https://github.com/servo/webrender_traits)",
 ]
@@ -896,17 +901,17 @@ source = "registry+https://github.com/ru
 dependencies = [
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "mac 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_codegen 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "tendril 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "httparse"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1087,21 +1092,21 @@ dependencies = [
  "msg 0.0.1",
  "net_traits 0.0.1",
  "plugins 0.0.1",
  "profile_traits 0.0.1",
  "range 0.0.1",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "script 0.0.1",
  "script_traits 0.0.1",
- "selectors 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "selectors 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "style 0.0.1",
  "style_traits 0.0.1",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-script 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
  "webrender_traits 0.1.0 (git+https://github.com/servo/webrender_traits)",
@@ -1822,20 +1827,20 @@ dependencies = [
  "plugins 0.0.1",
  "profile_traits 0.0.1",
  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "ref_filter_map 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "ref_slice 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 0.1.55 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "script_traits 0.0.1",
- "selectors 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "selectors 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "style 0.0.1",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
  "uuid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "webrender_traits 0.1.0 (git+https://github.com/servo/webrender_traits)",
  "websocket 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1876,28 +1881,28 @@ dependencies = [
  "style_traits 0.0.1",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
 ]
 
 [[package]]
 name = "selectors"
-version = "0.5.2"
+version = "0.5.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "quickersort 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "semver"
 version = "0.1.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -2048,17 +2053,17 @@ version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "string_cache"
-version = "0.2.12"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "debug_unreachable 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_generator 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_shared 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2078,39 +2083,39 @@ dependencies = [
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
  "plugins 0.0.1",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "selectors 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "selectors 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "style_traits 0.0.1",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
 ]
 
 [[package]]
 name = "style_tests"
 version = "0.0.1"
 dependencies = [
  "app_units 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "msg 0.0.1",
  "plugins 0.0.1",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "selectors 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "selectors 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "style 0.0.1",
  "style_traits 0.0.1",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
 ]
 
 [[package]]
 name = "style_traits"
@@ -2119,17 +2124,17 @@ dependencies = [
  "cssparser 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "plugins 0.0.1",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "selectors 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "selectors 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
 ]
 
 [[package]]
 name = "task_info"
@@ -2302,17 +2307,17 @@ dependencies = [
  "num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "plugins 0.0.1",
  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "util_tests"
 version = "0.0.1"
 dependencies = [
  "app_units 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2529,13 +2534,13 @@ name = "xml5ever"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "mac 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_codegen 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "tendril 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
--- a/servo/components/style/context.rs
+++ b/servo/components/style/context.rs
@@ -11,33 +11,25 @@ use matching::{ApplicableDeclarationsCac
 use properties::ComputedValues;
 use selector_impl::SelectorImplExt;
 use selector_matching::Stylist;
 use std::cell::RefCell;
 use std::collections::HashMap;
 use std::sync::mpsc::Sender;
 use std::sync::{Arc, Mutex, RwLock};
 
-pub struct StylistWrapper<Impl: SelectorImplExt>(pub *const Stylist<Impl>);
-
-// FIXME(#6569) This implementation is unsound.
-#[allow(unsafe_code)]
-unsafe impl<Impl: SelectorImplExt> Sync for StylistWrapper<Impl> {}
-
 pub struct SharedStyleContext<Impl: SelectorImplExt> {
     /// The current viewport size.
     pub viewport_size: Size2D<Au>,
 
     /// Screen sized changed?
     pub screen_size_changed: bool,
 
     /// The CSS selector stylist.
-    ///
-    /// FIXME(#2604): Make this no longer an unsafe pointer once we have fast `RWArc`s.
-    pub stylist: StylistWrapper<Impl>,
+    pub stylist: Arc<Stylist<Impl>>,
 
     /// Starts at zero, and increased by one every time a layout completes.
     /// This can be used to easily check for invalid stale data.
     pub generation: u32,
 
     /// A channel on which new animations that have been triggered by style recalculation can be
     /// sent.
     pub new_animations_sender: Mutex<Sender<Animation>>,
@@ -55,23 +47,21 @@ pub struct SharedStyleContext<Impl: Sele
     pub error_reporter: Box<ParseErrorReporter + Sync>,
 }
 
 pub struct LocalStyleContext<C: ComputedValues> {
     pub applicable_declarations_cache: RefCell<ApplicableDeclarationsCache<C>>,
     pub style_sharing_candidate_cache: RefCell<StyleSharingCandidateCache<C>>,
 }
 
-pub trait StyleContext<'a, Impl: SelectorImplExt, C: ComputedValues> {
-
+pub trait StyleContext<'a, Impl: SelectorImplExt> {
     fn shared_context(&self) -> &'a SharedStyleContext<Impl>;
-    fn local_context(&self) -> &LocalStyleContext<C>;
+    fn local_context(&self) -> &LocalStyleContext<Impl::ComputedValues>;
 }
 
 /// Why we're doing reflow.
 #[derive(PartialEq, Copy, Clone, Debug)]
 pub enum ReflowGoal {
     /// We're reflowing in order to send a display list to the screen.
     ForDisplay,
     /// We're reflowing in order to satisfy a script query. No display list will be created.
     ForScriptQuery,
 }
-
--- a/servo/components/style/dom.rs
+++ b/servo/components/style/dom.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/. */
 
 #![allow(unsafe_code)]
 
+use context::SharedStyleContext;
 use data::PrivateStyleData;
 use element_state::ElementState;
 use properties::{ComputedValues, PropertyDeclaration, PropertyDeclarationBlock};
 use restyle_hints::{ElementSnapshot, RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint};
-use selector_impl::ElementExt;
+use selector_impl::{ElementExt, SelectorImplExt};
 use selectors::Element;
 use selectors::matching::DeclarationBlock;
 use smallvec::VecLike;
 use std::cell::{Ref, RefMut};
 use std::ops::BitOr;
 use std::sync::Arc;
 use string_cache::{Atom, Namespace};
 
@@ -132,29 +133,29 @@ pub trait TNode : Sized + Copy + Clone {
     fn can_be_fragmented(&self) -> bool;
 
     unsafe fn set_can_be_fragmented(&self, value: bool);
 
     /// Borrows the PrivateStyleData without checks.
     #[inline(always)]
     unsafe fn borrow_data_unchecked(&self)
         -> Option<*const PrivateStyleData<<Self::ConcreteElement as Element>::Impl,
-                                          Self::ConcreteComputedValues>>;
+                                           Self::ConcreteComputedValues>>;
 
     /// Borrows the PrivateStyleData immutably. Fails on a conflicting borrow.
     #[inline(always)]
     fn borrow_data(&self)
         -> Option<Ref<PrivateStyleData<<Self::ConcreteElement as Element>::Impl,
-                                       Self::ConcreteComputedValues>>>;
+                                           Self::ConcreteComputedValues>>>;
 
     /// Borrows the PrivateStyleData mutably. Fails on a conflicting borrow.
     #[inline(always)]
     fn mutate_data(&self)
         -> Option<RefMut<PrivateStyleData<<Self::ConcreteElement as Element>::Impl,
-                                          Self::ConcreteComputedValues>>>;
+                                           Self::ConcreteComputedValues>>>;
 
     /// Get the description of how to account for recent style changes.
     fn restyle_damage(self) -> Self::ConcreteRestyleDamage;
 
     /// Set the restyle damage field.
     fn set_restyle_damage(self, damage: Self::ConcreteRestyleDamage);
 
     fn parent_node(&self) -> Option<Self>;
@@ -165,17 +166,20 @@ pub trait TNode : Sized + Copy + Clone {
 
     fn prev_sibling(&self) -> Option<Self>;
 
     fn next_sibling(&self) -> Option<Self>;
 
 
     /// Returns the style results for the given node. If CSS selector matching
     /// has not yet been performed, fails.
-    fn style(&self) -> Ref<Arc<Self::ConcreteComputedValues>> {
+    fn style(&self,
+             _context: &SharedStyleContext<<Self::ConcreteElement as Element>::Impl>)
+        -> Ref<Arc<Self::ConcreteComputedValues>>
+        where <Self::ConcreteElement as Element>::Impl: SelectorImplExt<ComputedValues=Self::ConcreteComputedValues> {
         Ref::map(self.borrow_data().unwrap(), |data| data.style.as_ref().unwrap())
     }
 
     /// Removes the style from this node.
     fn unstyle(self) {
         self.mutate_data().unwrap().style = None;
     }
 }
--- a/servo/components/style/restyle_hints.rs
+++ b/servo/components/style/restyle_hints.rs
@@ -131,17 +131,17 @@ impl<'a, E> Element for ElementWrapper<'
         self.element.next_sibling_element().map(ElementWrapper::new)
     }
     fn is_html_element_in_html_document(&self) -> bool {
         self.element.is_html_element_in_html_document()
     }
     fn get_local_name(&self) -> &Atom {
         self.element.get_local_name()
     }
-    fn get_namespace<'b>(&self) -> &Namespace {
+    fn get_namespace(&self) -> &Namespace {
         self.element.get_namespace()
     }
     fn get_id(&self) -> Option<Atom> {
         match self.snapshot.attrs {
             Some(_) => self.snapshot.get_attr(&ns!(), &atom!("id")).map(|value| value.as_atom().clone()),
             None => self.element.get_id(),
         }
     }
--- a/servo/components/style/selector_impl.rs
+++ b/servo/components/style/selector_impl.rs
@@ -1,24 +1,67 @@
 /* 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 element_state::ElementState;
+use properties::{self, ServoComputedValues};
 use selector_matching::{USER_OR_USER_AGENT_STYLESHEETS, QUIRKS_MODE_STYLESHEET};
 use selectors::Element;
 use selectors::parser::{ParserContext, SelectorImpl};
 use stylesheets::Stylesheet;
 
 pub trait ElementExt: Element {
     fn is_link(&self) -> bool;
 }
 
 pub trait SelectorImplExt : SelectorImpl + Sized {
+    type ComputedValues: properties::ComputedValues;
+
+    fn each_pseudo_element<F>(mut fun: F)
+        where F: FnMut(<Self as SelectorImpl>::PseudoElement);
+
+    /// This function determines if a pseudo-element is eagerly cascaded or not.
+    ///
+    /// Eagerly cascaded pseudo-elements are "normal" pseudo-elements (i.e.
+    /// `::before` and `::after`). They inherit styles normally as another
+    /// selector would do.
+    ///
+    /// Non-eagerly cascaded ones skip the cascade process entirely, mostly as
+    /// an optimisation since they are private pseudo-elements (like
+    /// `::-servo-details-content`). This pseudo-elements are resolved on the
+    /// fly using global rules (rules of the form `*|*`), and applying them to
+    /// the parent style.
+    ///
+    /// If you're implementing a public selector that the end-user might
+    /// customize, then you probably need doing the whole cascading process and
+    /// return true in this function for that pseudo.
+    ///
+    /// But if you are implementing a private pseudo-element, please consider if
+    /// it might be possible to skip the cascade for it.
+    fn is_eagerly_cascaded_pseudo_element(pseudo: &<Self as SelectorImpl>::PseudoElement) -> bool;
+
+    #[inline]
     fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
-        where F: FnMut(<Self as SelectorImpl>::PseudoElement);
+        where F: FnMut(<Self as SelectorImpl>::PseudoElement) {
+        Self::each_pseudo_element(|pseudo| {
+            if Self::is_eagerly_cascaded_pseudo_element(&pseudo) {
+                fun(pseudo)
+            }
+        })
+    }
+
+    #[inline]
+    fn each_non_eagerly_cascaded_pseudo_element<F>(mut fun: F)
+        where F: FnMut(<Self as SelectorImpl>::PseudoElement) {
+        Self::each_pseudo_element(|pseudo| {
+            if !Self::is_eagerly_cascaded_pseudo_element(&pseudo) {
+                fun(pseudo)
+            }
+        })
+    }
 
     fn pseudo_class_state_flag(pc: &Self::NonTSPseudoClass) -> ElementState;
 
     fn get_user_or_user_agent_stylesheets() -> &'static [Stylesheet<Self>];
 
     fn get_quirks_mode_stylesheet() -> Option<&'static Stylesheet<Self>>;
 }
 
@@ -26,16 +69,29 @@ pub trait SelectorImplExt : SelectorImpl
 pub enum PseudoElement {
     Before,
     After,
     Selection,
     DetailsSummary,
     DetailsContent,
 }
 
+impl PseudoElement {
+    #[inline]
+    pub fn is_eagerly_cascaded(&self) -> bool {
+        match *self {
+            PseudoElement::Before |
+            PseudoElement::After |
+            PseudoElement::Selection |
+            PseudoElement::DetailsSummary => true,
+            PseudoElement::DetailsContent => false,
+        }
+    }
+}
+
 #[derive(Clone, Debug, PartialEq, Eq, HeapSizeOf, Hash)]
 pub enum NonTSPseudoClass {
     AnyLink,
     Link,
     Visited,
     Active,
     Focus,
     Hover,
@@ -107,42 +163,45 @@ impl SelectorImpl for ServoSelectorImpl 
 
     fn parse_pseudo_element(context: &ParserContext,
                             name: &str) -> Result<PseudoElement, ()> {
         use self::PseudoElement::*;
         let pseudo_element = match_ignore_ascii_case! { name,
             "before" => Before,
             "after" => After,
             "selection" => Selection,
-            "-servo-details-summary" => if context.in_user_agent_stylesheet {
+            "-servo-details-summary" => {
+                if !context.in_user_agent_stylesheet {
+                    return Err(())
+                }
                 DetailsSummary
-            } else {
-                return Err(())
             },
-            "-servo-details-content" => if context.in_user_agent_stylesheet {
+            "-servo-details-content" => {
+                if !context.in_user_agent_stylesheet {
+                    return Err(())
+                }
                 DetailsContent
-            } else {
-                return Err(())
             },
             _ => return Err(())
         };
 
         Ok(pseudo_element)
     }
 }
 
-impl<E: Element<Impl=ServoSelectorImpl>> ElementExt for E {
-    fn is_link(&self) -> bool {
-        self.match_non_ts_pseudo_class(NonTSPseudoClass::AnyLink)
+impl SelectorImplExt for ServoSelectorImpl {
+    type ComputedValues = ServoComputedValues;
+
+    #[inline]
+    fn is_eagerly_cascaded_pseudo_element(pseudo: &PseudoElement) -> bool {
+        pseudo.is_eagerly_cascaded()
     }
-}
 
-impl SelectorImplExt for ServoSelectorImpl {
     #[inline]
-    fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
+    fn each_pseudo_element<F>(mut fun: F)
         where F: FnMut(PseudoElement) {
         fun(PseudoElement::Before);
         fun(PseudoElement::After);
         fun(PseudoElement::DetailsContent);
         fun(PseudoElement::DetailsSummary);
         fun(PseudoElement::Selection);
     }
 
@@ -156,8 +215,14 @@ impl SelectorImplExt for ServoSelectorIm
         &*USER_OR_USER_AGENT_STYLESHEETS
     }
 
     #[inline]
     fn get_quirks_mode_stylesheet() -> Option<&'static Stylesheet<Self>> {
         Some(&*QUIRKS_MODE_STYLESHEET)
     }
 }
+
+impl<E: Element<Impl=ServoSelectorImpl>> ElementExt for E {
+    fn is_link(&self) -> bool {
+        self.match_non_ts_pseudo_class(NonTSPseudoClass::AnyLink)
+    }
+}
--- a/servo/components/style/selector_matching.rs
+++ b/servo/components/style/selector_matching.rs
@@ -3,18 +3,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // For lazy_static
 #![allow(unsafe_code)]
 
 use dom::TElement;
 use element_state::*;
 use error_reporting::{ParseErrorReporter, StdoutErrorReporter};
+use euclid::Size2D;
 use media_queries::{Device, MediaType};
-use properties::{PropertyDeclaration, PropertyDeclarationBlock};
+use properties::{self, ComputedValues, PropertyDeclaration, PropertyDeclarationBlock};
 use restyle_hints::{ElementSnapshot, RestyleHint, DependencySet};
 use selector_impl::{SelectorImplExt, ServoSelectorImpl};
 use selectors::Element;
 use selectors::bloom::BloomFilter;
 use selectors::matching::DeclarationBlock as GenericDeclarationBlock;
 use selectors::matching::{Rule, SelectorMap};
 use selectors::parser::SelectorImpl;
 use smallvec::VecLike;
@@ -111,21 +112,30 @@ pub struct Stylist<Impl: SelectorImplExt
     quirks_mode: bool,
 
     /// If true, the device has changed, and the stylist needs to be updated.
     is_device_dirty: bool,
 
     /// The current selector maps, after evaluating media
     /// rules against the current device.
     element_map: PerPseudoElementSelectorMap<Impl>,
+
     /// The selector maps corresponding to a given pseudo-element
     /// (depending on the implementation)
     pseudos_map: HashMap<Impl::PseudoElement,
                          PerPseudoElementSelectorMap<Impl>,
                          BuildHasherDefault<::fnv::FnvHasher>>,
+
+    /// Applicable declarations for a given non-eagerly cascaded pseudo-element.
+    /// These are eagerly computed once, and then used to resolve the new
+    /// computed values on the fly on layout.
+    non_eagerly_cascaded_pseudo_element_decls: HashMap<Impl::PseudoElement,
+                                                       Vec<DeclarationBlock>,
+                                                       BuildHasherDefault<::fnv::FnvHasher>>,
+
     rules_source_order: usize,
 
     /// Selector dependencies used to compute restyle hints.
     state_deps: DependencySet<Impl>,
 }
 
 impl<Impl: SelectorImplExt> Stylist<Impl> {
     #[inline]
@@ -133,16 +143,17 @@ impl<Impl: SelectorImplExt> Stylist<Impl
         let mut stylist = Stylist {
             viewport_constraints: None,
             device: device,
             is_device_dirty: true,
             quirks_mode: false,
 
             element_map: PerPseudoElementSelectorMap::new(),
             pseudos_map: HashMap::with_hasher(Default::default()),
+            non_eagerly_cascaded_pseudo_element_decls: HashMap::with_hasher(Default::default()),
             rules_source_order: 0,
             state_deps: DependencySet::new(),
         };
 
         Impl::each_eagerly_cascaded_pseudo_element(|pseudo| {
             stylist.pseudos_map.insert(pseudo, PerPseudoElementSelectorMap::new());
         });
 
@@ -155,16 +166,21 @@ impl<Impl: SelectorImplExt> Stylist<Impl
                   stylesheets_changed: bool) -> bool
                   where Impl: 'static {
         if !(self.is_device_dirty || stylesheets_changed) {
             return false;
         }
 
         self.element_map = PerPseudoElementSelectorMap::new();
         self.pseudos_map = HashMap::with_hasher(Default::default());
+        Impl::each_eagerly_cascaded_pseudo_element(|pseudo| {
+            self.pseudos_map.insert(pseudo, PerPseudoElementSelectorMap::new());
+        });
+
+        self.non_eagerly_cascaded_pseudo_element_decls = HashMap::with_hasher(Default::default());
         self.rules_source_order = 0;
         self.state_deps.clear();
 
         for ref stylesheet in Impl::get_user_or_user_agent_stylesheets().iter() {
             self.add_stylesheet(&stylesheet);
         }
 
         if self.quirks_mode {
@@ -177,58 +193,91 @@ impl<Impl: SelectorImplExt> Stylist<Impl
             self.add_stylesheet(stylesheet);
         }
 
         self.is_device_dirty = false;
         true
     }
 
     fn add_stylesheet(&mut self, stylesheet: &Stylesheet<Impl>) {
-        let device = &self.device;
-        if !stylesheet.is_effective_for_device(device) {
+        if !stylesheet.is_effective_for_device(&self.device) {
             return;
         }
         let mut rules_source_order = self.rules_source_order;
 
         // Take apart the StyleRule into individual Rules and insert
         // them into the SelectorMap of that priority.
         macro_rules! append(
             ($style_rule: ident, $priority: ident) => {
                 if !$style_rule.declarations.$priority.is_empty() {
                     for selector in &$style_rule.selectors {
                         let map = if let Some(ref pseudo) = selector.pseudo_element {
-                            self.pseudos_map.entry(pseudo.clone())
-                                            .or_insert_with(PerPseudoElementSelectorMap::new)
-                                            .borrow_for_origin(&stylesheet.origin)
+                            self.pseudos_map
+                                .entry(pseudo.clone())
+                                .or_insert_with(PerPseudoElementSelectorMap::new)
+                                .borrow_for_origin(&stylesheet.origin)
                         } else {
                             self.element_map.borrow_for_origin(&stylesheet.origin)
                         };
 
                         map.$priority.insert(Rule {
-                                selector: selector.compound_selectors.clone(),
-                                declarations: DeclarationBlock {
-                                    specificity: selector.specificity,
-                                    declarations: $style_rule.declarations.$priority.clone(),
-                                    source_order: rules_source_order,
-                                },
+                            selector: selector.compound_selectors.clone(),
+                            declarations: DeclarationBlock {
+                                specificity: selector.specificity,
+                                declarations: $style_rule.declarations.$priority.clone(),
+                                source_order: rules_source_order,
+                            },
                         });
                     }
                 }
             };
         );
 
         for style_rule in stylesheet.effective_rules(&self.device).style() {
             append!(style_rule, normal);
             append!(style_rule, important);
             rules_source_order += 1;
             for selector in &style_rule.selectors {
                 self.state_deps.note_selector(selector.compound_selectors.clone());
             }
         }
+
         self.rules_source_order = rules_source_order;
+
+        Impl::each_non_eagerly_cascaded_pseudo_element(|pseudo| {
+            // TODO: Don't precompute this, compute it on demand instead and
+            // cache it.
+            //
+            // This is actually kind of hard, because the stylist is shared
+            // between threads.
+            if let Some(map) = self.pseudos_map.remove(&pseudo) {
+                let mut declarations = vec![];
+
+                map.user_agent.normal.get_universal_rules(&mut declarations);
+                map.user_agent.important.get_universal_rules(&mut declarations);
+
+                self.non_eagerly_cascaded_pseudo_element_decls.insert(pseudo, declarations);
+            }
+        })
+    }
+
+    pub fn computed_values_for_pseudo(&self,
+                                      pseudo: &Impl::PseudoElement,
+                                      parent: Option<&Arc<Impl::ComputedValues>>) -> Option<Arc<Impl::ComputedValues>> {
+        debug_assert!(!Impl::is_eagerly_cascaded_pseudo_element(pseudo));
+        if let Some(declarations) = self.non_eagerly_cascaded_pseudo_element_decls.get(pseudo) {
+            let (computed, _) =
+                properties::cascade::<Impl::ComputedValues>(Size2D::zero(),
+                                                            &declarations, false,
+                                                            parent.map(|p| &**p), None,
+                                                            box StdoutErrorReporter);
+            Some(Arc::new(computed))
+        } else {
+            parent.map(|p| p.clone())
+        }
     }
 
     pub fn compute_restyle_hint<E>(&self, element: &E,
                                    snapshot: &ElementSnapshot,
                                    // NB: We need to pass current_state as an argument because
                                    // selectors::Element doesn't provide access to ElementState
                                    // directly, and computing it from the ElementState would be
                                    // more expensive than getting it directly from the caller.
@@ -279,30 +328,26 @@ impl<Impl: SelectorImplExt> Stylist<Impl
                                         pseudo_element: Option<Impl::PseudoElement>,
                                         applicable_declarations: &mut V)
                                         -> bool
                                         where E: Element<Impl=Impl> + TElement,
                                               V: VecLike<DeclarationBlock> {
         assert!(!self.is_device_dirty);
         assert!(style_attribute.is_none() || pseudo_element.is_none(),
                 "Style attributes do not apply to pseudo-elements");
+        debug_assert!(pseudo_element.is_none() ||
+                      Impl::is_eagerly_cascaded_pseudo_element(pseudo_element.as_ref().unwrap()));
 
         let map = match pseudo_element {
-            Some(ref pseudo) => match self.pseudos_map.get(pseudo) {
-                Some(map) => map,
-                // TODO(emilio): get non eagerly-cascaded pseudo-element rules here.
-                // Actually assume there are no rules applicable.
-                None => return true,
-            },
+            Some(ref pseudo) => self.pseudos_map.get(pseudo).unwrap(),
             None => &self.element_map,
         };
 
         let mut shareable = true;
 
-
         // Step 1: Normal user-agent rules.
         map.user_agent.normal.get_all_matching_rules(element,
                                                      parent_bf,
                                                      applicable_declarations,
                                                      &mut shareable);
 
         // Step 2: Presentational hints.
         let length = applicable_declarations.len();
--- a/servo/components/style/servo.rs
+++ b/servo/components/style/servo.rs
@@ -7,10 +7,9 @@ use properties::ServoComputedValues;
 use selector_impl::ServoSelectorImpl;
 use selector_matching;
 use stylesheets;
 
 /// Concrete types for servo Style implementation
 pub type Stylesheet = stylesheets::Stylesheet<ServoSelectorImpl>;
 pub type PrivateStyleData = data::PrivateStyleData<ServoSelectorImpl, ServoComputedValues>;
 pub type Stylist = selector_matching::Stylist<ServoSelectorImpl>;
-pub type StylistWrapper = context::StylistWrapper<ServoSelectorImpl>;
 pub type SharedStyleContext = context::SharedStyleContext<ServoSelectorImpl>;
--- a/servo/components/style/traversal.rs
+++ b/servo/components/style/traversal.rs
@@ -118,18 +118,18 @@ pub trait DomTraversalContext<N: TNode> 
 /// The recalc-style-for-node traversal, which styles each node and must run before
 /// layout computation. This computes the styles applied to each node.
 #[inline]
 #[allow(unsafe_code)]
 pub fn recalc_style_at<'a, N, C>(context: &'a C,
                                  root: OpaqueNode,
                                  node: N)
     where N: TNode,
-          C: StyleContext<'a, <N::ConcreteElement as Element>::Impl, N::ConcreteComputedValues>,
-          <N::ConcreteElement as Element>::Impl: SelectorImplExt + 'a {
+          C: StyleContext<'a, <N::ConcreteElement as Element>::Impl>,
+          <N::ConcreteElement as Element>::Impl: SelectorImplExt<ComputedValues=N::ConcreteComputedValues> + 'a {
     // Initialize layout data.
     //
     // FIXME(pcwalton): Stop allocating here. Ideally this should just be done by the HTML
     // parser.
     node.initialize_data();
 
     // Get the parent node.
     let parent_opt = node.layout_parent_node(root);
@@ -162,18 +162,19 @@ pub fn recalc_style_at<'a, N, C>(context
         // Otherwise, match and cascade selectors.
         match sharing_result {
             StyleSharingResult::CannotShare => {
                 let mut applicable_declarations = ApplicableDeclarations::new();
 
                 let shareable_element = match node.as_element() {
                     Some(element) => {
                         // Perform the CSS selector matching.
-                        let stylist = unsafe { &*context.shared_context().stylist.0 };
-                        if element.match_element(stylist,
+                        let stylist = &context.shared_context().stylist;
+
+                        if element.match_element(&**stylist,
                                                  Some(&*bf),
                                                  &mut applicable_declarations) {
                             Some(element)
                         } else {
                             None
                         }
                     },
                     None => {
--- a/servo/ports/cef/Cargo.lock
+++ b/servo/ports/cef/Cargo.lock
@@ -139,16 +139,21 @@ version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "bitflags"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "bitflags"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "block"
 version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "brotli"
 version = "0.3.20"
 source = "git+https://github.com/ende76/brotli-rs#c243045b88b2d2924c35269586fa9b770184c74c"
@@ -651,17 +656,17 @@ dependencies = [
  "range 0.0.1",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo-fontconfig 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo-skia 0.20130412.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "simd 0.1.0 (git+https://github.com/huonw/simd)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "style 0.0.1",
  "style_traits 0.0.1",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-script 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
  "webrender_traits 0.1.0 (git+https://github.com/servo/webrender_traits)",
 ]
@@ -816,17 +821,17 @@ source = "registry+https://github.com/ru
 dependencies = [
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "mac 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_codegen 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "tendril 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "httparse"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1007,21 +1012,21 @@ dependencies = [
  "msg 0.0.1",
  "net_traits 0.0.1",
  "plugins 0.0.1",
  "profile_traits 0.0.1",
  "range 0.0.1",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "script 0.0.1",
  "script_traits 0.0.1",
- "selectors 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "selectors 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "style 0.0.1",
  "style_traits 0.0.1",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-script 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
  "webrender_traits 0.1.0 (git+https://github.com/servo/webrender_traits)",
@@ -1690,20 +1695,20 @@ dependencies = [
  "plugins 0.0.1",
  "profile_traits 0.0.1",
  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "ref_filter_map 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "ref_slice 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 0.1.55 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "script_traits 0.0.1",
- "selectors 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "selectors 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "style 0.0.1",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
  "uuid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "webrender_traits 0.1.0 (git+https://github.com/servo/webrender_traits)",
  "websocket 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1733,28 +1738,28 @@ dependencies = [
  "style_traits 0.0.1",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
 ]
 
 [[package]]
 name = "selectors"
-version = "0.5.2"
+version = "0.5.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "quickersort 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "semver"
 version = "0.1.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -1942,17 +1947,17 @@ version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "string_cache"
-version = "0.2.12"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "debug_unreachable 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_generator 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_shared 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1972,21 +1977,21 @@ dependencies = [
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
  "plugins 0.0.1",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "selectors 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "selectors 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "style_traits 0.0.1",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
 ]
 
 [[package]]
 name = "style_traits"
@@ -1995,17 +2000,17 @@ dependencies = [
  "cssparser 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "plugins 0.0.1",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "selectors 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "selectors 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
 ]
 
 [[package]]
 name = "task_info"
@@ -2178,17 +2183,17 @@ dependencies = [
  "num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "plugins 0.0.1",
  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "uuid"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
@@ -2394,13 +2399,13 @@ name = "xml5ever"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "mac 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_codegen 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "tendril 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
--- a/servo/ports/geckolib/Cargo.lock
+++ b/servo/ports/geckolib/Cargo.lock
@@ -8,19 +8,19 @@ dependencies = [
  "euclid 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "plugins 0.0.1",
- "selectors 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "selectors 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "style 0.0.1",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
 ]
 
 [[package]]
 name = "app_units"
 version = "0.2.3"
@@ -77,16 +77,21 @@ version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "bitflags"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "bitflags"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "byteorder"
 version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "cfg-if"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -417,28 +422,28 @@ dependencies = [
 
 [[package]]
 name = "rustc-serialize"
 version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "selectors"
-version = "0.5.2"
+version = "0.5.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "quickersort 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "serde"
 version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -461,17 +466,17 @@ dependencies = [
 
 [[package]]
 name = "smallvec"
 version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "string_cache"
-version = "0.2.12"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "debug_unreachable 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_generator 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_shared 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -491,21 +496,21 @@ dependencies = [
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
  "plugins 0.0.1",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "selectors 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "selectors 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "style_traits 0.0.1",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
 ]
 
 [[package]]
 name = "style_traits"
@@ -514,17 +519,17 @@ dependencies = [
  "cssparser 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "plugins 0.0.1",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "selectors 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "selectors 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
 ]
 
 [[package]]
 name = "tenacious"
@@ -595,17 +600,17 @@ dependencies = [
  "num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "plugins 0.0.1",
  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "uuid"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
--- a/servo/ports/geckolib/bindings.rs
+++ b/servo/ports/geckolib/bindings.rs
@@ -1,10 +1,11 @@
 /* automatically generated by rust-bindgen */
 
+use gecko_style_structs::SheetParsingMode;
 use gecko_style_structs::nsStyleFont;
 use gecko_style_structs::nsStyleColor;
 use gecko_style_structs::nsStyleList;
 use gecko_style_structs::nsStyleText;
 use gecko_style_structs::nsStyleVisibility;
 use gecko_style_structs::nsStyleUserInterface;
 use gecko_style_structs::nsStyleTableBorder;
 use gecko_style_structs::nsStyleSVG;
@@ -63,17 +64,18 @@ extern "C" {
     pub fn Gecko_IsTextNode(node: *mut RawGeckoNode) -> bool;
     pub fn Gecko_IsVisitedLink(element: *mut RawGeckoElement) -> bool;
     pub fn Gecko_IsUnvisitedLink(element: *mut RawGeckoElement) -> bool;
     pub fn Gecko_IsRootElement(element: *mut RawGeckoElement) -> bool;
     pub fn Gecko_GetNodeData(node: *mut RawGeckoNode) -> *mut ServoNodeData;
     pub fn Gecko_SetNodeData(node: *mut RawGeckoNode,
                              data: *mut ServoNodeData);
     pub fn Servo_DropNodeData(data: *mut ServoNodeData);
-    pub fn Servo_StylesheetFromUTF8Bytes(bytes: *const u8, length: u32)
+    pub fn Servo_StylesheetFromUTF8Bytes(bytes: *const u8, length: u32,
+                                         parsing_mode: SheetParsingMode)
      -> *mut RawServoStyleSheet;
     pub fn Servo_AddRefStyleSheet(sheet: *mut RawServoStyleSheet);
     pub fn Servo_ReleaseStyleSheet(sheet: *mut RawServoStyleSheet);
     pub fn Servo_AppendStyleSheet(sheet: *mut RawServoStyleSheet,
                                   set: *mut RawServoStyleSet);
     pub fn Servo_PrependStyleSheet(sheet: *mut RawServoStyleSheet,
                                    set: *mut RawServoStyleSet);
     pub fn Servo_InsertStyleSheetBefore(sheet: *mut RawServoStyleSheet,
@@ -91,120 +93,171 @@ extern "C" {
                                                   pseudoTag: *mut nsIAtom,
                                                   set: *mut RawServoStyleSet)
      -> *mut ServoComputedValues;
     pub fn Servo_AddRefComputedValues(arg1: *mut ServoComputedValues);
     pub fn Servo_ReleaseComputedValues(arg1: *mut ServoComputedValues);
     pub fn Gecko_GetAttrAsUTF8(element: *mut RawGeckoElement, ns: *const u8,
                                name: *const u8, length: *mut u32)
      -> *const ::std::os::raw::c_char;
+    pub fn Gecko_GetAtomAsUTF16(atom: *mut nsIAtom, length: *mut u32)
+     -> *const u16;
     pub fn Gecko_LocalName(element: *mut RawGeckoElement, length: *mut u32)
      -> *const u16;
     pub fn Gecko_Namespace(element: *mut RawGeckoElement, length: *mut u32)
      -> *const u16;
     pub fn Servo_RestyleDocument(doc: *mut RawGeckoDocument,
                                  set: *mut RawServoStyleSet);
     pub fn Gecko_Construct_nsStyleFont(ptr: *mut nsStyleFont);
     pub fn Gecko_CopyConstruct_nsStyleFont(ptr: *mut nsStyleFont,
                                            other: *const nsStyleFont);
     pub fn Gecko_Destroy_nsStyleFont(ptr: *mut nsStyleFont);
+    pub fn Servo_GetStyleFont(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleFont;
     pub fn Gecko_Construct_nsStyleColor(ptr: *mut nsStyleColor);
     pub fn Gecko_CopyConstruct_nsStyleColor(ptr: *mut nsStyleColor,
                                             other: *const nsStyleColor);
     pub fn Gecko_Destroy_nsStyleColor(ptr: *mut nsStyleColor);
+    pub fn Servo_GetStyleColor(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleColor;
     pub fn Gecko_Construct_nsStyleList(ptr: *mut nsStyleList);
     pub fn Gecko_CopyConstruct_nsStyleList(ptr: *mut nsStyleList,
                                            other: *const nsStyleList);
     pub fn Gecko_Destroy_nsStyleList(ptr: *mut nsStyleList);
+    pub fn Servo_GetStyleList(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleList;
     pub fn Gecko_Construct_nsStyleText(ptr: *mut nsStyleText);
     pub fn Gecko_CopyConstruct_nsStyleText(ptr: *mut nsStyleText,
                                            other: *const nsStyleText);
     pub fn Gecko_Destroy_nsStyleText(ptr: *mut nsStyleText);
+    pub fn Servo_GetStyleText(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleText;
     pub fn Gecko_Construct_nsStyleVisibility(ptr: *mut nsStyleVisibility);
     pub fn Gecko_CopyConstruct_nsStyleVisibility(ptr: *mut nsStyleVisibility,
                                                  other:
                                                      *const nsStyleVisibility);
     pub fn Gecko_Destroy_nsStyleVisibility(ptr: *mut nsStyleVisibility);
+    pub fn Servo_GetStyleVisibility(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleVisibility;
     pub fn Gecko_Construct_nsStyleUserInterface(ptr:
                                                     *mut nsStyleUserInterface);
     pub fn Gecko_CopyConstruct_nsStyleUserInterface(ptr:
                                                         *mut nsStyleUserInterface,
                                                     other:
                                                         *const nsStyleUserInterface);
     pub fn Gecko_Destroy_nsStyleUserInterface(ptr: *mut nsStyleUserInterface);
+    pub fn Servo_GetStyleUserInterface(computedValues:
+                                           *mut ServoComputedValues)
+     -> *const nsStyleUserInterface;
     pub fn Gecko_Construct_nsStyleTableBorder(ptr: *mut nsStyleTableBorder);
     pub fn Gecko_CopyConstruct_nsStyleTableBorder(ptr:
                                                       *mut nsStyleTableBorder,
                                                   other:
                                                       *const nsStyleTableBorder);
     pub fn Gecko_Destroy_nsStyleTableBorder(ptr: *mut nsStyleTableBorder);
+    pub fn Servo_GetStyleTableBorder(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleTableBorder;
     pub fn Gecko_Construct_nsStyleSVG(ptr: *mut nsStyleSVG);
     pub fn Gecko_CopyConstruct_nsStyleSVG(ptr: *mut nsStyleSVG,
                                           other: *const nsStyleSVG);
     pub fn Gecko_Destroy_nsStyleSVG(ptr: *mut nsStyleSVG);
+    pub fn Servo_GetStyleSVG(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleSVG;
     pub fn Gecko_Construct_nsStyleVariables(ptr: *mut nsStyleVariables);
     pub fn Gecko_CopyConstruct_nsStyleVariables(ptr: *mut nsStyleVariables,
                                                 other:
                                                     *const nsStyleVariables);
     pub fn Gecko_Destroy_nsStyleVariables(ptr: *mut nsStyleVariables);
+    pub fn Servo_GetStyleVariables(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleVariables;
     pub fn Gecko_Construct_nsStyleBackground(ptr: *mut nsStyleBackground);
     pub fn Gecko_CopyConstruct_nsStyleBackground(ptr: *mut nsStyleBackground,
                                                  other:
                                                      *const nsStyleBackground);
     pub fn Gecko_Destroy_nsStyleBackground(ptr: *mut nsStyleBackground);
+    pub fn Servo_GetStyleBackground(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleBackground;
     pub fn Gecko_Construct_nsStylePosition(ptr: *mut nsStylePosition);
     pub fn Gecko_CopyConstruct_nsStylePosition(ptr: *mut nsStylePosition,
                                                other: *const nsStylePosition);
     pub fn Gecko_Destroy_nsStylePosition(ptr: *mut nsStylePosition);
+    pub fn Servo_GetStylePosition(computedValues: *mut ServoComputedValues)
+     -> *const nsStylePosition;
     pub fn Gecko_Construct_nsStyleTextReset(ptr: *mut nsStyleTextReset);
     pub fn Gecko_CopyConstruct_nsStyleTextReset(ptr: *mut nsStyleTextReset,
                                                 other:
                                                     *const nsStyleTextReset);
     pub fn Gecko_Destroy_nsStyleTextReset(ptr: *mut nsStyleTextReset);
+    pub fn Servo_GetStyleTextReset(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleTextReset;
     pub fn Gecko_Construct_nsStyleDisplay(ptr: *mut nsStyleDisplay);
     pub fn Gecko_CopyConstruct_nsStyleDisplay(ptr: *mut nsStyleDisplay,
                                               other: *const nsStyleDisplay);
     pub fn Gecko_Destroy_nsStyleDisplay(ptr: *mut nsStyleDisplay);
+    pub fn Servo_GetStyleDisplay(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleDisplay;
     pub fn Gecko_Construct_nsStyleContent(ptr: *mut nsStyleContent);
     pub fn Gecko_CopyConstruct_nsStyleContent(ptr: *mut nsStyleContent,
                                               other: *const nsStyleContent);
     pub fn Gecko_Destroy_nsStyleContent(ptr: *mut nsStyleContent);
+    pub fn Servo_GetStyleContent(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleContent;
     pub fn Gecko_Construct_nsStyleUIReset(ptr: *mut nsStyleUIReset);
     pub fn Gecko_CopyConstruct_nsStyleUIReset(ptr: *mut nsStyleUIReset,
                                               other: *const nsStyleUIReset);
     pub fn Gecko_Destroy_nsStyleUIReset(ptr: *mut nsStyleUIReset);
+    pub fn Servo_GetStyleUIReset(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleUIReset;
     pub fn Gecko_Construct_nsStyleTable(ptr: *mut nsStyleTable);
     pub fn Gecko_CopyConstruct_nsStyleTable(ptr: *mut nsStyleTable,
                                             other: *const nsStyleTable);
     pub fn Gecko_Destroy_nsStyleTable(ptr: *mut nsStyleTable);
+    pub fn Servo_GetStyleTable(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleTable;
     pub fn Gecko_Construct_nsStyleMargin(ptr: *mut nsStyleMargin);
     pub fn Gecko_CopyConstruct_nsStyleMargin(ptr: *mut nsStyleMargin,
                                              other: *const nsStyleMargin);
     pub fn Gecko_Destroy_nsStyleMargin(ptr: *mut nsStyleMargin);
+    pub fn Servo_GetStyleMargin(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleMargin;
     pub fn Gecko_Construct_nsStylePadding(ptr: *mut nsStylePadding);
     pub fn Gecko_CopyConstruct_nsStylePadding(ptr: *mut nsStylePadding,
                                               other: *const nsStylePadding);
     pub fn Gecko_Destroy_nsStylePadding(ptr: *mut nsStylePadding);
+    pub fn Servo_GetStylePadding(computedValues: *mut ServoComputedValues)
+     -> *const nsStylePadding;
     pub fn Gecko_Construct_nsStyleBorder(ptr: *mut nsStyleBorder);
     pub fn Gecko_CopyConstruct_nsStyleBorder(ptr: *mut nsStyleBorder,
                                              other: *const nsStyleBorder);
     pub fn Gecko_Destroy_nsStyleBorder(ptr: *mut nsStyleBorder);
+    pub fn Servo_GetStyleBorder(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleBorder;
     pub fn Gecko_Construct_nsStyleOutline(ptr: *mut nsStyleOutline);
     pub fn Gecko_CopyConstruct_nsStyleOutline(ptr: *mut nsStyleOutline,
                                               other: *const nsStyleOutline);
     pub fn Gecko_Destroy_nsStyleOutline(ptr: *mut nsStyleOutline);
+    pub fn Servo_GetStyleOutline(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleOutline;
     pub fn Gecko_Construct_nsStyleXUL(ptr: *mut nsStyleXUL);
     pub fn Gecko_CopyConstruct_nsStyleXUL(ptr: *mut nsStyleXUL,
                                           other: *const nsStyleXUL);
     pub fn Gecko_Destroy_nsStyleXUL(ptr: *mut nsStyleXUL);
+    pub fn Servo_GetStyleXUL(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleXUL;
     pub fn Gecko_Construct_nsStyleSVGReset(ptr: *mut nsStyleSVGReset);
     pub fn Gecko_CopyConstruct_nsStyleSVGReset(ptr: *mut nsStyleSVGReset,
                                                other: *const nsStyleSVGReset);
     pub fn Gecko_Destroy_nsStyleSVGReset(ptr: *mut nsStyleSVGReset);
+    pub fn Servo_GetStyleSVGReset(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleSVGReset;
     pub fn Gecko_Construct_nsStyleColumn(ptr: *mut nsStyleColumn);
     pub fn Gecko_CopyConstruct_nsStyleColumn(ptr: *mut nsStyleColumn,
                                              other: *const nsStyleColumn);
     pub fn Gecko_Destroy_nsStyleColumn(ptr: *mut nsStyleColumn);
+    pub fn Servo_GetStyleColumn(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleColumn;
     pub fn Gecko_Construct_nsStyleEffects(ptr: *mut nsStyleEffects);
     pub fn Gecko_CopyConstruct_nsStyleEffects(ptr: *mut nsStyleEffects,
                                               other: *const nsStyleEffects);
     pub fn Gecko_Destroy_nsStyleEffects(ptr: *mut nsStyleEffects);
+    pub fn Servo_GetStyleEffects(computedValues: *mut ServoComputedValues)
+     -> *const nsStyleEffects;
 }
--- a/servo/ports/geckolib/data.rs
+++ b/servo/ports/geckolib/data.rs
@@ -17,17 +17,17 @@ use style::media_queries::{Device, Media
 use style::parallel::WorkQueueData;
 use util::geometry::ViewportPx;
 use util::thread_state;
 use util::workqueue::WorkQueue;
 
 pub struct PerDocumentStyleData {
 
     /// Rule processor.
-    pub stylist: Stylist,
+    pub stylist: Arc<Stylist>,
 
     /// List of stylesheets, mirrored from Gecko.
     pub stylesheets: Vec<Arc<Stylesheet>>,
 
     /// Whether the stylesheets list above has changed since the last restyle.
     pub stylesheets_changed: bool,
 
     // FIXME(bholley): Hook these up to something.
@@ -45,18 +45,18 @@ impl PerDocumentStyleData {
         // FIXME(bholley): Real window size.
         let window_size: TypedSize2D<ViewportPx, f32> = Size2D::typed(800.0, 600.0);
         let device = Device::new(MediaType::Screen, window_size);
 
         let (new_anims_sender, new_anims_receiver) = channel();
         let num_threads = cmp::max(num_cpus::get() * 3 / 4, 1);
 
         PerDocumentStyleData {
-            stylist: Stylist::new(device),
-            stylesheets: Vec::new(),
+            stylist: Arc::new(Stylist::new(device)),
+            stylesheets: vec![],
             stylesheets_changed: true,
             new_animations_sender: new_anims_sender,
             new_animations_receiver: new_anims_receiver,
             running_animations: Arc::new(RwLock::new(HashMap::new())),
             expired_animations: Arc::new(RwLock::new(HashMap::new())),
             work_queue: WorkQueue::new("StyleWorker", thread_state::LAYOUT, num_threads),
         }
     }
--- a/servo/ports/geckolib/gecko_style_structs.rs
+++ b/servo/ports/geckolib/gecko_style_structs.rs
@@ -3981,16 +3981,44 @@ fn bindgen_test_layout_AnonymousCounterS
 pub struct CounterStyleManager {
     pub _bindgen_opaque_blob: [u64; 9usize],
 }
 #[test]
 fn bindgen_test_layout_CounterStyleManager() {
     assert_eq!(::std::mem::size_of::<CounterStyleManager>() , 72usize);
     assert_eq!(::std::mem::align_of::<CounterStyleManager>() , 8usize);
 }
+/**
+ * Enum defining the mode in which a sheet is to be parsed.  This is
+ * usually, but not always, the same as the cascade level at which the
+ * sheet will apply (see nsStyleSet.h).  Most of the Loader APIs only
+ * support loading of author sheets.
+ *
+ * Author sheets are the normal case: styles embedded in or linked
+ * from HTML pages.  They are also the most restricted.
+ *
+ * User sheets can do anything author sheets can do, and also get
+ * access to a few CSS extensions that are not yet suitable for
+ * exposure on the public Web, but are very useful for expressing
+ * user style overrides, such as @-moz-document rules.
+ *
+ * Agent sheets have access to all author- and user-sheet features
+ * plus more extensions that are necessary for internal use but,
+ * again, not yet suitable for exposure on the public Web.  Some of
+ * these are outright unsafe to expose; in particular, incorrect
+ * styling of anonymous box pseudo-elements can violate layout
+ * invariants.
+ */
+#[repr(u32)]
+#[derive(Debug, Copy, Clone)]
+pub enum SheetParsingMode {
+    eAuthorSheetFeatures = 0,
+    eUserSheetFeatures = 1,
+    eAgentSheetFeatures = 2,
+}
 pub type nsLoadFlags = u32;
 #[repr(C)]
 #[derive(Debug, Copy)]
 pub struct nsIRequest {
     pub _base: nsISupports,
 }
 #[repr(C)]
 pub struct _vftable_nsIRequest {
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -5,34 +5,58 @@
 #![allow(unsafe_code)]
 
 use app_units::Au;
 use bindings::{RawGeckoDocument, RawGeckoNode};
 use bindings::{RawServoStyleSet, RawServoStyleSheet, ServoComputedValues, ServoNodeData};
 use bindings::{nsIAtom};
 use data::PerDocumentStyleData;
 use euclid::Size2D;
+use gecko_style_structs::SheetParsingMode;
 use properties::GeckoComputedValues;
-use selector_impl::{SharedStyleContext, Stylesheet};
+use selector_impl::{GeckoSelectorImpl, PseudoElement, SharedStyleContext, Stylesheet};
 use std::marker::PhantomData;
 use std::mem::{forget, transmute};
 use std::ptr;
 use std::slice;
 use std::str::from_utf8_unchecked;
 use std::sync::{Arc, Mutex};
-use style::context::{ReflowGoal, StylistWrapper};
-use style::dom::{TDocument, TElement, TNode};
+use style::context::{ReflowGoal};
+use style::dom::{TDocument, TNode};
 use style::error_reporting::StdoutErrorReporter;
 use style::parallel;
 use style::properties::ComputedValues;
 use style::stylesheets::Origin;
 use traversal::RecalcStyleOnly;
 use url::Url;
 use util::arc_ptr_eq;
-use wrapper::{GeckoDocument, GeckoElement, GeckoNode, NonOpaqueStyleData};
+use wrapper::{GeckoDocument, GeckoNode, NonOpaqueStyleData};
+
+// TODO: This is ugly and should go away once we get an atom back-end.
+pub fn pseudo_element_from_atom(pseudo: *mut nsIAtom,
+                                in_ua_stylesheet: bool) -> Result<PseudoElement, String> {
+    use bindings::Gecko_GetAtomAsUTF16;
+    use selectors::parser::{ParserContext, SelectorImpl};
+
+    let pseudo_string = unsafe {
+        let mut length = 0;
+        let mut buff = Gecko_GetAtomAsUTF16(pseudo, &mut length);
+
+        // Handle the annoying preceding colon in front of everything in nsCSSAnonBoxList.h.
+        debug_assert!(length >= 2 && *buff == ':' as u16 && *buff.offset(1) != ':' as u16);
+        buff = buff.offset(1);
+        length -= 1;
+
+        String::from_utf16(slice::from_raw_parts(buff, length as usize)).unwrap()
+    };
+
+    let mut context = ParserContext::new();
+    context.in_user_agent_stylesheet = in_ua_stylesheet;
+    GeckoSelectorImpl::parse_pseudo_element(&context, &pseudo_string).map_err(|_| pseudo_string)
+}
 
 /*
  * For Gecko->Servo function calls, we need to redeclare the same signature that was declared in
  * the C header in Gecko. In order to catch accidental mismatches, we run rust-bindgen against
  * those signatures as well, giving us a second declaration of all the Servo_* functions in this
  * crate. If there's a mismatch, LLVM will assert and abort, which is a rather awful thing to
  * depend on but good enough for our purposes.
  */
@@ -46,25 +70,26 @@ pub extern "C" fn Servo_RestyleDocument(
     };
     let data = unsafe { &mut *(raw_data as *mut PerDocumentStyleData) };
 
     // Force the creation of our lazily-constructed initial computed values on
     // the main thread, since it's not safe to call elsewhere. This should move
     // into a runtime-wide init hook at some point.
     GeckoComputedValues::initial_values();
 
-    let _needs_dirtying = data.stylist.update(&data.stylesheets, data.stylesheets_changed);
+    let _needs_dirtying = Arc::get_mut(&mut data.stylist).unwrap()
+                              .update(&data.stylesheets, data.stylesheets_changed);
     data.stylesheets_changed = false;
 
     let shared_style_context = SharedStyleContext {
         viewport_size: Size2D::new(Au(0), Au(0)),
         screen_size_changed: false,
         generation: 0,
         goal: ReflowGoal::ForScriptQuery,
-        stylist: StylistWrapper(&data.stylist),
+        stylist: data.stylist.clone(),
         new_animations_sender: Mutex::new(data.new_animations_sender.clone()),
         running_animations: data.running_animations.clone(),
         expired_animations: data.expired_animations.clone(),
         error_reporter: Box::new(StdoutErrorReporter),
     };
 
     if node.is_dirty() || node.has_dirty_descendants() {
         parallel::traverse_dom::<GeckoNode, RecalcStyleOnly>(node, &shared_style_context, &mut data.work_queue);
@@ -75,46 +100,74 @@ pub extern "C" fn Servo_RestyleDocument(
 pub extern "C" fn Servo_DropNodeData(data: *mut ServoNodeData) -> () {
     unsafe {
         let _ = Box::<NonOpaqueStyleData>::from_raw(data as *mut NonOpaqueStyleData);
     }
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_StylesheetFromUTF8Bytes(bytes: *const u8,
-                                                length: u32) -> *mut RawServoStyleSheet {
+                                                length: u32,
+                                                mode: SheetParsingMode) -> *mut RawServoStyleSheet {
 
     let input = unsafe { from_utf8_unchecked(slice::from_raw_parts(bytes, length as usize)) };
 
-    // FIXME(heycam): Pass in the real base URL and sheet origin to use.
+    let origin = match mode {
+        SheetParsingMode::eAuthorSheetFeatures => Origin::Author,
+        SheetParsingMode::eUserSheetFeatures => Origin::User,
+        SheetParsingMode::eAgentSheetFeatures => Origin::UserAgent,
+    };
+
+    // FIXME(heycam): Pass in the real base URL.
     let url = Url::parse("about:none").unwrap();
-    let sheet = Arc::new(Stylesheet::from_str(input, url, Origin::Author, Box::new(StdoutErrorReporter)));
+    let sheet = Arc::new(Stylesheet::from_str(input, url, origin, Box::new(StdoutErrorReporter)));
     unsafe {
         transmute(sheet)
     }
 }
 
 pub struct ArcHelpers<GeckoType, ServoType> {
     phantom1: PhantomData<GeckoType>,
     phantom2: PhantomData<ServoType>,
 }
 
+
 impl<GeckoType, ServoType> ArcHelpers<GeckoType, ServoType> {
     pub fn with<F, Output>(raw: *mut GeckoType, cb: F) -> Output
                            where F: FnOnce(&Arc<ServoType>) -> Output {
+        debug_assert!(!raw.is_null());
+
         let owned = unsafe { Self::into(raw) };
         let result = cb(&owned);
         forget(owned);
         result
     }
 
+    pub fn maybe_with<F, Output>(maybe_raw: *mut GeckoType, cb: F) -> Output
+                                 where F: FnOnce(Option<&Arc<ServoType>>) -> Output {
+        let owned = if maybe_raw.is_null() {
+            None
+        } else {
+            Some(unsafe { Self::into(maybe_raw) })
+        };
+
+        let result = cb(owned.as_ref());
+        forget(owned);
+
+        result
+    }
+
     pub unsafe fn into(ptr: *mut GeckoType) -> Arc<ServoType> {
         transmute(ptr)
     }
 
+    pub fn from(owned: Arc<ServoType>) -> *mut GeckoType {
+        unsafe { transmute(owned) }
+    }
+
     pub unsafe fn addref(ptr: *mut GeckoType) {
         Self::with(ptr, |arc| forget(arc.clone()));
     }
 
     pub unsafe fn release(ptr: *mut GeckoType) {
         let _ = Self::into(ptr);
     }
 }
@@ -192,22 +245,36 @@ pub extern "C" fn Servo_ReleaseStyleShee
 pub extern "C" fn Servo_GetComputedValues(node: *mut RawGeckoNode)
      -> *mut ServoComputedValues {
     let node = unsafe { GeckoNode::from_raw(node) };
     let arc_cv = node.borrow_data().map(|data| data.style.clone());
     arc_cv.map_or(ptr::null_mut(), |arc| unsafe { transmute(arc) })
 }
 
 #[no_mangle]
-pub extern "C" fn Servo_GetComputedValuesForAnonymousBox(_parentStyleOrNull: *mut ServoComputedValues,
-                                                         _pseudoTag: *mut nsIAtom,
+pub extern "C" fn Servo_GetComputedValuesForAnonymousBox(parent_style_or_null: *mut ServoComputedValues,
+                                                         pseudo_tag: *mut nsIAtom,
                                                          raw_data: *mut RawServoStyleSet)
      -> *mut ServoComputedValues {
-    let _data = PerDocumentStyleData::borrow_mut_from_raw(raw_data);
-    unimplemented!();
+    let data = PerDocumentStyleData::borrow_mut_from_raw(raw_data);
+
+    let pseudo = match pseudo_element_from_atom(pseudo_tag, true) {
+        Ok(pseudo) => pseudo,
+        Err(pseudo) => {
+            warn!("stylo: Unable to parse anonymous-box pseudo-element: {}", pseudo);
+            return ptr::null_mut();
+        }
+    };
+
+    type Helpers = ArcHelpers<ServoComputedValues, GeckoComputedValues>;
+
+    Helpers::maybe_with(parent_style_or_null, |maybe_parent| {
+        let new_computed = data.stylist.computed_values_for_pseudo(&pseudo, maybe_parent);
+        new_computed.map_or(ptr::null_mut(), |c| Helpers::from(c))
+    })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_AddRefComputedValues(ptr: *mut ServoComputedValues) -> () {
     type Helpers = ArcHelpers<ServoComputedValues, GeckoComputedValues>;
     unsafe { Helpers::addref(ptr) };
 }
 
--- a/servo/ports/geckolib/selector_impl.rs
+++ b/servo/ports/geckolib/selector_impl.rs
@@ -146,24 +146,34 @@ impl SelectorImpl for GeckoSelectorImpl 
             "read-write" => ReadWrite,
             "read-only" => ReadOnly,
             _ => return Err(())
         };
 
         Ok(pseudo_class)
     }
 
-    fn parse_pseudo_element(_context: &ParserContext,
+    fn parse_pseudo_element(context: &ParserContext,
                             name: &str) -> Result<PseudoElement, ()> {
         use self::PseudoElement::*;
-        let pseudo_element = match_ignore_ascii_case! { name,
-            "before" => Before,
-            "after" => After,
-            "first-line" => FirstLine,
 
+        // The braces here are unfortunate, but they're needed for
+        // match_ignore_ascii_case! to work as expected.
+        match_ignore_ascii_case! { name,
+            "before" => { return Ok(Before) },
+            "after" => { return Ok(After) },
+            "first-line" => { return Ok(FirstLine) },
+            _ => {}
+        }
+
+        if !context.in_user_agent_stylesheet {
+            return Err(())
+        }
+
+        Ok(match_ignore_ascii_case! { name,
             "-moz-non-element" => MozNonElement,
 
             "-moz-anonymous-block" => MozAnonymousBlock,
             "-moz-anonymous-positioned-block" => MozAnonymousPositionedBlock,
             "-moz-mathml-anonymous-block" => MozMathMLAnonymousBlock,
             "-moz-xul-anonymous-block" => MozXULAnonymousBlock,
 
             "-moz-hframeset-border" => MozHorizontalFramesetBorder,
@@ -220,29 +230,104 @@ impl SelectorImpl for GeckoSelectorImpl 
             "-moz-tree-progressmeter" => MozTreeProgressMeter,
             "-moz-tree-drop-feedback" => MozTreeDropFeedback,
             "-moz-svg-marker-anon-child" => MozSVGMarkerAnonChild,
             "-moz-svg-outer-svg-anon-child" => MozSVGOuterSVGAnonChild,
             "-moz-svg-foreign-content" => MozSVGForeignContent,
             "-moz-svg-text" => MozSVGText,
 
             _ => return Err(())
-        };
-
-        Ok(pseudo_element)
+        })
     }
 }
 
 impl SelectorImplExt for GeckoSelectorImpl {
+    type ComputedValues = GeckoComputedValues;
+
     #[inline]
-    fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
+    fn is_eagerly_cascaded_pseudo_element(pseudo: &PseudoElement) -> bool {
+        match *pseudo {
+            PseudoElement::Before |
+            PseudoElement::After |
+            PseudoElement::FirstLine => true,
+            _ => false,
+        }
+    }
+
+    #[inline]
+    fn each_pseudo_element<F>(mut fun: F)
         where F: FnMut(PseudoElement) {
         fun(PseudoElement::Before);
         fun(PseudoElement::After);
-        // TODO: probably a lot more are missing here
+        fun(PseudoElement::FirstLine);
+
+        fun(PseudoElement::MozNonElement);
+        fun(PseudoElement::MozAnonymousBlock);
+        fun(PseudoElement::MozAnonymousPositionedBlock);
+        fun(PseudoElement::MozMathMLAnonymousBlock);
+        fun(PseudoElement::MozXULAnonymousBlock);
+
+        fun(PseudoElement::MozHorizontalFramesetBorder);
+        fun(PseudoElement::MozVerticalFramesetBorder);
+        fun(PseudoElement::MozLineFrame);
+        fun(PseudoElement::MozButtonContent);
+        fun(PseudoElement::MozButtonLabel);
+        fun(PseudoElement::MozCellContent);
+        fun(PseudoElement::MozDropdownList);
+        fun(PseudoElement::MozFieldsetContent);
+        fun(PseudoElement::MozFramesetBlank);
+        fun(PseudoElement::MozDisplayComboboxControlFrame);
+
+        fun(PseudoElement::MozHTMLCanvasContent);
+        fun(PseudoElement::MozInlineTable);
+        fun(PseudoElement::MozTable);
+        fun(PseudoElement::MozTableCell);
+        fun(PseudoElement::MozTableColumnGroup);
+        fun(PseudoElement::MozTableColumn);
+        fun(PseudoElement::MozTableOuter);
+        fun(PseudoElement::MozTableRowGroup);
+        fun(PseudoElement::MozTableRow);
+
+        fun(PseudoElement::MozCanvas);
+        fun(PseudoElement::MozPageBreak);
+        fun(PseudoElement::MozPage);
+        fun(PseudoElement::MozPageContent);
+        fun(PseudoElement::MozPageSequence);
+        fun(PseudoElement::MozScrolledContent);
+        fun(PseudoElement::MozScrolledCanvas);
+        fun(PseudoElement::MozScrolledPageSequence);
+        fun(PseudoElement::MozColumnContent);
+        fun(PseudoElement::MozViewport);
+        fun(PseudoElement::MozViewportScroll);
+        fun(PseudoElement::MozAnonymousFlexItem);
+        fun(PseudoElement::MozAnonymousGridItem);
+
+        fun(PseudoElement::MozRuby);
+        fun(PseudoElement::MozRubyBase);
+        fun(PseudoElement::MozRubyBaseContainer);
+        fun(PseudoElement::MozRubyText);
+        fun(PseudoElement::MozRubyTextContainer);
+
+        fun(PseudoElement::MozTreeColumn);
+        fun(PseudoElement::MozTreeRow);
+        fun(PseudoElement::MozTreeSeparator);
+        fun(PseudoElement::MozTreeCell);
+        fun(PseudoElement::MozTreeIndentation);
+        fun(PseudoElement::MozTreeLine);
+        fun(PseudoElement::MozTreeTwisty);
+        fun(PseudoElement::MozTreeImage);
+        fun(PseudoElement::MozTreeCellText);
+        fun(PseudoElement::MozTreeCheckbox);
+        fun(PseudoElement::MozTreeProgressMeter);
+        fun(PseudoElement::MozTreeDropFeedback);
+
+        fun(PseudoElement::MozSVGMarkerAnonChild);
+        fun(PseudoElement::MozSVGOuterSVGAnonChild);
+        fun(PseudoElement::MozSVGForeignContent);
+        fun(PseudoElement::MozSVGText);
     }
 
     #[inline]
     fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState {
         pc.state_flag()
     }
 
     #[inline]
--- a/servo/ports/geckolib/tools/regen_bindings.sh
+++ b/servo/ports/geckolib/tools/regen_bindings.sh
@@ -23,42 +23,42 @@ fi
 if [ "$(uname)" == "Linux" ]; then
   PLATFORM_DEPENDENT_DEFINES+="-DOS_LINUX";
   LIBCLANG_PATH=/usr/lib/llvm-3.8/lib;
 else
   PLATFORM_DEPENDENT_DEFINES+="-DOS_MACOSX";
   LIBCLANG_PATH=`brew --prefix llvm38`/lib/llvm-3.8/lib;
 fi
 
-# Prevent bindgen from generating opaque types for the gecko style structs.
-export MAP_GECKO_STRUCTS=""
-for STRUCT in nsStyleFont nsStyleColor nsStyleList nsStyleText \
+# Prevent bindgen from generating opaque types for common gecko types
+export MAP_GECKO_TYPES=""
+for STRUCT in SheetParsingMode nsStyleFont nsStyleColor nsStyleList nsStyleText \
               nsStyleVisibility nsStyleUserInterface nsStyleTableBorder \
               nsStyleSVG nsStyleVariables nsStyleBackground nsStylePosition \
               nsStyleTextReset nsStyleDisplay nsStyleContent nsStyleUIReset \
               nsStyleTable nsStyleMargin nsStylePadding nsStyleBorder \
               nsStyleOutline nsStyleXUL nsStyleSVGReset nsStyleColumn nsStyleEffects
 do
-  MAP_GECKO_STRUCTS=$MAP_GECKO_STRUCTS"-blacklist-type $STRUCT "
-  MAP_GECKO_STRUCTS=$MAP_GECKO_STRUCTS"-raw-line 'use gecko_style_structs::$STRUCT;' "
+  MAP_GECKO_TYPES=$MAP_GECKO_TYPES"-blacklist-type $STRUCT "
+  MAP_GECKO_TYPES=$MAP_GECKO_TYPES"-raw-line 'use gecko_style_structs::$STRUCT;' "
 done
 
 # Check for the include directory.
 export DIST_INCLUDE="$1/dist/include"
 if [ ! -d "$DIST_INCLUDE" ]; then
   echo "$DIST_INCLUDE: directory not found"
   exit 1
 fi
 
 export RUST_BACKTRACE=1
 
-# We need to use 'eval' here to make MAP_GECKO_STRUCTS evaluate properly as
+# We need to use 'eval' here to make MAP_GECKO_TYPES evaluate properly as
 # multiple arguments.
 eval ./rust-bindgen/target/debug/bindgen           \
   -x c++ -std=gnu++0x                              \
   "-I$DIST_INCLUDE"                                \
   $PLATFORM_DEPENDENT_DEFINES                      \
   -o ../bindings.rs                                \
   -no-type-renaming                                \
   "$DIST_INCLUDE/mozilla/ServoBindings.h"          \
   -match "ServoBindings.h"                         \
   -match "nsStyleStructList.h"                     \
-  $MAP_GECKO_STRUCTS
+  $MAP_GECKO_TYPES
--- a/servo/ports/geckolib/tools/regen_style_structs.sh
+++ b/servo/ports/geckolib/tools/regen_style_structs.sh
@@ -90,16 +90,17 @@ export RUST_BACKTRACE=1
   -match "CounterStyleManager.h"                                    \
   -match "nsStyleConsts.h"                                          \
   -match "nsCSSValue.h"                                             \
   -match "SheetType.h"                                              \
   -match "nsIPrincipal.h"                                           \
   -match "nsDataHashtable.h"                                        \
   -match "nsCSSScanner.h"                                           \
   -match "Types.h"                                                  \
+  -match "SheetParsingMode.h"                                       \
   -blacklist-type "IsDestructibleFallbackImpl"                      \
   -blacklist-type "IsDestructibleFallback"                          \
   -opaque-type "nsIntMargin"                                        \
   -opaque-type "nsIntPoint"                                         \
   -opaque-type "nsIntRect"                                          \
   -opaque-type "nsTArray"                                           \
   -opaque-type "nsStyleAutoArray"                                   \
   -opaque-type "nsCOMArray"                                         \
--- a/servo/ports/geckolib/traversal.rs
+++ b/servo/ports/geckolib/traversal.rs
@@ -47,17 +47,17 @@ impl<'a> StandaloneStyleContext<'a> {
         let local_context = create_or_get_local_context(shared);
         StandaloneStyleContext {
             shared: shared,
             cached_local_context: local_context,
         }
     }
 }
 
-impl<'a> StyleContext<'a, GeckoSelectorImpl, GeckoComputedValues> for StandaloneStyleContext<'a> {
+impl<'a> StyleContext<'a, GeckoSelectorImpl> for StandaloneStyleContext<'a> {
     fn shared_context(&self) -> &'a SharedStyleContext {
         &self.shared
     }
 
     fn local_context(&self) -> &LocalStyleContext<GeckoComputedValues> {
         &self.cached_local_context
     }
 }
--- a/servo/ports/gonk/Cargo.lock
+++ b/servo/ports/gonk/Cargo.lock
@@ -132,16 +132,21 @@ version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "bitflags"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "bitflags"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "block"
 version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "brotli"
 version = "0.3.20"
 source = "git+https://github.com/ende76/brotli-rs#c243045b88b2d2924c35269586fa9b770184c74c"
@@ -654,17 +659,17 @@ dependencies = [
  "range 0.0.1",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo-fontconfig 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo-skia 0.20130412.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "simd 0.1.0 (git+https://github.com/huonw/simd)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "style 0.0.1",
  "style_traits 0.0.1",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-script 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
  "webrender_traits 0.1.0 (git+https://github.com/servo/webrender_traits)",
 ]
@@ -799,17 +804,17 @@ source = "registry+https://github.com/ru
 dependencies = [
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "mac 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_codegen 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "tendril 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "httparse"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -990,21 +995,21 @@ dependencies = [
  "msg 0.0.1",
  "net_traits 0.0.1",
  "plugins 0.0.1",
  "profile_traits 0.0.1",
  "range 0.0.1",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "script 0.0.1",
  "script_traits 0.0.1",
- "selectors 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "selectors 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "style 0.0.1",
  "style_traits 0.0.1",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-script 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
  "webrender_traits 0.1.0 (git+https://github.com/servo/webrender_traits)",
@@ -1673,20 +1678,20 @@ dependencies = [
  "plugins 0.0.1",
  "profile_traits 0.0.1",
  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "ref_filter_map 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "ref_slice 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 0.1.55 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "script_traits 0.0.1",
- "selectors 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "selectors 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "style 0.0.1",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
  "uuid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "webrender_traits 0.1.0 (git+https://github.com/servo/webrender_traits)",
  "websocket 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1716,28 +1721,28 @@ dependencies = [
  "style_traits 0.0.1",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
 ]
 
 [[package]]
 name = "selectors"
-version = "0.5.2"
+version = "0.5.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "quickersort 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "semver"
 version = "0.1.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -1923,17 +1928,17 @@ version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "string_cache"
-version = "0.2.12"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "debug_unreachable 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_generator 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_shared 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1953,21 +1958,21 @@ dependencies = [
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
  "plugins 0.0.1",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "selectors 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "selectors 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "style_traits 0.0.1",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
 ]
 
 [[package]]
 name = "style_traits"
@@ -1976,17 +1981,17 @@ dependencies = [
  "cssparser 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "plugins 0.0.1",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "selectors 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "selectors 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
 ]
 
 [[package]]
 name = "task_info"
@@ -2159,17 +2164,17 @@ dependencies = [
  "num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "plugins 0.0.1",
  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "uuid"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
@@ -2345,13 +2350,13 @@ name = "xml5ever"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "mac 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_codegen 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "string_cache 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "tendril 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
--- a/servo/resources/servo.css
+++ b/servo/resources/servo.css
@@ -46,25 +46,21 @@ details {
 details::-servo-details-summary {
   margin-left: 40px;
   display: list-item;
   list-style: disclosure-closed;
 }
 details[open]::-servo-details-summary {
   list-style: disclosure-open;
 }
-details::-servo-details-content {
+*|*::-servo-details-content {
   margin-left: 40px;
   overflow: hidden;
-  display: none;
-}
-details[open]::-servo-details-content {
   display: block;
 }
-
 /*
  * Until servo supports svg properly, make sure to at least prevent svg
  * children from being layed out and rendered like usual html.
  * https://github.com/servo/servo/issues/10646
  */
 svg > * {
   display: none;
 }
--- a/servo/tests/unit/layout/size_of.rs
+++ b/servo/tests/unit/layout/size_of.rs
@@ -1,26 +1,47 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use layout::Fragment;
+use layout::ServoThreadSafeLayoutNode;
 use std::mem::size_of;
 
 #[test]
 fn test_size_of_fragment() {
     let expected = 168;
     let actual = size_of::<Fragment>();
 
     if actual < expected {
         panic!("Your changes have decreased the stack size of layout::fragment::Fragment \
-                from {} to {}. Good work! Please update the size in tests/layout/size_of.rs",
+                from {} to {}. Good work! Please update the size in tests/unit/layout/size_of.rs",
                 expected, actual);
     }
 
     if actual > expected {
         panic!("Your changes have increased the stack size of layout::fragment::Fragment \
                 from {} to {}.  Please consider choosing a design which avoids this increase. \
                 If you feel that the increase is necessary, update the size in \
-                tests/layout/size_of.rs.",
+                tests/unit/layout/size_of.rs.",
                 expected, actual);
     }
 }
+
+#[test]
+fn test_size_of_layout_node() {
+    let expected = 16;
+    let actual = size_of::<ServoThreadSafeLayoutNode>();
+
+    if actual < expected {
+        panic!("Your changes have decreased the stack size of layout::wrapper::ServoThreadSafeLayoutNode \
+                from {} to {}. Good work! Please update the size in tests/layout/unit/size_of.rs",
+                expected, actual);
+    }
+
+    if actual > expected {
+        panic!("Your changes have increased the stack size of layout::wrapper::ServoThreadSafeLayoutNode \
+                from {} to {}.  Please consider choosing a design which avoids this increase. \
+                If you feel that the increase is necessary, update the size in \
+                tests/unit/layout/size_of.rs.",
+                expected, actual);
+    }
+}
--- a/servo/tests/unit/style/Cargo.toml
+++ b/servo/tests/unit/style/Cargo.toml
@@ -13,11 +13,11 @@ msg = {path = "../../../components/msg"}
 plugins = {path = "../../../components/plugins"}
 style = {path = "../../../components/style"}
 style_traits = {path = "../../../components/style_traits"}
 util = {path = "../../../components/util"}
 app_units = {version = "0.2.3", features = ["plugins"]}
 cssparser = {version = "0.5.4", features = ["heap_size"]}
 euclid = {version = "0.6.4", features = ["plugins"]}
 selectors = {version = "0.5", features = ["heap_size"]}
-string_cache = {version = "0.2.12", features = ["heap_size"]}
+string_cache = {version = "0.2", features = ["heap_size"]}
 url = {version = "1.0.0", features = ["heap_size"]}
 rustc-serialize = "0.3"
--- a/servo/tests/unit/style/stylesheets.rs
+++ b/servo/tests/unit/style/stylesheets.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use cssparser::{self, Parser, SourcePosition};
 use media_queries::CSSErrorReporterTest;
 use selectors::parser::*;
 use std::borrow::ToOwned;
 use std::sync::Arc;
 use std::sync::Mutex;
-use string_cache::Atom;
+use string_cache::{Atom, Namespace};
 use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, DeclaredValue, longhands};
 use style::stylesheets::{CSSRule, StyleRule, Origin};
 use style::error_reporting::ParseErrorReporter;
 use style::servo::Stylesheet;
 use url::Url;
 
 #[test]
 fn test_parse_stylesheet() {
@@ -27,23 +27,23 @@ fn test_parse_stylesheet() {
     let url = Url::parse("about::test").unwrap();
     let stylesheet = Stylesheet::from_str(css, url, Origin::UserAgent,
                                           Box::new(CSSErrorReporterTest));
     assert_eq!(stylesheet, Stylesheet {
         origin: Origin::UserAgent,
         media: None,
         dirty_on_viewport_size_change: false,
         rules: vec![
-            CSSRule::Namespace(None, ns!(html)),
+            CSSRule::Namespace(None, Namespace(Atom::from("http://www.w3.org/1999/xhtml"))),
             CSSRule::Style(StyleRule {
                 selectors: vec![
                     Selector {
                         compound_selectors: Arc::new(CompoundSelector {
                             simple_selectors: vec![
-                                SimpleSelector::Namespace(ns!(html)),
+                                SimpleSelector::Namespace(Namespace(Atom::from("http://www.w3.org/1999/xhtml"))),
                                 SimpleSelector::LocalName(LocalName {
                                     name: atom!("input"),
                                     lower_name: atom!("input"),
                                 }),
                                 SimpleSelector::AttrEqual(AttrSelector {
                                     name: atom!("type"),
                                     lower_name: atom!("type"),
                                     namespace: NamespaceConstraint::Specific(ns!()),
@@ -63,31 +63,31 @@ fn test_parse_stylesheet() {
                     ]),
                 },
             }),
             CSSRule::Style(StyleRule {
                 selectors: vec![
                     Selector {
                         compound_selectors: Arc::new(CompoundSelector {
                             simple_selectors: vec![
-                                SimpleSelector::Namespace(ns!(html)),
+                                SimpleSelector::Namespace(Namespace(Atom::from("http://www.w3.org/1999/xhtml"))),
                                 SimpleSelector::LocalName(LocalName {
                                     name: atom!("html"),
                                     lower_name: atom!("html"),
                                 }),
                             ],
                             next: None,
                         }),
                         pseudo_element: None,
                         specificity: (0 << 20) + (0 << 10) + (1 << 0),
                     },
                     Selector {
                         compound_selectors: Arc::new(CompoundSelector {
                             simple_selectors: vec![
-                                SimpleSelector::Namespace(ns!(html)),
+                                SimpleSelector::Namespace(Namespace(Atom::from("http://www.w3.org/1999/xhtml"))),
                                 SimpleSelector::LocalName(LocalName {
                                     name: atom!("body"),
                                     lower_name: atom!("body"),
                                 }),
                             ],
                             next: None,
                         }),
                         pseudo_element: None,
@@ -102,20 +102,22 @@ fn test_parse_stylesheet() {
                     important: Arc::new(vec![]),
                 },
             }),
             CSSRule::Style(StyleRule {
                 selectors: vec![
                     Selector {
                         compound_selectors: Arc::new(CompoundSelector {
                             simple_selectors: vec![
+                                SimpleSelector::Namespace(Namespace(Atom::from("http://www.w3.org/1999/xhtml"))),
                                 SimpleSelector::Class(Atom::from("ok")),
                             ],
                             next: Some((Arc::new(CompoundSelector {
                                 simple_selectors: vec![
+                                    SimpleSelector::Namespace(Namespace(Atom::from("http://www.w3.org/1999/xhtml"))),
                                     SimpleSelector::ID(Atom::from("d1")),
                                 ],
                                 next: None,
                             }), Combinator::Child)),
                         }),
                         pseudo_element: None,
                         specificity: (1 << 20) + (1 << 10) + (0 << 0),
                     },
@@ -140,17 +142,16 @@ fn test_parse_stylesheet() {
                     ]),
                     important: Arc::new(vec![]),
                 },
             }),
         ],
     });
 }
 
-
 struct CSSError {
     pub line: usize,
     pub column: usize,
     pub message: String
 }
 
 struct CSSInvalidErrorReporterTest {
     pub errors: Arc<Mutex<Vec<CSSError>>>