From 2a499d5a0bd51dded25a2705f8922841718d3f48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sun, 24 Apr 2016 18:34:36 +0200 Subject: [PATCH] layout: Stop storing PrecomputedStyleData in LayoutNode Use the SharedStyleContext instead. --- components/layout/construct.rs | 139 ++++++++++-------- components/layout/context.rs | 5 + components/layout/data.rs | 7 +- components/layout/flow.rs | 15 +- components/layout/fragment.rs | 17 ++- components/layout/query.rs | 10 +- components/layout/wrapper.rs | 66 +++++---- components/style/data.rs | 11 +- components/style/dom.rs | 12 +- components/style/selector_impl.rs | 2 +- components/style/selector_matching.rs | 77 +++------- components/style/servo.rs | 1 - components/style/traversal.rs | 2 +- resources/servo.css | 1 - .../tests/mozilla/details_ui_opened.html | 1 - 15 files changed, 176 insertions(+), 190 deletions(-) diff --git a/components/layout/construct.rs b/components/layout/construct.rs index cc34642466a..876e7545957 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -47,6 +47,7 @@ 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; @@ -210,15 +211,15 @@ impl InlineFragmentsAccumulator { } } - fn from_inline_node(node: &N) -> InlineFragmentsAccumulator + fn from_inline_node(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, @@ -287,6 +288,11 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> } } + #[inline] + fn style_context(&self) -> &SharedStyleContext { + self.layout_context.style_context() + } + #[inline] fn set_flow_construction_result(&self, node: &ConcreteThreadSafeLayoutNode, @@ -336,7 +342,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> 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. @@ -344,7 +350,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> } }; - 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. @@ -356,13 +362,14 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> 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)); @@ -371,12 +378,12 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> *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)); @@ -385,13 +392,13 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> *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)); @@ -449,7 +456,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> 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 { @@ -479,7 +486,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> 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; } @@ -587,10 +594,11 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> 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); @@ -696,7 +704,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> } } - 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); } @@ -722,7 +730,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> 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) => { @@ -765,7 +773,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> /// to happen. fn build_flow_for_block(&mut self, node: &ConcreteThreadSafeLayoutNode, float_kind: Option) -> ConstructionResult { - if node.style().is_multicol() { + if node.style(self.style_context()).is_multicol() { return self.build_flow_for_multicol(node, float_kind) } @@ -791,7 +799,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> 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) @@ -805,8 +813,8 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> fn build_fragments_for_nonreplaced_inline_content(&mut self, node: &ConcreteThreadSafeLayoutNode) -> ConstructionResult { let mut opt_inline_block_splits: LinkedList = 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(); @@ -828,7 +836,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> 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); @@ -879,12 +887,13 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> 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(_)) => { @@ -894,17 +903,18 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> } } - 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) @@ -917,7 +927,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> // 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(); @@ -944,17 +954,17 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> } // 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 @@ -987,14 +997,15 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> _ => 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); @@ -1022,16 +1033,17 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> 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); @@ -1087,7 +1099,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> child_flows: Vec, 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) { @@ -1115,10 +1127,10 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> fn build_flow_for_multicol(&mut self, node: &ConcreteThreadSafeLayoutNode, float_kind: Option) -> 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. @@ -1156,11 +1168,11 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> /// 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. @@ -1218,7 +1230,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> /// 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) } @@ -1226,7 +1238,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> /// 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) } @@ -1234,14 +1246,14 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> /// 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 @@ -1257,15 +1269,15 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> 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(), @@ -1275,14 +1287,15 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> 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)] } } } @@ -1295,7 +1308,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> // 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)) @@ -1322,7 +1335,8 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> let specific = SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node)); let construction_item = ConstructionItem::TableColumnFragment(Fragment::new(node, - specific)); + specific, + self.layout_context)); ConstructionResult::ConstructionItem(construction_item) } @@ -1332,7 +1346,8 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> -> 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` @@ -1345,7 +1360,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> 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(); @@ -1387,11 +1402,11 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> 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) { @@ -1490,7 +1505,7 @@ impl<'a, ConcreteThreadSafeLayoutNode> PostorderNodeMutTraversal { // 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, @@ -1501,7 +1516,7 @@ impl<'a, ConcreteThreadSafeLayoutNode> PostorderNodeMutTraversal { - 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 diff --git a/components/layout/context.rs b/components/layout/context.rs index 0a9933d4abd..9c752645513 100644 --- a/components/layout/context.rs +++ b/components/layout/context.rs @@ -129,6 +129,11 @@ impl<'a> LayoutContext<'a> { } } + #[inline(always)] + pub fn style_context(&self) -> &SharedStyleContext { + &self.shared.style_context + } + #[inline(always)] pub fn font_context(&self) -> RefMut { self.cached_local_layout_context.font_context.borrow_mut() diff --git a/components/layout/data.rs b/components/layout/data.rs index b7d80d8a0ea..4e5c8339157 100644 --- a/components/layout/data.rs +++ b/components/layout/data.rs @@ -4,8 +4,7 @@ use construct::ConstructionResult; use incremental::RestyleDamage; -use std::sync::Arc; -use style::servo::{PrecomputedStyleData, PrivateStyleData}; +use style::servo::PrivateStyleData; /// Data that layout associates with a node. pub struct PrivateLayoutData { @@ -37,9 +36,9 @@ pub struct PrivateLayoutData { impl PrivateLayoutData { /// Creates new layout data. - pub fn new(precomputed_style_data: Arc) -> PrivateLayoutData { + pub fn new() -> PrivateLayoutData { PrivateLayoutData { - style_data: PrivateStyleData::new(precomputed_style_data), + style_data: PrivateStyleData::new(), restyle_damage: RestyleDamage::empty(), flow_construction_result: ConstructionResult::None, before_flow_construction_result: ConstructionResult::None, diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 2dfcc05ab06..79ab2fc2c07 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -483,7 +483,7 @@ pub trait ImmutableFlowUtils { fn need_anonymous_flow(self, child: &Flow) -> bool; /// Generates missing child flow of this flow. - fn generate_missing_child_flow(self, node: &N) -> FlowRef; + fn generate_missing_child_flow(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; @@ -1275,8 +1275,9 @@ impl<'a> ImmutableFlowUtils for &'a 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(self, node: &N) -> FlowRef { - let mut style = node.style().clone(); + fn generate_missing_child_flow(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( @@ -1286,7 +1287,7 @@ impl<'a> ImmutableFlowUtils for &'a Flow { 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)) @@ -1299,10 +1300,10 @@ impl<'a> ImmutableFlowUtils for &'a Flow { 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 => { @@ -1310,7 +1311,7 @@ impl<'a> ImmutableFlowUtils for &'a Flow { 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)) diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index ce9e55496c7..9523159de41 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -319,9 +319,9 @@ pub struct CanvasFragmentInfo { } impl CanvasFragmentInfo { - pub fn new(node: &N, data: HTMLCanvasData) -> CanvasFragmentInfo { + pub fn new(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), @@ -382,7 +382,7 @@ impl ImageFragmentInfo { }; ImageFragmentInfo { - replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node), + replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node, layout_context), image: image, metadata: metadata, } @@ -441,9 +441,9 @@ pub struct ReplacedImageFragmentInfo { } impl ReplacedImageFragmentInfo { - pub fn new(node: &N) -> ReplacedImageFragmentInfo + pub fn new(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, @@ -789,8 +789,9 @@ impl TableColumnFragmentInfo { impl Fragment { /// Constructs a new `Fragment` instance. - pub fn new(node: &N, specific: SpecificFragmentInfo) -> Fragment { - let style = node.style().clone(); + pub fn new(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(); @@ -799,7 +800,7 @@ impl Fragment { 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), diff --git a/components/layout/query.rs b/components/layout/query.rs index 5b28acfa462..df94ed0024e 100644 --- a/components/layout/query.rs +++ b/components/layout/query.rs @@ -574,8 +574,8 @@ pub fn process_resolved_style_request( 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) }; @@ -590,7 +590,7 @@ pub fn process_resolved_style_request( 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 | @@ -711,7 +711,7 @@ pub fn process_offset_parent_query(requested_node: N, layout_root pub fn process_node_overflow_request(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)))) @@ -720,7 +720,7 @@ pub fn process_node_overflow_request(requested_node: N) -> NodeOv pub fn process_margin_style_query(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 { diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs index 9c96ec557e3..3912af79eac 100644 --- a/components/layout/wrapper.rs +++ b/components/layout/wrapper.rs @@ -72,7 +72,7 @@ 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::{PrecomputedStyleData, PrivateStyleData}; +use style::servo::{PrivateStyleData, SharedStyleContext}; use url::Url; use util::str::is_whitespace; @@ -168,11 +168,11 @@ impl<'ln> TNode for ServoLayoutNode<'ln> { OpaqueNodeMethods::from_jsmanaged(unsafe { self.get_jsmanaged() }) } - fn initialize_data(self, precomputed: &Arc) { + fn initialize_data(self) { let has_data = unsafe { self.borrow_data_unchecked().is_some() }; if !has_data { let ptr: NonOpaqueStyleAndLayoutData = - Box::into_raw(box RefCell::new(PrivateLayoutData::new(precomputed.clone()))); + Box::into_raw(box RefCell::new(PrivateLayoutData::new())); let opaque = OpaqueStyleAndLayoutData { ptr: unsafe { NonZero::new(ptr as *mut ()) } }; @@ -428,14 +428,14 @@ impl<'le> TElement for ServoLayoutElement<'le> { } #[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) } @@ -745,19 +745,12 @@ pub trait ThreadSafeLayoutNode : Clone + Copy + Sized + PartialEq { return None; } - self.borrow_layout_data().unwrap() - .style_data - .precomputed - .computed_values_for(&PseudoElement::DetailsContent, - Some(&*self.style())) - .map(|style| { - let display = if element.get_attr(&ns!(), &atom!("open")).is_some() { - style.get_box().display - } else { - display::T::none - }; - self.with_pseudo(PseudoElementType::DetailsContent(display)) - }) + let display = if element.get_attr(&ns!(), &atom!("open")).is_some() { + display::T::block + } else { + display::T::none + }; + Some(self.with_pseudo(PseudoElementType::DetailsContent(display))) } /// Borrows the layout data immutably. Fails on a conflicting borrow. @@ -777,7 +770,7 @@ pub trait ThreadSafeLayoutNode : Clone + Copy + Sized + PartialEq { /// /// Unlike the version on TNode, this handles pseudo-elements. #[inline] - fn style(&self) -> Ref> { + fn style(&self, context: &SharedStyleContext) -> Ref> { match self.get_pseudo_element_type() { PseudoElementType::Normal => { Ref::map(self.borrow_layout_data().unwrap(), |data| { @@ -791,10 +784,9 @@ pub trait ThreadSafeLayoutNode : Clone + Copy + Sized + PartialEq { 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 = data.style_data - .precomputed - .computed_values_for(&style_pseudo, - data.style_data.style.as_ref()); + 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()); } @@ -805,8 +797,26 @@ pub trait ThreadSafeLayoutNode : Clone + Copy + Sized + PartialEq { } } + /// 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, not from layout itself. #[inline] - fn selected_style(&self) -> Ref> { + fn resolved_style(&self) -> Ref> { + Ref::map(self.borrow_layout_data().unwrap(), |data| { + 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, _context: &SharedStyleContext) -> Ref> { Ref::map(self.borrow_layout_data().unwrap(), |data| { data.style_data.per_pseudo .get(&PseudoElement::Selection) @@ -830,7 +840,7 @@ pub trait ThreadSafeLayoutNode : Clone + Copy + Sized + PartialEq { }; } - fn is_ignorable_whitespace(&self) -> bool; + fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool; fn restyle_damage(self) -> RestyleDamage; @@ -909,7 +919,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized { type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode; #[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; @@ -1030,7 +1040,7 @@ impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> { self.node.mutate_layout_data() } - fn is_ignorable_whitespace(&self) -> bool { + fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool { unsafe { let text: LayoutJS = match self.get_jsmanaged().downcast() { Some(text) => text, @@ -1047,7 +1057,7 @@ impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> { // // 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() } } diff --git a/components/style/data.rs b/components/style/data.rs index 843d6b2448d..3698eeddf5b 100644 --- a/components/style/data.rs +++ b/components/style/data.rs @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use properties::ComputedValues; -use selector_matching::PrecomputedStyleData; use selectors::parser::SelectorImpl; use std::collections::HashMap; use std::hash::BuildHasherDefault; @@ -14,12 +13,6 @@ pub struct PrivateStyleData>, - /// Shared rules data needed to avoid doing the cascade for some - /// pseudo-elements like "-servo-details-content" - /// - /// TODO: Move to TLS to avoid this extra pointer? - pub precomputed: Arc>, - /// The results of CSS styling for each pseudo-element (if any). pub per_pseudo: HashMap, BuildHasherDefault<::fnv::FnvHasher>>, @@ -30,11 +23,9 @@ pub struct PrivateStyleData PrivateStyleData where Impl: SelectorImpl, ConcreteComputedValues: ComputedValues { - pub fn new(precomputed: Arc>) - -> PrivateStyleData { + pub fn new() -> PrivateStyleData { PrivateStyleData { style: None, - precomputed: precomputed, per_pseudo: HashMap::with_hasher(Default::default()), parallel: DomParallelInfo::new(), } diff --git a/components/style/dom.rs b/components/style/dom.rs index a10cc22e30d..fed1597c627 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -4,12 +4,12 @@ #![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, SelectorImplExt}; -use selector_matching::PrecomputedStyleData; use selectors::Element; use selectors::matching::DeclarationBlock; use smallvec::VecLike; @@ -90,10 +90,7 @@ pub trait TNode : Sized + Copy + Clone { /// initialized. /// /// FIXME(pcwalton): Do this as part of fragment building instead of in a traversal. - fn initialize_data(self, - precomputed: &Arc::Impl, - Self::ConcreteComputedValues>>) - where ::Impl: SelectorImplExt; + fn initialize_data(self); /// While doing a reflow, the node at the root has no parent, as far as we're /// concerned. This method returns `None` at the reflow root. @@ -174,7 +171,10 @@ pub trait TNode : Sized + Copy + Clone { /// Returns the style results for the given node. If CSS selector matching /// has not yet been performed, fails. - fn style(&self) -> Ref> { + fn style(&self, + _context: &SharedStyleContext<::Impl>) + -> Ref> + where ::Impl: SelectorImplExt { Ref::map(self.borrow_data().unwrap(), |data| data.style.as_ref().unwrap()) } diff --git a/components/style/selector_impl.rs b/components/style/selector_impl.rs index 7c9f7d3be89..842f08ae33d 100644 --- a/components/style/selector_impl.rs +++ b/components/style/selector_impl.rs @@ -2,11 +2,11 @@ * 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; -use properties::{self, ServoComputedValues}; pub trait ElementExt: Element { fn is_link(&self) -> bool; diff --git a/components/style/selector_matching.rs b/components/style/selector_matching.rs index 844a060daac..8dcd139b7b4 100644 --- a/components/style/selector_matching.rs +++ b/components/style/selector_matching.rs @@ -84,42 +84,6 @@ lazy_static! { }; } -#[derive(HeapSizeOf)] -pub struct PrecomputedStyleData { - /// 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_elements: HashMap, - BuildHasherDefault<::fnv::FnvHasher>>, - _phantom: ::std::marker::PhantomData, -} - -impl PrecomputedStyleData - where Impl: SelectorImpl, Computed: ComputedValues { - fn new() -> Self { - PrecomputedStyleData { - non_eagerly_cascaded_pseudo_elements: HashMap::with_hasher(Default::default()), - _phantom: ::std::marker::PhantomData, - } - } - - pub fn computed_values_for(&self, - pseudo: &Impl::PseudoElement, - parent: Option<&Arc>) -> Option> { - if let Some(declarations) = self.non_eagerly_cascaded_pseudo_elements.get(pseudo) { - let (computed, _) = - properties::cascade::(Size2D::zero(), - &declarations, false, - parent.map(|p| &**p), None, - box StdoutErrorReporter); - Some(Arc::new(computed)) - } else { - parent.map(|p| p.clone()) - } - } -} - /// This structure holds all the selectors and device characteristics /// for a given document. The selectors are converted into `Rule`s /// (defined in rust-selectors), and introduced in a `SelectorMap` @@ -160,10 +124,12 @@ pub struct Stylist { PerPseudoElementSelectorMap, BuildHasherDefault<::fnv::FnvHasher>>, - /// Precomputed data to be shared to the nodes. - /// Note that this has to be an Arc, since the layout thread needs the - /// stylist mutable. - precomputed: Arc>, + /// 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, + BuildHasherDefault<::fnv::FnvHasher>>, rules_source_order: usize, @@ -182,7 +148,7 @@ impl Stylist { element_map: PerPseudoElementSelectorMap::new(), pseudos_map: HashMap::with_hasher(Default::default()), - precomputed: Arc::new(PrecomputedStyleData::new()), + non_eagerly_cascaded_pseudo_element_decls: HashMap::with_hasher(Default::default()), rules_source_order: 0, state_deps: DependencySet::new(), }; @@ -205,7 +171,7 @@ impl Stylist { self.element_map = PerPseudoElementSelectorMap::new(); self.pseudos_map = HashMap::with_hasher(Default::default()); - self.precomputed = Arc::new(PrecomputedStyleData::new()); + self.non_eagerly_cascaded_pseudo_element_decls = HashMap::with_hasher(Default::default()); self.rules_source_order = 0; self.state_deps.clear(); @@ -279,29 +245,30 @@ impl Stylist { // This is actually kind of hard, because the stylist is shared // between threads. if let Some(map) = self.pseudos_map.remove(&pseudo) { - let mut precomputed = Arc::get_mut(&mut self.precomputed) - .expect("Stylist was not the single owner of PrecomputedStyleData"); - let mut declarations = vec![]; map.user_agent.normal.get_universal_rules(&mut declarations); map.user_agent.important.get_universal_rules(&mut declarations); - precomputed.non_eagerly_cascaded_pseudo_elements.insert(pseudo, declarations); + self.non_eagerly_cascaded_pseudo_element_decls.insert(pseudo, declarations); } }) } - pub fn get_precomputed_data(&self) -> &Arc> { - &self.precomputed - } - - pub fn get_non_eagerly_cascaded_pseudo_element_style(&self, - pseudo: &Impl::PseudoElement, - parent: Option<&Arc>) -> Option> { + pub fn computed_values_for_pseudo(&self, + pseudo: &Impl::PseudoElement, + parent: Option<&Arc>) -> Option> { debug_assert!(!Impl::is_eagerly_cascaded_pseudo_element(pseudo)); - self.precomputed - .computed_values_for(pseudo, parent) + if let Some(declarations) = self.non_eagerly_cascaded_pseudo_element_decls.get(pseudo) { + let (computed, _) = + properties::cascade::(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(&self, element: &E, diff --git a/components/style/servo.rs b/components/style/servo.rs index 4dbde75ac57..f5ce9d608b0 100644 --- a/components/style/servo.rs +++ b/components/style/servo.rs @@ -11,6 +11,5 @@ use stylesheets; /// Concrete types for servo Style implementation pub type Stylesheet = stylesheets::Stylesheet; pub type PrivateStyleData = data::PrivateStyleData; -pub type PrecomputedStyleData = selector_matching::PrecomputedStyleData; pub type Stylist = selector_matching::Stylist; pub type SharedStyleContext = context::SharedStyleContext; diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 2cf5220d78f..c7ba825acbd 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -129,7 +129,7 @@ pub fn recalc_style_at<'a, N, C>(context: &'a C, // // FIXME(pcwalton): Stop allocating here. Ideally this should just be done by the HTML // parser. - node.initialize_data(context.shared_context().stylist.get_precomputed_data()); + node.initialize_data(); // Get the parent node. let parent_opt = node.layout_parent_node(root); diff --git a/resources/servo.css b/resources/servo.css index 0ef23b8f86d..25e41f82f04 100644 --- a/resources/servo.css +++ b/resources/servo.css @@ -54,7 +54,6 @@ details[open]::-servo-details-summary { *|*::-servo-details-content { margin-left: 40px; overflow: hidden; - display: block; } /* * Until servo supports svg properly, make sure to at least prevent svg diff --git a/tests/wpt/mozilla/tests/mozilla/details_ui_opened.html b/tests/wpt/mozilla/tests/mozilla/details_ui_opened.html index 10e65e98d81..2d9b13aad0a 100644 --- a/tests/wpt/mozilla/tests/mozilla/details_ui_opened.html +++ b/tests/wpt/mozilla/tests/mozilla/details_ui_opened.html @@ -6,4 +6,3 @@ Test Contents -