diff options
author | bors-servo <metajack+bors@gmail.com> | 2015-03-09 23:24:47 -0600 |
---|---|---|
committer | bors-servo <metajack+bors@gmail.com> | 2015-03-09 23:24:47 -0600 |
commit | fd1bb49a65dd998c8ef9890a1576aaf62ddfdba1 (patch) | |
tree | f426bc35a25e9f887f10856ee3e090b9e6cb5891 | |
parent | f4a362725faeb41aef85631965d3a4fd6153a70d (diff) | |
parent | 8703cd974720ad336349febdaf2649cb08eeea67 (diff) | |
download | servo-fd1bb49a65dd998c8ef9890a1576aaf62ddfdba1.tar.gz servo-fd1bb49a65dd998c8ef9890a1576aaf62ddfdba1.zip |
auto merge of #5160 : pcwalton/servo/counters-redux, r=SimonSapin
Only simple alphabetic and numeric counter styles are supported. (This
is most of them though.)
Although this PR adds a sequential pass to layout, I verified that on
pages that contain a reasonable number of ordered lists (Reddit
`/r/rust`), the time spent in generated content resolution is dwarfed by
the time spent in the parallelizable parts of layout. So I don't expect
this to negatively affect our parallelism expect perhaps in pathological
cases.
Reconstructed from #5138 via raw diffing.
r? @SimonSapin
43 files changed, 1879 insertions, 535 deletions
diff --git a/components/layout/block.rs b/components/layout/block.rs index 456ee73d09b..77c60b25b3d 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -27,7 +27,6 @@ #![deny(unsafe_blocks)] -use construct::FlowConstructor; use context::LayoutContext; use css::node_style::StyledNode; use display_list_builder::{BlockFlowDisplayListBuilding, FragmentDisplayListBuilding}; @@ -50,12 +49,8 @@ use wrapper::ThreadSafeLayoutNode; use geom::{Point2D, Rect, Size2D}; use gfx::display_list::{ClippingRegion, DisplayList}; -use rustc_serialize::{Encoder, Encodable}; use msg::compositor_msg::LayerId; -use msg::constellation_msg::ConstellationChan; -use util::geometry::{Au, MAX_AU}; -use util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode}; -use util::opts; +use rustc_serialize::{Encoder, Encodable}; use std::cmp::{max, min}; use std::fmt; use style::computed_values::{overflow_x, overflow_y, position, box_sizing, display, float}; @@ -63,6 +58,9 @@ use style::properties::ComputedValues; use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; use style::values::computed::{LengthOrPercentageOrNone}; use std::sync::Arc; +use util::geometry::{Au, MAX_AU}; +use util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode}; +use util::opts; /// Information specific to floated blocks. #[derive(Clone, RustcEncodable)] @@ -570,20 +568,6 @@ impl Encodable for BlockFlowFlags { } impl BlockFlow { - pub fn from_node(constructor: &mut FlowConstructor, node: &ThreadSafeLayoutNode) -> BlockFlow { - let writing_mode = node.style().writing_mode; - BlockFlow { - base: BaseFlow::new(Some((*node).clone()), writing_mode, ForceNonfloatedFlag::ForceNonfloated), - fragment: Fragment::new(constructor, node), - static_b_offset: Au::new(0), - inline_size_of_preceding_left_floats: Au(0), - inline_size_of_preceding_right_floats: Au(0), - hypothetical_position: LogicalPoint::new(writing_mode, Au(0), Au(0)), - float: None, - flags: BlockFlowFlags::empty(), - } - } - pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode, fragment: Fragment) -> BlockFlow { let writing_mode = node.style().writing_mode; BlockFlow { @@ -598,23 +582,6 @@ impl BlockFlow { } } - pub fn float_from_node(constructor: &mut FlowConstructor, - node: &ThreadSafeLayoutNode, - float_kind: FloatKind) - -> BlockFlow { - let writing_mode = node.style().writing_mode; - BlockFlow { - base: BaseFlow::new(Some((*node).clone()), writing_mode, ForceNonfloatedFlag::FloatIfNecessary), - fragment: Fragment::new(constructor, node), - static_b_offset: Au::new(0), - inline_size_of_preceding_left_floats: Au(0), - inline_size_of_preceding_right_floats: Au(0), - hypothetical_position: LogicalPoint::new(writing_mode, Au(0), Au(0)), - float: Some(box FloatedBlockInfo::new(float_kind)), - flags: BlockFlowFlags::empty(), - } - } - pub fn float_from_node_and_fragment(node: &ThreadSafeLayoutNode, fragment: Fragment, float_kind: FloatKind) @@ -1950,8 +1917,8 @@ impl Flow for BlockFlow { .translate(stacking_context_position)); } - fn remove_compositor_layers(&self, constellation_chan: ConstellationChan) { - self.fragment.remove_compositor_layers(constellation_chan); + fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) { + (*mutator)(&mut self.fragment) } } diff --git a/components/layout/construct.rs b/components/layout/construct.rs index b8dd5a8415e..b29b6507c2d 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -16,23 +16,23 @@ use block::BlockFlow; use context::LayoutContext; use css::node_style::StyledNode; +use data::{HAS_NEWLY_CONSTRUCTED_FLOW, LayoutDataAccess, LayoutDataWrapper}; use floats::FloatKind; use flow::{Descendants, AbsDescendants}; use flow::{Flow, ImmutableFlowUtils, MutableOwnedFlowUtils}; use flow::{IS_ABSOLUTELY_POSITIONED}; use flow; use flow_ref::FlowRef; +use fragment::{Fragment, GeneratedContentInfo, IframeFragmentInfo}; use fragment::CanvasFragmentInfo; use fragment::ImageFragmentInfo; use fragment::InlineAbsoluteHypotheticalFragmentInfo; use fragment::TableColumnFragmentInfo; use fragment::UnscannedTextFragmentInfo; -use fragment::{Fragment, IframeFragmentInfo}; use fragment::{InlineBlockFragmentInfo, SpecificFragmentInfo}; use incremental::{RECONSTRUCT_FLOW, RestyleDamage}; use inline::InlineFlow; -use data::{HAS_NEWLY_CONSTRUCTED_FLOW, LayoutDataAccess, LayoutDataWrapper}; -use list_item::{self, ListItemFlow}; +use list_item::{ListItemFlow, ListStyleTypeContent}; use opaque_node::OpaqueNodeMethods; use parallel; use table::TableFlow; @@ -55,9 +55,10 @@ use std::borrow::ToOwned; use std::collections::DList; use std::mem; 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::{ComputedValues, make_inline}; +use style::properties::{self, ComputedValues}; use std::sync::Arc; use url::Url; @@ -265,41 +266,51 @@ impl<'a> FlowConstructor<'a> { } } - /// Builds specific `Fragment` info for the given node. - /// - /// This does *not* construct the text for generated content (but, for generated content with - /// `display: block`, it does construct the generic fragment corresponding to the block). - /// Construction of the text fragment is done specially by `build_flow_using_children()` and - /// `build_fragments_for_replaced_inline_content()`. - pub fn build_specific_fragment_info_for_node(&mut self, node: &ThreadSafeLayoutNode) - -> SpecificFragmentInfo { - match node.type_id() { - Some(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLIFrameElement))) => { + /// Builds the fragment for the given block or subclass thereof. + fn build_fragment_for_block(&mut self, node: &ThreadSafeLayoutNode) -> Fragment { + let specific_fragment_info = match node.type_id() { + Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLIFrameElement))) => { SpecificFragmentInfo::Iframe(box IframeFragmentInfo::new(node)) } - Some(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLImageElement))) => { + Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLImageElement))) => { self.build_fragment_info_for_image(node, node.image_url()) } - Some(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLObjectElement))) => { + Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLObjectElement))) => { let data = node.get_object_data(); self.build_fragment_info_for_image(node, data) } - Some(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableElement))) => SpecificFragmentInfo::TableWrapper, - Some(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableColElement))) => { + Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLTableElement))) => { + SpecificFragmentInfo::TableWrapper + } + Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLTableColElement))) => { SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node)) } - Some(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableCellElement(_)))) => SpecificFragmentInfo::TableCell, - Some(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableRowElement))) | - Some(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableSectionElement))) => SpecificFragmentInfo::TableRow, - Some(NodeTypeId::Text) => SpecificFragmentInfo::UnscannedText(UnscannedTextFragmentInfo::new(node)), - Some(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLCanvasElement))) => { + Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLTableCellElement(_)))) => { + SpecificFragmentInfo::TableCell + } + Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLTableRowElement))) | + Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLTableSectionElement))) => { + SpecificFragmentInfo::TableRow + } + Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLCanvasElement))) => { SpecificFragmentInfo::Canvas(box CanvasFragmentInfo::new(node)) } _ => { // This includes pseudo-elements. SpecificFragmentInfo::Generic } - } + }; + + Fragment::new(node, specific_fragment_info) } /// Creates an inline flow from a set of inline fragments, then adds it as a child of the given @@ -339,7 +350,9 @@ impl<'a> FlowConstructor<'a> { let mut inline_block_flows = vec!(); for f in fragments.iter() { match f.specific { - SpecificFragmentInfo::InlineBlock(ref info) => inline_block_flows.push(info.flow_ref.clone()), + SpecificFragmentInfo::InlineBlock(ref info) => { + inline_block_flows.push(info.flow_ref.clone()) + } SpecificFragmentInfo::InlineAbsoluteHypothetical(ref info) => { inline_block_flows.push(info.flow_ref.clone()) } @@ -473,8 +486,8 @@ impl<'a> FlowConstructor<'a> { 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(UnscannedTextFragmentInfo::from_text(" ".to_owned())); + let fragment_info = SpecificFragmentInfo::UnscannedText( + UnscannedTextFragmentInfo::from_text(" ".to_owned())); let fragment = Fragment::from_opaque_node_and_style(whitespace_node, whitespace_style, whitespace_damage, @@ -488,25 +501,20 @@ impl<'a> FlowConstructor<'a> { } } - /// Constructs a block flow, beginning with the given `initial_fragment` if present and then + /// Constructs a block flow, beginning with the given `initial_fragments` if present and then /// appending the construction results of children to the child list of the block flow. {ib} /// splits and absolutely-positioned descendants are handled correctly. - fn build_flow_for_block_starting_with_fragment(&mut self, - mut flow: FlowRef, - node: &ThreadSafeLayoutNode, - initial_fragment: Option<Fragment>) - -> ConstructionResult { + fn build_flow_for_block_starting_with_fragments(&mut self, + mut flow: FlowRef, + node: &ThreadSafeLayoutNode, + mut initial_fragments: DList<Fragment>) + -> ConstructionResult { // Gather up fragments for the inline flows we might need to create. let mut inline_fragment_accumulator = InlineFragmentsAccumulator::new(); let mut consecutive_siblings = vec!(); - let mut first_fragment = match initial_fragment { - None => true, - Some(initial_fragment) => { - inline_fragment_accumulator.fragments.push_back(initial_fragment); - false - } - }; + inline_fragment_accumulator.fragments.append(&mut initial_fragments); + let mut first_fragment = inline_fragment_accumulator.fragments.is_empty(); // List of absolute descendants, in tree order. let mut abs_descendants = Descendants::new(); @@ -558,7 +566,7 @@ impl<'a> FlowConstructor<'a> { /// Constructs a flow for the given block node and its children. This method creates an /// initial fragment as appropriate and then dispatches to - /// `build_flow_for_block_starting_with_fragment`. Currently the following kinds of flows get + /// `build_flow_for_block_starting_with_fragments`. Currently the following kinds of flows get /// initial content: /// /// * Generated content gets the initial content specified by the `content` attribute of the @@ -569,32 +577,60 @@ impl<'a> FlowConstructor<'a> { /// `<textarea>`. fn build_flow_for_block(&mut self, flow: FlowRef, node: &ThreadSafeLayoutNode) -> ConstructionResult { - let initial_fragment = if node.get_pseudo_element_type() != PseudoElementType::Normal || - node.type_id() == Some(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement))) || - node.type_id() == Some(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement))) { + let mut initial_fragments = DList::new(); + if node.get_pseudo_element_type() != PseudoElementType::Normal || + node.type_id() == Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLInputElement))) || + node.type_id() == Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLTextAreaElement))) { // A TextArea's text contents are displayed through the input text // box, so don't construct them. - if node.type_id() == Some(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement))) { + if node.type_id() == Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLTextAreaElement))) { for kid in node.children() { - self.set_flow_construction_result(&kid, ConstructionResult::None) + kid.set_flow_construction_result(ConstructionResult::None) } } - Some(Fragment::new_from_specific_info( - node, - SpecificFragmentInfo::UnscannedText(UnscannedTextFragmentInfo::new(node)))) - } else { - None - }; - self.build_flow_for_block_starting_with_fragment(flow, node, initial_fragment) + self.create_fragments_for_node_text_content(&mut initial_fragments, node, node.style()); + } + + self.build_flow_for_block_starting_with_fragments(flow, node, initial_fragments) + } + + /// Pushes fragments appropriate for the content of the given node onto the given list. + fn create_fragments_for_node_text_content(&self, + fragments: &mut DList<Fragment>, + node: &ThreadSafeLayoutNode, + style: &Arc<ComputedValues>) { + for content_item in node.text_content().into_iter() { + let specific = match content_item { + ContentItem::String(string) => { + let info = UnscannedTextFragmentInfo::from_text(string); + SpecificFragmentInfo::UnscannedText(info) + } + content_item => { + let content_item = box GeneratedContentInfo::ContentItem(content_item); + SpecificFragmentInfo::GeneratedContent(content_item) + } + }; + + let opaque_node = OpaqueNodeMethods::from_thread_safe_layout_node(node); + fragments.push_back(Fragment::from_opaque_node_and_style(opaque_node, + style.clone(), + node.restyle_damage(), + specific)) + } } + /// 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_nonfloated_block(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult { - let flow = box BlockFlow::from_node(self, node) as Box<Flow>; + let flow = box BlockFlow::from_node_and_fragment(node, self.build_fragment_for_block(node)) + as Box<Flow>; self.build_flow_for_block(FlowRef::new(flow), node) } @@ -602,7 +638,9 @@ impl<'a> FlowConstructor<'a> { /// a `BlockFlow` underneath it. fn build_flow_for_floated_block(&mut self, node: &ThreadSafeLayoutNode, float_kind: FloatKind) -> ConstructionResult { - let flow = box BlockFlow::float_from_node(self, node, float_kind) as Box<Flow>; + let fragment = self.build_fragment_for_block(node); + let flow = box BlockFlow::float_from_node_and_fragment(node, fragment, float_kind) as + Box<Flow>; self.build_flow_for_block(FlowRef::new(flow), node) } @@ -670,8 +708,8 @@ impl<'a> FlowConstructor<'a> { whitespace_style, whitespace_damage)) => { // Instantiate the whitespace fragment. - let fragment_info = SpecificFragmentInfo::UnscannedText(UnscannedTextFragmentInfo::from_text( - " ".to_owned())); + let fragment_info = SpecificFragmentInfo::UnscannedText( + UnscannedTextFragmentInfo::from_text(" ".to_owned())); let fragment = Fragment::from_opaque_node_and_style(whitespace_node, whitespace_style, whitespace_damage, @@ -706,7 +744,7 @@ impl<'a> FlowConstructor<'a> { fn build_fragments_for_replaced_inline_content(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult { for kid in node.children() { - self.set_flow_construction_result(&kid, ConstructionResult::None) + kid.set_flow_construction_result(ConstructionResult::None) } // If this node is ignorable whitespace, bail out now. @@ -725,30 +763,22 @@ impl<'a> FlowConstructor<'a> { // `baz` had better not be absolutely positioned! let mut style = (*node.style()).clone(); if style.get_box().display != display::T::inline { - style = Arc::new(make_inline(&*style)) + style = Arc::new(properties::make_inline(&*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 fragment = if node.get_pseudo_element_type() != PseudoElementType::Normal { - let fragment_info = - SpecificFragmentInfo::UnscannedText(UnscannedTextFragmentInfo::new(node)); - Fragment::from_opaque_node_and_style( - OpaqueNodeMethods::from_thread_safe_layout_node(node), - style, - node.restyle_damage(), - fragment_info) - } else { - Fragment::from_opaque_node_and_style( - OpaqueNodeMethods::from_thread_safe_layout_node(node), - style, - node.restyle_damage(), - self.build_specific_fragment_info_for_node(node)) - }; - let mut fragments = DList::new(); - fragments.push_back(fragment); + match (node.get_pseudo_element_type(), node.type_id()) { + (_, Some(NodeTypeId::Text)) => { + self.create_fragments_for_node_text_content(&mut fragments, node, &style) + } + (PseudoElementType::Normal, _) => { + fragments.push_back(self.build_fragment_for_block(node)); + } + (_, _) => self.create_fragments_for_node_text_content(&mut fragments, node, &style), + } let construction_item = ConstructionItem::InlineFragments(InlineFragmentsConstructionResult { @@ -769,7 +799,7 @@ impl<'a> FlowConstructor<'a> { let fragment_info = SpecificFragmentInfo::InlineBlock(InlineBlockFragmentInfo::new( block_flow)); - let fragment = Fragment::new_from_specific_info(node, fragment_info); + let fragment = Fragment::new(node, fragment_info); let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node); fragment_accumulator.fragments.push_back(fragment); @@ -795,7 +825,7 @@ impl<'a> FlowConstructor<'a> { let fragment_info = SpecificFragmentInfo::InlineAbsoluteHypothetical( InlineAbsoluteHypotheticalFragmentInfo::new(block_flow)); - let fragment = Fragment::new_from_specific_info(node, fragment_info); + let fragment = Fragment::new(node, fragment_info); let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node); fragment_accumulator.fragments.push_back(fragment); @@ -880,7 +910,7 @@ impl<'a> FlowConstructor<'a> { /// possibly other `TableCaptionFlow`s or `TableFlow`s underneath it. fn build_flow_for_table_wrapper(&mut self, node: &ThreadSafeLayoutNode, float_value: float::T) -> ConstructionResult { - let fragment = Fragment::new_from_specific_info(node, SpecificFragmentInfo::TableWrapper); + let fragment = Fragment::new(node, SpecificFragmentInfo::TableWrapper); let wrapper_flow = match float_value { float::T::none => box TableWrapperFlow::from_node_and_fragment(node, fragment), _ => { @@ -890,7 +920,7 @@ impl<'a> FlowConstructor<'a> { }; let mut wrapper_flow = FlowRef::new(wrapper_flow as Box<Flow>); - let table_fragment = Fragment::new_from_specific_info(node, SpecificFragmentInfo::Table); + let table_fragment = Fragment::new(node, SpecificFragmentInfo::Table); let table_flow = box TableFlow::from_node_and_fragment(node, table_fragment); let table_flow = FlowRef::new(table_flow as Box<Flow>); @@ -924,7 +954,8 @@ impl<'a> FlowConstructor<'a> { wrapper_flow.finish(); let is_positioned = wrapper_flow.as_block().is_positioned(); let is_fixed_positioned = wrapper_flow.as_block().is_fixed(); - let is_absolutely_positioned = flow::base(&*wrapper_flow).flags.contains(IS_ABSOLUTELY_POSITIONED); + let is_absolutely_positioned = + flow::base(&*wrapper_flow).flags.contains(IS_ABSOLUTELY_POSITIONED); if is_positioned { // This is the containing block for all the absolute descendants. wrapper_flow.set_absolute_descendants(abs_descendants); @@ -947,7 +978,8 @@ impl<'a> FlowConstructor<'a> { /// Builds a flow for a node with `display: table-caption`. This yields a `TableCaptionFlow` /// with possibly other `BlockFlow`s or `InlineFlow`s underneath it. fn build_flow_for_table_caption(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult { - let flow = box TableCaptionFlow::from_node(self, node) as Box<Flow>; + let fragment = self.build_fragment_for_block(node); + let flow = box TableCaptionFlow::from_node_and_fragment(node, fragment) as Box<Flow>; self.build_flow_for_block(FlowRef::new(flow), node) } @@ -955,7 +987,7 @@ impl<'a> FlowConstructor<'a> { /// with possibly other `TableRowFlow`s underneath it. fn build_flow_for_table_rowgroup(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult { - let fragment = Fragment::new_from_specific_info(node, SpecificFragmentInfo::TableRow); + let fragment = Fragment::new(node, SpecificFragmentInfo::TableRow); let flow = box TableRowGroupFlow::from_node_and_fragment(node, fragment); let flow = flow as Box<Flow>; self.build_flow_for_block(FlowRef::new(flow), node) @@ -964,7 +996,7 @@ impl<'a> FlowConstructor<'a> { /// 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: &ThreadSafeLayoutNode) -> ConstructionResult { - let fragment = Fragment::new_from_specific_info(node, SpecificFragmentInfo::TableRow); + let fragment = Fragment::new(node, SpecificFragmentInfo::TableRow); let flow = box TableRowFlow::from_node_and_fragment(node, fragment) as Box<Flow>; self.build_flow_for_block(FlowRef::new(flow), node) } @@ -972,7 +1004,7 @@ impl<'a> FlowConstructor<'a> { /// 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: &ThreadSafeLayoutNode) -> ConstructionResult { - let fragment = Fragment::new_from_specific_info(node, SpecificFragmentInfo::TableCell); + let fragment = Fragment::new(node, SpecificFragmentInfo::TableCell); // 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 @@ -1000,19 +1032,18 @@ impl<'a> FlowConstructor<'a> { }; let marker_fragment = match node.style().get_list().list_style_image { Some(ref url) => { - Some(Fragment::new_from_specific_info( - node, - self.build_fragment_info_for_image(node, Some((*url).clone())))) + Some(Fragment::new(node, + self.build_fragment_info_for_image(node, Some((*url).clone())))) } None => { - match list_item::static_text_for_list_style_type(node.style() + match ListStyleTypeContent::from_list_style_type(node.style() .get_list() .list_style_type) { - None => None, - Some(text) => { - let text = text.to_owned(); + ListStyleTypeContent::None => None, + ListStyleTypeContent::StaticText(ch) => { + let text = format!("{}\u{a0}", ch); let mut unscanned_marker_fragments = DList::new(); - unscanned_marker_fragments.push_back(Fragment::new_from_specific_info( + unscanned_marker_fragments.push_back(Fragment::new( node, SpecificFragmentInfo::UnscannedText( UnscannedTextFragmentInfo::from_text(text)))); @@ -1022,6 +1053,9 @@ impl<'a> FlowConstructor<'a> { debug_assert!(marker_fragments.len() == 1); marker_fragments.fragments.into_iter().next() } + ListStyleTypeContent::GeneratedContent(info) => { + Some(Fragment::new(node, SpecificFragmentInfo::GeneratedContent(info))) + } } } }; @@ -1031,28 +1065,29 @@ impl<'a> FlowConstructor<'a> { // 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 flow; - let initial_fragment; - match node.style().get_list().list_style_position { + let mut initial_fragments = DList::new(); + let main_fragment = self.build_fragment_for_block(node); + let flow = match node.style().get_list().list_style_position { list_style_position::T::outside => { - flow = box ListItemFlow::from_node_marker_and_flotation(self, - node, - marker_fragment, - flotation); - initial_fragment = None; + box ListItemFlow::from_node_fragments_and_flotation(node, + main_fragment, + marker_fragment, + flotation) } list_style_position::T::inside => { - flow = box ListItemFlow::from_node_marker_and_flotation(self, - node, - None, - flotation); - initial_fragment = marker_fragment; + if let Some(marker_fragment) = marker_fragment { + initial_fragments.push_back(marker_fragment) + } + box ListItemFlow::from_node_fragments_and_flotation(node, + main_fragment, + None, + flotation) } - } + }; - self.build_flow_for_block_starting_with_fragment(FlowRef::new(flow as Box<Flow>), - node, - initial_fragment) + self.build_flow_for_block_starting_with_fragments(FlowRef::new(flow as Box<Flow>), + node, + initial_fragments) } /// Creates a fragment for a node with `display: table-column`. @@ -1060,13 +1095,12 @@ impl<'a> FlowConstructor<'a> { -> 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) + kid.set_flow_construction_result(ConstructionResult::None) } let specific = SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node)); - let construction_item = ConstructionItem::TableColumnFragment( - Fragment::new_from_specific_info(node, specific) - ); + let construction_item = ConstructionItem::TableColumnFragment(Fragment::new(node, + specific)); ConstructionResult::ConstructionItem(construction_item) } @@ -1074,9 +1108,9 @@ impl<'a> FlowConstructor<'a> { /// This yields a `TableColGroupFlow`. fn build_flow_for_table_colgroup(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult { - let fragment = Fragment::new_from_specific_info( - node, - SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node))); + let fragment = + Fragment::new(node, + SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node))); 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` @@ -1092,7 +1126,7 @@ impl<'a> FlowConstructor<'a> { if col_fragments.is_empty() { debug!("add SpecificFragmentInfo::TableColumn for empty colgroup"); let specific = SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node)); - col_fragments.push(Fragment::new_from_specific_info(node, specific)); + col_fragments.push(Fragment::new(node, specific)); } let flow = box TableColGroupFlow::from_node_and_fragments(node, fragment, col_fragments); let mut flow = FlowRef::new(flow as Box<Flow>); @@ -1188,15 +1222,15 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { // results of children. (display::T::none, _, _) => { for child in node.children() { - self.set_flow_construction_result(&child, ConstructionResult::None); + child.set_flow_construction_result(ConstructionResult::None); } - self.set_flow_construction_result(node, ConstructionResult::None); + node.set_flow_construction_result(ConstructionResult::None); } // Table items contribute table flow construction results. (display::T::table, float_value, _) => { let construction_result = self.build_flow_for_table_wrapper(node, float_value); - self.set_flow_construction_result(node, construction_result) + node.set_flow_construction_result(construction_result) } // Absolutely positioned elements will have computed value of @@ -1207,14 +1241,13 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { // below. (display::T::block, _, position::T::absolute) | (_, _, position::T::fixed) => { - let construction_result = self.build_flow_for_nonfloated_block(node); - self.set_flow_construction_result(node, construction_result) + node.set_flow_construction_result(self.build_flow_for_nonfloated_block(node)) } // List items contribute their own special flows. (display::T::list_item, float_value, _) => { - let construction_result = self.build_flow_for_list_item(node, float_value); - self.set_flow_construction_result(node, construction_result) + node.set_flow_construction_result(self.build_flow_for_list_item(node, + float_value)) } // Inline items that are absolutely-positioned contribute inline fragment construction @@ -1222,7 +1255,7 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { (display::T::inline, _, position::T::absolute) => { let construction_result = self.build_fragment_for_absolutely_positioned_inline(node); - self.set_flow_construction_result(node, construction_result) + node.set_flow_construction_result(construction_result) } // Inline items contribute inline fragment construction results. @@ -1230,31 +1263,31 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { // FIXME(pcwalton, #3307): This is not sufficient to handle floated generated content. (display::T::inline, float::T::none, _) => { let construction_result = self.build_fragments_for_inline(node); - self.set_flow_construction_result(node, construction_result) + node.set_flow_construction_result(construction_result) } // Inline-block items contribute inline fragment construction results. (display::T::inline_block, float::T::none, _) => { let construction_result = self.build_fragment_for_inline_block(node); - self.set_flow_construction_result(node, construction_result) + node.set_flow_construction_result(construction_result) } // Table items contribute table flow construction results. (display::T::table_caption, _, _) => { let construction_result = self.build_flow_for_table_caption(node); - self.set_flow_construction_result(node, construction_result) + node.set_flow_construction_result(construction_result) } // Table items contribute table flow construction results. (display::T::table_column_group, _, _) => { let construction_result = self.build_flow_for_table_colgroup(node); - self.set_flow_construction_result(node, construction_result) + node.set_flow_construction_result(construction_result) } // Table items contribute table flow construction results. (display::T::table_column, _, _) => { let construction_result = self.build_fragments_for_table_column(node); - self.set_flow_construction_result(node, construction_result) + node.set_flow_construction_result(construction_result) } // Table items contribute table flow construction results. @@ -1262,19 +1295,19 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { (display::T::table_header_group, _, _) | (display::T::table_footer_group, _, _) => { let construction_result = self.build_flow_for_table_rowgroup(node); - self.set_flow_construction_result(node, construction_result) + node.set_flow_construction_result(construction_result) } // Table items contribute table flow construction results. (display::T::table_row, _, _) => { let construction_result = self.build_flow_for_table_row(node); - self.set_flow_construction_result(node, construction_result) + node.set_flow_construction_result(construction_result) } // Table items contribute table flow construction results. (display::T::table_cell, _, _) => { let construction_result = self.build_flow_for_table_cell(node); - self.set_flow_construction_result(node, construction_result) + node.set_flow_construction_result(construction_result) } // Block flows that are not floated contribute block flow construction results. @@ -1283,15 +1316,14 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { // properties separately. (_, float::T::none, _) => { - let construction_result = self.build_flow_for_nonfloated_block(node); - self.set_flow_construction_result(node, construction_result) + node.set_flow_construction_result(self.build_flow_for_nonfloated_block(node)) } // Floated flows contribute float flow construction results. (_, float_value, _) => { let float_kind = FloatKind::from_property(float_value); - let construction_result = self.build_flow_for_floated_block(node, float_kind); - self.set_flow_construction_result(node, construction_result) + node.set_flow_construction_result( + self.build_flow_for_floated_block(node, float_kind)) } } @@ -1310,25 +1342,29 @@ trait NodeUtils { /// Sets the construction result of a flow. fn set_flow_construction_result(self, result: ConstructionResult); - /// Replaces the flow construction result in a node with `ConstructionResult::None` and returns the - /// old value. + /// Replaces the flow construction result in a node with `ConstructionResult::None` and returns + /// the old value. fn swap_out_construction_result(self) -> ConstructionResult; } impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> { fn is_replaced_content(&self) -> bool { match self.type_id() { + None | Some(NodeTypeId::Text) | Some(NodeTypeId::ProcessingInstruction) | Some(NodeTypeId::Comment) | Some(NodeTypeId::DocumentType) | Some(NodeTypeId::DocumentFragment) | Some(NodeTypeId::Document) | - None | - Some(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLImageElement))) => true, - Some(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLObjectElement))) => self.has_object_data(), - Some(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLIFrameElement))) => true, - Some(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLCanvasElement))) => true, + Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLImageElement))) | + Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLIFrameElement))) | + Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLCanvasElement))) => true, + Some(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLObjectElement))) => self.has_object_data(), Some(NodeTypeId::Element(_)) => false, } } diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index b5985a8a937..fe361cd2575 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -957,6 +957,7 @@ impl FragmentDisplayListBuilding for Fragment { } } SpecificFragmentInfo::Generic | + SpecificFragmentInfo::GeneratedContent(..) | SpecificFragmentInfo::Iframe(..) | SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell | diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 6049afa8634..1d737348c54 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -33,9 +33,9 @@ use floats::Floats; use flow_list::{FlowList, FlowListIterator, MutFlowListIterator}; use flow_ref::FlowRef; use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo}; -use incremental::{RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW, RestyleDamage}; +use incremental::{self, RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW, RestyleDamage}; use inline::InlineFlow; -use model::{CollapsibleMargins, IntrinsicISizes}; +use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo}; use parallel::FlowParallelInfo; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, TableFlow}; use table_caption::TableCaptionFlow; @@ -236,6 +236,15 @@ pub trait Flow: fmt::Debug + Sync { iterator: &mut FragmentBorderBoxIterator, stacking_context_position: &Point2D<Au>); + /// Mutably iterates through fragments in this flow. + fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)); + + fn compute_collapsible_block_start_margin(&mut self, + _layout_context: &mut LayoutContext, + _margin_collapse_info: &mut MarginCollapseInfo) { + // The default implementation is a no-op. + } + /// Marks this flow as the root flow. The default implementation is a no-op. fn mark_as_root(&mut self) {} @@ -478,51 +487,67 @@ pub trait PostorderFlowTraversal { } } +/// An in-order (sequential only) traversal. +pub trait InorderFlowTraversal { + /// The operation to perform. Returns the level of the tree we're at. + fn process(&mut self, flow: &mut Flow, level: u32); + + /// Returns true if this node should be processed and false if neither this node nor its + /// descendants should be processed. + fn should_process(&mut self, flow: &mut Flow) -> bool; +} + bitflags! { #[doc = "Flags used in flows."] - flags FlowFlags: u16 { + flags FlowFlags: u32 { // floated descendants flags #[doc = "Whether this flow has descendants that float left in the same block formatting"] #[doc = "context."] - const HAS_LEFT_FLOATED_DESCENDANTS = 0b0000_0000_0000_0001, + const HAS_LEFT_FLOATED_DESCENDANTS = 0b0000_0000_0000_0000_0001, #[doc = "Whether this flow has descendants that float right in the same block formatting"] #[doc = "context."] - const HAS_RIGHT_FLOATED_DESCENDANTS = 0b0000_0000_0000_0010, + const HAS_RIGHT_FLOATED_DESCENDANTS = 0b0000_0000_0000_0000_0010, #[doc = "Whether this flow is impacted by floats to the left in the same block formatting"] #[doc = "context (i.e. its height depends on some prior flows with `float: left`)."] - const IMPACTED_BY_LEFT_FLOATS = 0b0000_0000_0000_0100, + const IMPACTED_BY_LEFT_FLOATS = 0b0000_0000_0000_0000_0100, #[doc = "Whether this flow is impacted by floats to the right in the same block"] #[doc = "formatting context (i.e. its height depends on some prior flows with `float:"] #[doc = "right`)."] - const IMPACTED_BY_RIGHT_FLOATS = 0b0000_0000_0000_1000, + const IMPACTED_BY_RIGHT_FLOATS = 0b0000_0000_0000_0000_1000, // text align flags #[doc = "Whether this flow contains a flow that has its own layer within the same absolute"] #[doc = "containing block."] - const LAYERS_NEEDED_FOR_DESCENDANTS = 0b0000_0000_0001_0000, + const LAYERS_NEEDED_FOR_DESCENDANTS = 0b0000_0000_0000_0001_0000, #[doc = "Whether this flow must have its own layer. Even if this flag is not set, it might"] #[doc = "get its own layer if it's deemed to be likely to overlap flows with their own"] #[doc = "layer."] - const NEEDS_LAYER = 0b0000_0000_0010_0000, + const NEEDS_LAYER = 0b0000_0000_0000_0010_0000, #[doc = "Whether this flow is absolutely positioned. This is checked all over layout, so a"] #[doc = "virtual call is too expensive."] - const IS_ABSOLUTELY_POSITIONED = 0b0000_0000_0100_0000, + const IS_ABSOLUTELY_POSITIONED = 0b0000_0000_0000_0100_0000, #[doc = "Whether this flow clears to the left. This is checked all over layout, so a"] #[doc = "virtual call is too expensive."] - const CLEARS_LEFT = 0b0000_0000_1000_0000, + const CLEARS_LEFT = 0b0000_0000_0000_1000_0000, #[doc = "Whether this flow clears to the right. This is checked all over layout, so a"] #[doc = "virtual call is too expensive."] - const CLEARS_RIGHT = 0b0000_0001_0000_0000, + const CLEARS_RIGHT = 0b0000_0000_0001_0000_0000, #[doc = "Whether this flow is left-floated. This is checked all over layout, so a"] #[doc = "virtual call is too expensive."] - const FLOATS_LEFT = 0b0000_0010_0000_0000, + const FLOATS_LEFT = 0b0000_0000_0010_0000_0000, #[doc = "Whether this flow is right-floated. This is checked all over layout, so a"] #[doc = "virtual call is too expensive."] - const FLOATS_RIGHT = 0b0000_0100_0000_0000, + const FLOATS_RIGHT = 0b0000_0000_0100_0000_0000, #[doc = "Text alignment. \ NB: If you update this, update `TEXT_ALIGN_SHIFT` below."] - const TEXT_ALIGN = 0b0111_1000_0000_0000, + const TEXT_ALIGN = 0b0000_0111_1000_0000_0000, + #[doc = "Whether this flow has a fragment with `counter-reset` or `counter-increment` \ + styles."] + const AFFECTS_COUNTERS = 0b0000_1000_0000_0000_0000, + #[doc = "Whether this flow's descendants have fragments that affect `counter-reset` or \ + `counter-increment` styles."] + const HAS_COUNTER_AFFECTING_CHILDREN = 0b0001_0000_0000_0000_0000 } } @@ -548,13 +573,13 @@ impl FlowFlags { #[inline] pub fn text_align(self) -> text_align::T { - FromPrimitive::from_u16((self & TEXT_ALIGN).bits() >> TEXT_ALIGN_SHIFT).unwrap() + FromPrimitive::from_u32((self & TEXT_ALIGN).bits() >> TEXT_ALIGN_SHIFT).unwrap() } #[inline] pub fn set_text_align(&mut self, value: text_align::T) { *self = (*self & !TEXT_ALIGN) | - FlowFlags::from_bits((value as u16) << TEXT_ALIGN_SHIFT).unwrap(); + FlowFlags::from_bits((value as u32) << TEXT_ALIGN_SHIFT).unwrap(); } #[inline] @@ -885,39 +910,41 @@ impl BaseFlow { force_nonfloated: ForceNonfloatedFlag) -> BaseFlow { let mut flags = FlowFlags::empty(); - match node { - None => {} - Some(node) => { - let node_style = node.style(); - match node_style.get_box().position { - position::T::absolute | position::T::fixed => { - flags.insert(IS_ABSOLUTELY_POSITIONED) - } - _ => {} + if let Some(node) = node { + let node_style = node.style(); + match node_style.get_box().position { + position::T::absolute | position::T::fixed => { + flags.insert(IS_ABSOLUTELY_POSITIONED) } + _ => {} + } - if force_nonfloated == ForceNonfloatedFlag::FloatIfNecessary { - match node_style.get_box().float { - float::T::none => {} - float::T::left => flags.insert(FLOATS_LEFT), - float::T::right => flags.insert(FLOATS_RIGHT), - } + if force_nonfloated == ForceNonfloatedFlag::FloatIfNecessary { + match node_style.get_box().float { + float::T::none => {} + float::T::left => flags.insert(FLOATS_LEFT), + float::T::right => flags.insert(FLOATS_RIGHT), } + } - match node_style.get_box().clear { - clear::T::none => {} - clear::T::left => flags.insert(CLEARS_LEFT), - clear::T::right => flags.insert(CLEARS_RIGHT), - clear::T::both => { - flags.insert(CLEARS_LEFT); - flags.insert(CLEARS_RIGHT); - } + match node_style.get_box().clear { + clear::T::none => {} + clear::T::left => flags.insert(CLEARS_LEFT), + clear::T::right => flags.insert(CLEARS_RIGHT), + clear::T::both => { + flags.insert(CLEARS_LEFT); + flags.insert(CLEARS_RIGHT); } } + + if !node_style.get_counters().counter_reset.0.is_empty() || + !node_style.get_counters().counter_increment.0.is_empty() { + flags.insert(AFFECTS_COUNTERS) + } } // New flows start out as fully damaged. - let mut damage = RestyleDamage::all(); + let mut damage = incremental::rebuild_and_reflow(); damage.remove(RECONSTRUCT_FLOW); BaseFlow { @@ -992,10 +1019,12 @@ impl BaseFlow { } impl<'a> ImmutableFlowUtils for &'a (Flow + 'a) { - /// Returns true if this flow is a block flow. + /// Returns true if this flow is a block flow or subclass thereof. fn is_block_like(self) -> bool { match self.class() { - FlowClass::Block => true, + FlowClass::Block | FlowClass::ListItem | FlowClass::Table | FlowClass::TableRowGroup | + FlowClass::TableRow | FlowClass::TableCaption | FlowClass::TableCell | + FlowClass::TableWrapper => true, _ => false, } } diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 4faa266217c..3b8dff61e63 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -8,13 +8,12 @@ use canvas::canvas_paint_task::CanvasMsg; use css::node_style::StyledNode; -use construct::FlowConstructor; use context::LayoutContext; use floats::ClearType; use flow; use flow::Flow; use flow_ref::FlowRef; -use incremental::RestyleDamage; +use incremental::{self, RestyleDamage}; use inline::{InlineFragmentContext, InlineMetrics}; use layout_debug; use model::{IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, specified}; @@ -44,15 +43,17 @@ use std::collections::DList; use std::fmt; use std::num::ToPrimitive; use std::str::FromStr; -use std::sync::{Arc, Mutex}; use std::sync::mpsc::Sender; +use std::sync::{Arc, Mutex}; use string_cache::Atom; -use style::properties::{ComputedValues, cascade_anonymous, make_border}; -use style::node::{TElement, TNode}; -use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; +use style::computed_values::content::ContentItem; use style::computed_values::{clear, mix_blend_mode, overflow_wrap}; use style::computed_values::{position, text_align, text_decoration, vertical_align, white_space}; use style::computed_values::{word_break}; +use style::node::{TElement, TNode}; +use style::properties::{ComputedValues, cascade_anonymous, make_border}; +use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; +use style::values::computed::{LengthOrPercentageOrNone}; use text::TextRunScanner; use url::Url; @@ -136,6 +137,11 @@ impl Encodable for Fragment { #[derive(Clone)] pub enum SpecificFragmentInfo { Generic, + + /// A piece of generated content that cannot be resolved into `ScannedText` until the generated + /// content resolution phase (e.g. an ordered list item marker). + GeneratedContent(Box<GeneratedContentInfo>), + Iframe(Box<IframeFragmentInfo>), Image(Box<ImageFragmentInfo>), Canvas(Box<CanvasFragmentInfo>), @@ -158,17 +164,18 @@ impl SpecificFragmentInfo { fn restyle_damage(&self) -> RestyleDamage { let flow = match *self { - SpecificFragmentInfo::Iframe(_) - | SpecificFragmentInfo::Image(_) - | SpecificFragmentInfo::ScannedText(_) - | SpecificFragmentInfo::Table - | SpecificFragmentInfo::TableCell - | SpecificFragmentInfo::TableColumn(_) - | SpecificFragmentInfo::TableRow - | SpecificFragmentInfo::TableWrapper - | SpecificFragmentInfo::UnscannedText(_) - | SpecificFragmentInfo::Canvas(_) - | SpecificFragmentInfo::Generic => return RestyleDamage::empty(), + SpecificFragmentInfo::Canvas(_) | + SpecificFragmentInfo::GeneratedContent(_) | + SpecificFragmentInfo::Iframe(_) | + SpecificFragmentInfo::Image(_) | + SpecificFragmentInfo::ScannedText(_) | + SpecificFragmentInfo::Table | + SpecificFragmentInfo::TableCell | + SpecificFragmentInfo::TableColumn(_) | + SpecificFragmentInfo::TableRow | + SpecificFragmentInfo::TableWrapper | + SpecificFragmentInfo::UnscannedText(_) | + SpecificFragmentInfo::Generic => return RestyleDamage::empty(), SpecificFragmentInfo::InlineAbsoluteHypothetical(ref info) => &info.flow_ref, SpecificFragmentInfo::InlineBlock(ref info) => &info.flow_ref, }; @@ -180,9 +187,12 @@ impl SpecificFragmentInfo { match *self { SpecificFragmentInfo::Canvas(_) => "SpecificFragmentInfo::Canvas", SpecificFragmentInfo::Generic => "SpecificFragmentInfo::Generic", + SpecificFragmentInfo::GeneratedContent(_) => "SpecificFragmentInfo::GeneratedContent", SpecificFragmentInfo::Iframe(_) => "SpecificFragmentInfo::Iframe", SpecificFragmentInfo::Image(_) => "SpecificFragmentInfo::Image", - SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => "SpecificFragmentInfo::InlineAbsoluteHypothetical", + SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => { + "SpecificFragmentInfo::InlineAbsoluteHypothetical" + } SpecificFragmentInfo::InlineBlock(_) => "SpecificFragmentInfo::InlineBlock", SpecificFragmentInfo::ScannedText(_) => "SpecificFragmentInfo::ScannedText", SpecificFragmentInfo::Table => "SpecificFragmentInfo::Table", @@ -196,8 +206,11 @@ impl SpecificFragmentInfo { } /// Clamp a value obtained from style_length, based on min / max lengths. -fn clamp_size(size: Au, min_size: LengthOrPercentage, max_size: LengthOrPercentageOrNone, - container_inline_size: Au) -> Au { +fn clamp_size(size: Au, + min_size: LengthOrPercentage, + max_size: LengthOrPercentageOrNone, + container_inline_size: Au) + -> Au { let min_size = model::specified(min_size, container_inline_size); let max_size = model::specified_or_none(max_size, container_inline_size); @@ -207,6 +220,13 @@ fn clamp_size(size: Au, min_size: LengthOrPercentage, max_size: LengthOrPercenta }) } +/// Information for generated content. +#[derive(Clone)] +pub enum GeneratedContentInfo { + ListItem, + ContentItem(ContentItem), +} + /// A hypothetical box (see CSS 2.1 § 10.3.7) for an absolutely-positioned block that was declared /// with `display: inline;`. /// @@ -637,14 +657,6 @@ pub struct UnscannedTextFragmentInfo { } impl UnscannedTextFragmentInfo { - /// Creates a new instance of `UnscannedTextFragmentInfo` from the given DOM node. - pub fn new(node: &ThreadSafeLayoutNode) -> UnscannedTextFragmentInfo { - // FIXME(pcwalton): Don't copy text; atomically reference count it instead. - UnscannedTextFragmentInfo { - text: box node.text(), - } - } - /// Creates a new instance of `UnscannedTextFragmentInfo` from the given text. #[inline] pub fn from_text(text: String) -> UnscannedTextFragmentInfo { @@ -678,34 +690,8 @@ impl TableColumnFragmentInfo { } impl Fragment { - /// Constructs a new `Fragment` instance for the given node. - /// - /// This does *not* construct the text for generated content. See comments in - /// `FlowConstructor::build_specific_fragment_info_for_node()` for more details. - /// - /// Arguments: - /// - /// * `constructor`: The flow constructor. - /// * `node`: The node to create a fragment for. - pub fn new(constructor: &mut FlowConstructor, node: &ThreadSafeLayoutNode) -> Fragment { - let style = node.style().clone(); - let writing_mode = style.writing_mode; - Fragment { - node: OpaqueNodeMethods::from_thread_safe_layout_node(node), - style: style, - restyle_damage: node.restyle_damage(), - border_box: LogicalRect::zero(writing_mode), - border_padding: LogicalMargin::zero(writing_mode), - margin: LogicalMargin::zero(writing_mode), - specific: constructor.build_specific_fragment_info_for_node(node), - inline_context: None, - debug_id: layout_debug::generate_unique_debug_id(), - } - } - - /// Constructs a new `Fragment` instance from a specific info. - pub fn new_from_specific_info(node: &ThreadSafeLayoutNode, specific: SpecificFragmentInfo) - -> Fragment { + /// Constructs a new `Fragment` instance. + pub fn new(node: &ThreadSafeLayoutNode, specific: SpecificFragmentInfo) -> Fragment { let style = node.style().clone(); let writing_mode = style.writing_mode; Fragment { @@ -721,24 +707,6 @@ impl Fragment { } } - /// Constructs a new `Fragment` instance for an anonymous object. - pub fn new_anonymous(constructor: &mut FlowConstructor, node: &ThreadSafeLayoutNode) - -> Fragment { - let node_style = cascade_anonymous(&**node.style()); - let writing_mode = node_style.writing_mode; - Fragment { - node: OpaqueNodeMethods::from_thread_safe_layout_node(node), - style: Arc::new(node_style), - restyle_damage: node.restyle_damage(), - border_box: LogicalRect::zero(writing_mode), - border_padding: LogicalMargin::zero(writing_mode), - margin: LogicalMargin::zero(writing_mode), - specific: constructor.build_specific_fragment_info_for_node(node), - inline_context: None, - debug_id: layout_debug::generate_unique_debug_id(), - } - } - /// Constructs a new `Fragment` instance for an anonymous table object. pub fn new_anonymous_from_specific_info(node: &ThreadSafeLayoutNode, specific: SpecificFragmentInfo) @@ -837,7 +805,7 @@ impl Fragment { Fragment { node: self.node, style: self.style.clone(), - restyle_damage: RestyleDamage::all(), + restyle_damage: incremental::rebuild_and_reflow(), border_box: new_border_box, border_padding: self.border_padding, margin: self.margin, @@ -912,6 +880,7 @@ impl Fragment { match self.specific { SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Generic | + SpecificFragmentInfo::GeneratedContent(_) | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::InlineBlock(_) => { @@ -931,7 +900,9 @@ impl Fragment { INTRINSIC_INLINE_SIZE_INCLUDES_BORDER | INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED } - SpecificFragmentInfo::ScannedText(_) | SpecificFragmentInfo::TableColumn(_) | SpecificFragmentInfo::UnscannedText(_) | + SpecificFragmentInfo::ScannedText(_) | + SpecificFragmentInfo::TableColumn(_) | + SpecificFragmentInfo::UnscannedText(_) | SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => { QuantitiesIncludedInIntrinsicInlineSizes::empty() } @@ -1221,6 +1192,14 @@ impl Fragment { } } + /// Returns true if and only if this fragment is a generated content fragment. + pub fn is_generated_content(&self) -> bool { + match self.specific { + SpecificFragmentInfo::GeneratedContent(..) => true, + _ => false, + } + } + /// Returns true if and only if this is a scanned text fragment. pub fn is_scanned_text_fragment(&self) -> bool { match self.specific { @@ -1234,6 +1213,7 @@ impl Fragment { let mut result = self.style_specified_intrinsic_inline_size(); match self.specific { SpecificFragmentInfo::Generic | + SpecificFragmentInfo::GeneratedContent(_) | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell | @@ -1303,6 +1283,7 @@ impl Fragment { pub fn content_inline_size(&self) -> Au { match self.specific { SpecificFragmentInfo::Generic | + SpecificFragmentInfo::GeneratedContent(_) | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell | @@ -1334,6 +1315,7 @@ impl Fragment { pub fn content_block_size(&self, layout_context: &LayoutContext) -> Au { match self.specific { SpecificFragmentInfo::Generic | + SpecificFragmentInfo::GeneratedContent(_) | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell | @@ -1352,7 +1334,7 @@ impl Fragment { self.calculate_line_height(layout_context) } SpecificFragmentInfo::TableColumn(_) => { - panic!("Table column fragments do not have block_size") + panic!("Table column fragments do not have block size") } SpecificFragmentInfo::UnscannedText(_) => { panic!("Unscanned text fragments should have been scanned by now!") @@ -1382,6 +1364,7 @@ impl Fragment { match self.specific { SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Generic | + SpecificFragmentInfo::GeneratedContent(_) | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::Table | @@ -1689,22 +1672,23 @@ impl Fragment { pub fn assign_replaced_inline_size_if_necessary<'a>(&'a mut self, container_inline_size: Au) { match self.specific { SpecificFragmentInfo::Generic | + SpecificFragmentInfo::GeneratedContent(_) | SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell | SpecificFragmentInfo::TableRow | SpecificFragmentInfo::TableWrapper => return, SpecificFragmentInfo::TableColumn(_) => { - panic!("Table column fragments do not have inline_size") + panic!("Table column fragments do not have inline size") } SpecificFragmentInfo::UnscannedText(_) => { panic!("Unscanned text fragments should have been scanned by now!") } SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Image(_) | - SpecificFragmentInfo::ScannedText(_) | + SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::InlineBlock(_) | SpecificFragmentInfo::InlineAbsoluteHypothetical(_) | - SpecificFragmentInfo::Iframe(_) => {} + SpecificFragmentInfo::ScannedText(_) => {} }; let style = self.style().clone(); @@ -1770,22 +1754,23 @@ impl Fragment { pub fn assign_replaced_block_size_if_necessary(&mut self, containing_block_block_size: Au) { match self.specific { SpecificFragmentInfo::Generic | + SpecificFragmentInfo::GeneratedContent(_) | SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell | SpecificFragmentInfo::TableRow | SpecificFragmentInfo::TableWrapper => return, SpecificFragmentInfo::TableColumn(_) => { - panic!("Table column fragments do not have block_size") + panic!("Table column fragments do not have block size") } SpecificFragmentInfo::UnscannedText(_) => { panic!("Unscanned text fragments should have been scanned by now!") } SpecificFragmentInfo::Canvas(_) | + SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Image(_) | - SpecificFragmentInfo::ScannedText(_) | SpecificFragmentInfo::InlineBlock(_) | SpecificFragmentInfo::InlineAbsoluteHypothetical(_) | - SpecificFragmentInfo::Iframe(_) => {} + SpecificFragmentInfo::ScannedText(_) => {} } let style = self.style().clone(); @@ -1923,6 +1908,7 @@ impl Fragment { SpecificFragmentInfo::TableWrapper => false, SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Generic | + SpecificFragmentInfo::GeneratedContent(_) | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::ScannedText(_) | diff --git a/components/layout/generated_content.rs b/components/layout/generated_content.rs new file mode 100644 index 00000000000..f90e8f79753 --- /dev/null +++ b/components/layout/generated_content.rs @@ -0,0 +1,561 @@ +/* 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/. */ + +//! The generated content assignment phase. +//! +//! This phase handles CSS counters, quotes, and ordered lists per CSS § 12.3-12.5. It cannot be +//! done in parallel and is therefore a sequential pass that runs on as little of the flow tree +//! as possible. + +use context::LayoutContext; +use flow::{self, AFFECTS_COUNTERS, Flow, HAS_COUNTER_AFFECTING_CHILDREN, ImmutableFlowUtils}; +use flow::{InorderFlowTraversal}; +use fragment::{Fragment, GeneratedContentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo}; +use incremental::{self, RESOLVE_GENERATED_CONTENT}; +use text::TextRunScanner; + +use gfx::display_list::OpaqueNode; +use std::collections::{DList, HashMap}; +use std::sync::Arc; +use style::computed_values::content::ContentItem; +use style::computed_values::{display, list_style_type}; +use style::properties::ComputedValues; +use util::smallvec::{SmallVec, SmallVec8}; + +// Decimal styles per CSS-COUNTER-STYLES § 6.1: +static DECIMAL: [char; 10] = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ]; +// TODO(pcwalton): `decimal-leading-zero` +static ARABIC_INDIC: [char; 10] = [ '٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩' ]; +// TODO(pcwalton): `armenian`, `upper-armenian`, `lower-armenian` +static BENGALI: [char; 10] = [ '০', '১', '২', '৩', '৪', '৫', '৬', '৭', '৮', '৯' ]; +static CAMBODIAN: [char; 10] = [ '០', '១', '២', '៣', '៤', '៥', '៦', '៧', '៨', '៩' ]; +// TODO(pcwalton): Suffix for CJK decimal. +static CJK_DECIMAL: [char; 10] = [ '〇', '一', '二', '三', '四', '五', '六', '七', '八', '九' ]; +static DEVANAGARI: [char; 10] = [ '०', '१', '२', '३', '४', '५', '६', '७', '८', '९' ]; +// TODO(pcwalton): `georgian` +static GUJARATI: [char; 10] = ['૦', '૧', '૨', '૩', '૪', '૫', '૬', '૭', '૮', '૯']; +static GURMUKHI: [char; 10] = ['੦', '੧', '੨', '੩', '੪', '੫', '੬', '੭', '੮', '੯']; +// TODO(pcwalton): `hebrew` +static KANNADA: [char; 10] = ['೦', '೧', '೨', '೩', '೪', '೫', '೬', '೭', '೮', '೯']; +static LAO: [char; 10] = ['໐', '໑', '໒', '໓', '໔', '໕', '໖', '໗', '໘', '໙']; +static MALAYALAM: [char; 10] = ['൦', '൧', '൨', '൩', '൪', '൫', '൬', '൭', '൮', '൯']; +static MONGOLIAN: [char; 10] = ['᠐', '᠑', '᠒', '᠓', '᠔', '᠕', '᠖', '᠗', '᠘', '᠙']; +static MYANMAR: [char; 10] = ['၀', '၁', '၂', '၃', '၄', '၅', '၆', '၇', '၈', '၉']; +static ORIYA: [char; 10] = ['୦', '୧', '୨', '୩', '୪', '୫', '୬', '୭', '୮', '୯']; +static PERSIAN: [char; 10] = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹']; +// TODO(pcwalton): `lower-roman`, `upper-roman` +static TELUGU: [char; 10] = ['౦', '౧', '౨', '౩', '౪', '౫', '౬', '౭', '౮', '౯']; +static THAI: [char; 10] = ['๐', '๑', '๒', '๓', '๔', '๕', '๖', '๗', '๘', '๙']; +static TIBETAN: [char; 10] = ['༠', '༡', '༢', '༣', '༤', '༥', '༦', '༧', '༨', '༩']; + +// Alphabetic styles per CSS-COUNTER-STYLES § 6.2: +static LOWER_ALPHA: [char; 26] = [ + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', + 't', 'u', 'v', 'w', 'x', 'y', 'z' +]; +static UPPER_ALPHA: [char; 26] = [ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', + 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' +]; +static CJK_EARTHLY_BRANCH: [char; 12] = [ + '子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥' +]; +static CJK_HEAVENLY_STEM: [char; 10] = [ + '甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸' +]; +static LOWER_GREEK: [char; 24] = [ + 'α', 'β', 'γ', 'δ', 'ε', 'ζ', 'η', 'θ', 'ι', 'κ', 'λ', 'μ', 'ν', 'ξ', 'ο', 'π', 'ρ', 'σ', 'τ', + 'υ', 'φ', 'χ', 'ψ', 'ω' +]; +static HIRAGANA: [char; 48] = [ + 'あ', 'い', 'う', 'え', 'お', 'か', 'き', 'く', 'け', 'こ', 'さ', 'し', 'す', 'せ', 'そ', + 'た', 'ち', 'つ', 'て', 'と', 'な', 'に', 'ぬ', 'ね', 'の', 'は', 'ひ', 'ふ', 'へ', 'ほ', + 'ま', 'み', 'む', 'め', 'も', 'や', 'ゆ', 'よ', 'ら', 'り', 'る', 'れ', 'ろ', + 'わ', 'ゐ', 'ゑ', 'を', 'ん' +]; +static HIRAGANA_IROHA: [char; 47] = [ + 'い', 'ろ', 'は', 'に', 'ほ', 'へ', 'と', 'ち', 'り', 'ぬ', 'る', 'を', 'わ', 'か', 'よ', + 'た', 'れ', 'そ', 'つ', 'ね', 'な', 'ら', 'む', 'う', 'ゐ', 'の', 'お', 'く', 'や', 'ま', + 'け', 'ふ', 'こ', 'え', 'て', 'あ', 'さ', 'き', 'ゆ', 'め', 'み', 'し', 'ゑ', + 'ひ', 'も', 'せ', 'す' +]; +static KATAKANA: [char; 48] = [ + 'ア', 'イ', 'ウ', 'エ', 'オ', 'カ', 'キ', 'ク', 'ケ', 'コ', 'サ', 'シ', 'ス', 'セ', 'ソ', + 'タ', 'チ', 'ツ', 'テ', 'ト', 'ナ', 'ニ', 'ヌ', 'ネ', 'ノ', 'ハ', 'ヒ', 'フ', 'ヘ', 'ホ', + 'マ', 'ミ', 'ム', 'メ', 'モ', 'ヤ', 'ユ', 'ヨ', 'ラ', 'リ', 'ル', 'レ', 'ロ', + 'ワ', 'ヰ', 'ヱ', 'ヲ', 'ン' +]; +static KATAKANA_IROHA: [char; 47] = [ + 'イ', 'ロ', 'ハ', 'ニ', 'ホ', 'ヘ', 'ト', 'チ', 'リ', 'ヌ', 'ル', 'ヲ', 'ワ', 'カ', 'ヨ', + 'タ', 'レ', 'ソ', 'ツ', 'ネ', 'ナ', 'ラ', 'ム', 'ウ', 'ヰ', 'ノ', 'オ', 'ク', 'ヤ', 'マ', + 'ケ', 'フ', 'コ', 'エ', 'テ', 'ア', 'サ', 'キ', 'ユ', 'メ', 'ミ', 'シ', 'ヱ', + 'ヒ', 'モ', 'セ', 'ス' +]; + +/// The generated content resolution traversal. +pub struct ResolveGeneratedContent<'a> { + /// The layout context. + layout_context: &'a LayoutContext<'a>, + /// The counter representing an ordered list item. + list_item: Counter, + /// Named CSS counters. + counters: HashMap<String,Counter>, + /// The level of quote nesting. + quote: u32, +} + +impl<'a> ResolveGeneratedContent<'a> { + /// Creates a new generated content resolution traversal. + pub fn new(layout_context: &'a LayoutContext<'a>) -> ResolveGeneratedContent<'a> { + ResolveGeneratedContent { + layout_context: layout_context, + list_item: Counter::new(), + counters: HashMap::new(), + quote: 0, + } + } +} + +impl<'a> InorderFlowTraversal for ResolveGeneratedContent<'a> { + #[inline] + fn process(&mut self, flow: &mut Flow, level: u32) { + let mut mutator = ResolveGeneratedContentFragmentMutator { + traversal: self, + level: level, + is_block: flow.is_block_like(), + incremented: false, + }; + flow.mutate_fragments(&mut |fragment| mutator.mutate_fragment(fragment)) + } + + #[inline] + fn should_process(&mut self, flow: &mut Flow) -> bool { + flow::base(flow).restyle_damage.intersects(RESOLVE_GENERATED_CONTENT) || + flow::base(flow).flags.intersects(AFFECTS_COUNTERS | HAS_COUNTER_AFFECTING_CHILDREN) + } +} + +/// The object that mutates the generated content fragments. +struct ResolveGeneratedContentFragmentMutator<'a,'b:'a> { + /// The traversal. + traversal: &'a mut ResolveGeneratedContent<'b>, + /// The level we're at in the flow tree. + level: u32, + /// Whether this flow is a block flow. + is_block: bool, + /// Whether we've incremented the counter yet. + incremented: bool, +} + +impl<'a,'b> ResolveGeneratedContentFragmentMutator<'a,'b> { + fn mutate_fragment(&mut self, fragment: &mut Fragment) { + // We only reset and/or increment counters once per flow. This avoids double-incrementing + // counters on list items (once for the main fragment and once for the marker). + if !self.incremented { + self.reset_and_increment_counters_as_necessary(fragment); + } + + let mut list_style_type = fragment.style().get_list().list_style_type; + if fragment.style().get_box().display != display::T::list_item { + list_style_type = list_style_type::T::none + } + + let mut new_info = None; + { + let info = + if let SpecificFragmentInfo::GeneratedContent(ref mut info) = fragment.specific { + info + } else { + return + }; + + match **info { + GeneratedContentInfo::ListItem => { + new_info = self.traversal.list_item.render(self.traversal.layout_context, + fragment.node, + fragment.style.clone(), + list_style_type, + RenderingMode::Suffix(".\u{00a0}")) + } + GeneratedContentInfo::ContentItem(ContentItem::String(_)) => { + // Nothing to do here. + } + GeneratedContentInfo::ContentItem(ContentItem::Counter(ref counter_name, + counter_style)) => { + let mut temporary_counter = Counter::new(); + let counter = self.traversal + .counters + .get(counter_name.as_slice()) + .unwrap_or(&mut temporary_counter); + new_info = counter.render(self.traversal.layout_context, + fragment.node, + fragment.style.clone(), + counter_style, + RenderingMode::Plain) + } + GeneratedContentInfo::ContentItem(ContentItem::Counters(ref counter_name, + ref separator, + counter_style)) => { + let mut temporary_counter = Counter::new(); + let counter = self.traversal + .counters + .get(counter_name.as_slice()) + .unwrap_or(&mut temporary_counter); + new_info = counter.render(self.traversal.layout_context, + fragment.node, + fragment.style.clone(), + counter_style, + RenderingMode::All(separator.as_slice())); + } + GeneratedContentInfo::ContentItem(ContentItem::OpenQuote) => { + new_info = Some(render_text(self.traversal.layout_context, + fragment.node, + fragment.style.clone(), + self.quote(&*fragment.style, false))); + self.traversal.quote += 1 + } + GeneratedContentInfo::ContentItem(ContentItem::CloseQuote) => { + if self.traversal.quote >= 1 { + self.traversal.quote -= 1 + } + + new_info = Some(render_text(self.traversal.layout_context, + fragment.node, + fragment.style.clone(), + self.quote(&*fragment.style, true))); + } + GeneratedContentInfo::ContentItem(ContentItem::NoOpenQuote) => { + self.traversal.quote += 1 + } + GeneratedContentInfo::ContentItem(ContentItem::NoCloseQuote) => { + if self.traversal.quote >= 1 { + self.traversal.quote -= 1 + } + } + } + }; + + if let Some(new_info) = new_info { + fragment.specific = new_info + } + } + + fn reset_and_increment_counters_as_necessary(&mut self, fragment: &mut Fragment) { + let mut list_style_type = fragment.style().get_list().list_style_type; + if !self.is_block || fragment.style().get_box().display != display::T::list_item { + list_style_type = list_style_type::T::none + } + + match list_style_type { + list_style_type::T::disc | list_style_type::T::none | list_style_type::T::circle | + list_style_type::T::square | list_style_type::T::disclosure_open | + list_style_type::T::disclosure_closed => {} + _ => self.traversal.list_item.increment(self.level, 1), + } + + // Truncate down counters. + for (_, counter) in self.traversal.counters.iter_mut() { + counter.truncate_to_level(self.level); + } + self.traversal.list_item.truncate_to_level(self.level); + + for &(ref counter_name, value) in fragment.style().get_counters().counter_reset.0.iter() { + if let Some(ref mut counter) = self.traversal.counters.get_mut(counter_name) { + counter.reset(self.level, value); + continue + } + + let mut counter = Counter::new(); + counter.reset(self.level, value); + self.traversal.counters.insert((*counter_name).clone(), counter); + } + + for &(ref counter_name, value) in fragment.style() + .get_counters() + .counter_increment + .0 + .iter() { + if let Some(ref mut counter) = self.traversal.counters.get_mut(counter_name) { + counter.increment(self.level, value); + continue + } + + let mut counter = Counter::new(); + counter.increment(self.level, value); + self.traversal.counters.insert((*counter_name).clone(), counter); + } + + self.incremented = true + } + + fn quote(&self, style: &ComputedValues, close: bool) -> String { + let quotes = &style.get_list().quotes; + debug_assert!(!quotes.0.is_empty()); + let &(ref open_quote, ref close_quote) = + if self.traversal.quote as uint >= quotes.0.len() { + quotes.0.last().unwrap() + } else { + "es.0[self.traversal.quote as uint] + }; + if close { + close_quote.to_string() + } else { + open_quote.to_string() + } + } +} + +/// A counter per CSS 2.1 § 12.4. +struct Counter { + /// The values at each level. + values: Vec<CounterValue>, +} + +impl Counter { + fn new() -> Counter { + Counter { + values: Vec::new(), + } + } + + fn reset(&mut self, level: u32, value: i32) { + // Do we have an instance of the counter at this level? If so, just mutate it. + if let Some(ref mut existing_value) = self.values.last_mut() { + if level == existing_value.level { + existing_value.value = value; + return + } + } + + // Otherwise, push a new instance of the counter. + self.values.push(CounterValue { + level: level, + value: value, + }) + } + + fn truncate_to_level(&mut self, level: u32) { + if let Some(position) = self.values.iter().position(|value| value.level > level) { + self.values.truncate(position) + } + } + + fn increment(&mut self, level: u32, amount: i32) { + if let Some(ref mut value) = self.values.last_mut() { + value.value += amount; + return + } + + self.values.push(CounterValue { + level: level, + value: amount, + }) + } + + fn render(&self, + layout_context: &LayoutContext, + node: OpaqueNode, + style: Arc<ComputedValues>, + list_style_type: list_style_type::T, + mode: RenderingMode) + -> Option<SpecificFragmentInfo> { + let mut string = String::new(); + match mode { + RenderingMode::Plain => { + let value = match self.values.last() { + Some(ref value) => value.value, + None => 0, + }; + push_representation(value, list_style_type, &mut string) + } + RenderingMode::Suffix(suffix) => { + let value = match self.values.last() { + Some(ref value) => value.value, + None => 0, + }; + push_representation(value, list_style_type, &mut string); + string.push_str(suffix) + } + RenderingMode::All(separator) => { + let mut first = true; + for value in self.values.iter() { + if !first { + string.push_str(separator) + } + first = false; + push_representation(value.value, list_style_type, &mut string) + } + } + } + + if string.is_empty() { + None + } else { + Some(render_text(layout_context, node, style, string)) + } + } +} + +/// How a counter value is to be rendered. +enum RenderingMode<'a> { + /// The innermost counter value is rendered with no extra decoration. + Plain, + /// The innermost counter value is rendered with the given string suffix. + Suffix(&'a str), + /// All values of the counter are rendered with the given separator string between them. + All(&'a str), +} + +/// The value of a counter at a given level. +struct CounterValue { + /// The level of the flow tree that this corresponds to. + level: u32, + /// The value of the counter at this level. + value: i32, +} + +/// Creates fragment info for a literal string. +fn render_text(layout_context: &LayoutContext, + node: OpaqueNode, + style: Arc<ComputedValues>, + string: String) + -> SpecificFragmentInfo { + let mut fragments = DList::new(); + let info = SpecificFragmentInfo::UnscannedText(UnscannedTextFragmentInfo::from_text(string)); + fragments.push_back(Fragment::from_opaque_node_and_style(node, + style, + incremental::rebuild_and_reflow(), + info)); + let fragments = TextRunScanner::new().scan_for_runs(layout_context.font_context(), fragments); + debug_assert!(fragments.len() == 1); + fragments.fragments.into_iter().next().unwrap().specific +} + +/// Appends string that represents the value rendered using the system appropriate for the given +/// `list-style-type` onto the given string. +fn push_representation(value: i32, list_style_type: list_style_type::T, accumulator: &mut String) { + match list_style_type { + list_style_type::T::none => {} + list_style_type::T::disc | + list_style_type::T::circle | + list_style_type::T::square | + list_style_type::T::disclosure_open | + list_style_type::T::disclosure_closed => { + accumulator.push(static_representation(list_style_type)) + } + list_style_type::T::decimal => push_numeric_representation(value, &DECIMAL, accumulator), + list_style_type::T::arabic_indic => { + push_numeric_representation(value, &ARABIC_INDIC, accumulator) + } + list_style_type::T::bengali => push_numeric_representation(value, &BENGALI, accumulator), + list_style_type::T::cambodian | list_style_type::T::khmer => { + push_numeric_representation(value, &CAMBODIAN, accumulator) + } + list_style_type::T::cjk_decimal => { + push_numeric_representation(value, &CJK_DECIMAL, accumulator) + } + list_style_type::T::devanagari => { + push_numeric_representation(value, &DEVANAGARI, accumulator) + } + list_style_type::T::gujarati => push_numeric_representation(value, &GUJARATI, accumulator), + list_style_type::T::gurmukhi => push_numeric_representation(value, &GURMUKHI, accumulator), + list_style_type::T::kannada => push_numeric_representation(value, &KANNADA, accumulator), + list_style_type::T::lao => push_numeric_representation(value, &LAO, accumulator), + list_style_type::T::malayalam => { + push_numeric_representation(value, &MALAYALAM, accumulator) + } + list_style_type::T::mongolian => { + push_numeric_representation(value, &MONGOLIAN, accumulator) + } + list_style_type::T::myanmar => push_numeric_representation(value, &MYANMAR, accumulator), + list_style_type::T::oriya => push_numeric_representation(value, &ORIYA, accumulator), + list_style_type::T::persian => push_numeric_representation(value, &PERSIAN, accumulator), + list_style_type::T::telugu => push_numeric_representation(value, &TELUGU, accumulator), + list_style_type::T::thai => push_numeric_representation(value, &THAI, accumulator), + list_style_type::T::tibetan => push_numeric_representation(value, &TIBETAN, accumulator), + list_style_type::T::lower_alpha => { + push_alphabetic_representation(value, &LOWER_ALPHA, accumulator) + } + list_style_type::T::upper_alpha => { + push_alphabetic_representation(value, &UPPER_ALPHA, accumulator) + } + list_style_type::T::cjk_earthly_branch => { + push_alphabetic_representation(value, &CJK_EARTHLY_BRANCH, accumulator) + } + list_style_type::T::cjk_heavenly_stem => { + push_alphabetic_representation(value, &CJK_HEAVENLY_STEM, accumulator) + } + list_style_type::T::lower_greek => { + push_alphabetic_representation(value, &LOWER_GREEK, accumulator) + } + list_style_type::T::hiragana => { + push_alphabetic_representation(value, &HIRAGANA, accumulator) + } + list_style_type::T::hiragana_iroha => { + push_alphabetic_representation(value, &HIRAGANA_IROHA, accumulator) + } + list_style_type::T::katakana => { + push_alphabetic_representation(value, &KATAKANA, accumulator) + } + list_style_type::T::katakana_iroha => { + push_alphabetic_representation(value, &KATAKANA_IROHA, accumulator) + } + } +} + +/// Returns the static character that represents the value rendered using the given list-style, if +/// possible. +pub fn static_representation(list_style_type: list_style_type::T) -> char { + match list_style_type { + list_style_type::T::disc => '•', + list_style_type::T::circle => '◦', + list_style_type::T::square => '▪', + list_style_type::T::disclosure_open => '▾', + list_style_type::T::disclosure_closed => '‣', + _ => panic!("No static representation for this list-style-type!"), + } +} + +/// Pushes the string that represents the value rendered using the given *alphabetic system* onto +/// the accumulator per CSS-COUNTER-STYLES § 3.1.4. +fn push_alphabetic_representation(mut value: i32, system: &[char], accumulator: &mut String) { + let mut string = SmallVec8::new(); + while value != 0 { + // Step 1. + value = value - 1; + // Step 2. + string.push(system[(value as uint) % system.len()]); + // Step 3. + value = ((value as uint) / system.len()) as i32; + } + + for i in range(0, string.len()).rev() { + accumulator.push(*string.get(i)) + } +} + +/// Pushes the string that represents the value rendered using the given *numeric system* onto the +/// accumulator per CSS-COUNTER-STYLES § 3.1.4. +fn push_numeric_representation(mut value: i32, system: &[char], accumulator: &mut String) { + // Step 1. + if value == 0 { + accumulator.push(system[0]); + return + } + + // Step 2. + let mut string = SmallVec8::new(); + while value != 0 { + // Step 2.1. + string.push(system[(value as uint) % system.len()]); + // Step 2.2. + value = ((value as uint) / system.len()) as i32; + } + + // Step 3. + for &ch in string.iter().rev() { + accumulator.push(ch) + } +} + diff --git a/components/layout/incremental.rs b/components/layout/incremental.rs index b163a8fbf29..9d989c7b65a 100644 --- a/components/layout/incremental.rs +++ b/components/layout/incremental.rs @@ -2,8 +2,7 @@ * 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 flow::{self, Flow}; -use flow::{IS_ABSOLUTELY_POSITIONED}; +use flow::{self, AFFECTS_COUNTERS, Flow, HAS_COUNTER_AFFECTING_CHILDREN, IS_ABSOLUTELY_POSITIONED}; use std::fmt; use std::sync::Arc; @@ -32,8 +31,12 @@ bitflags! { #[doc = "top-down."] const REFLOW = 0x08, + #[doc = "Re-resolve generated content. \ + Propagates up the flow tree because the computation is inorder."] + const RESOLVE_GENERATED_CONTENT = 0x10, + #[doc = "The entire flow needs to be reconstructed."] - const RECONSTRUCT_FLOW = 0x10 + const RECONSTRUCT_FLOW = 0x20 } } @@ -50,9 +53,9 @@ impl RestyleDamage { /// we should add to the *parent* of this flow. pub fn damage_for_parent(self, child_is_absolutely_positioned: bool) -> RestyleDamage { if child_is_absolutely_positioned { - self & (REPAINT | REFLOW_OUT_OF_FLOW) + self & (REPAINT | REFLOW_OUT_OF_FLOW | RESOLVE_GENERATED_CONTENT) } else { - self & (REPAINT | REFLOW | REFLOW_OUT_OF_FLOW) + self & (REPAINT | REFLOW | REFLOW_OUT_OF_FLOW | RESOLVE_GENERATED_CONTENT) } } @@ -91,10 +94,11 @@ impl fmt::Debug for RestyleDamage { let mut first_elem = true; let to_iter = - [ (REPAINT, "Repaint") - , (BUBBLE_ISIZES, "BubbleISizes") + [ (REPAINT, "Repaint") + , (BUBBLE_ISIZES, "BubbleISizes") , (REFLOW_OUT_OF_FLOW, "ReflowOutOfFlow") - , (REFLOW, "Reflow") + , (REFLOW, "Reflow") + , (RESOLVE_GENERATED_CONTENT, "ResolveGeneratedContent") , (RECONSTRUCT_FLOW, "ReconstructFlow") ]; @@ -126,10 +130,18 @@ macro_rules! add_if_not_equal( }) ); +/// Returns a bitmask that represents a flow that needs to be rebuilt and reflowed. +/// +/// Use this instead of `RestyleDamage::all()` because `RestyleDamage::all()` will result in +/// unnecessary sequential resolution of generated content. +pub fn rebuild_and_reflow() -> RestyleDamage { + REPAINT | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW | RECONSTRUCT_FLOW +} + pub fn compute_damage(old: &Option<Arc<ComputedValues>>, new: &ComputedValues) -> RestyleDamage { let old: &ComputedValues = match old.as_ref() { - None => return RestyleDamage::all(), + None => return rebuild_and_reflow(), Some(cv) => &**cv, }; @@ -186,6 +198,9 @@ impl<'a> LayoutDamageComputation for &'a mut (Flow + 'a) { let mut special_damage = SpecialRestyleDamage::empty(); let is_absolutely_positioned = flow::base(self).flags.contains(IS_ABSOLUTELY_POSITIONED); + // In addition to damage, we use this phase to compute whether nodes affect CSS counters. + let mut has_counter_affecting_children = false; + { let self_base = flow::mut_base(self); for kid in self_base.children.iter_mut() { @@ -199,21 +214,32 @@ impl<'a> LayoutDamageComputation for &'a mut (Flow + 'a) { self_base.restyle_damage .insert(flow::base(kid).restyle_damage.damage_for_parent( child_is_absolutely_positioned)); + + has_counter_affecting_children = has_counter_affecting_children || + flow::base(kid).flags.intersects(AFFECTS_COUNTERS | + HAS_COUNTER_AFFECTING_CHILDREN); + } } - let self_base = flow::base(self); + let self_base = flow::mut_base(self); if self_base.flags.float_kind() != float::T::none && self_base.restyle_damage.intersects(REFLOW) { special_damage.insert(REFLOW_ENTIRE_DOCUMENT); } + if has_counter_affecting_children { + self_base.flags.insert(HAS_COUNTER_AFFECTING_CHILDREN) + } else { + self_base.flags.remove(HAS_COUNTER_AFFECTING_CHILDREN) + } + special_damage } fn reflow_entire_document(self) { let self_base = flow::mut_base(self); - self_base.restyle_damage.insert(RestyleDamage::all()); + self_base.restyle_damage.insert(rebuild_and_reflow()); self_base.restyle_damage.remove(RECONSTRUCT_FLOW); for kid in self_base.children.iter_mut() { kid.reflow_entire_document(); diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 679a8eae608..5fb3ec9f180 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -12,9 +12,8 @@ use flow::{BaseFlow, FlowClass, Flow, MutableFlowUtils, ForceNonfloatedFlag}; use flow::{IS_ABSOLUTELY_POSITIONED}; use flow; use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, ScannedTextFragmentInfo}; -use fragment::{SpecificFragmentInfo}; -use fragment::SplitInfo; -use incremental::{REFLOW, REFLOW_OUT_OF_FLOW}; +use fragment::{SpecificFragmentInfo, SplitInfo}; +use incremental::{REFLOW, REFLOW_OUT_OF_FLOW, RESOLVE_GENERATED_CONTENT}; use layout_debug; use model::IntrinsicISizesContribution; use text; @@ -789,14 +788,22 @@ pub struct InlineFlow { impl InlineFlow { pub fn from_fragments(fragments: InlineFragments, writing_mode: WritingMode) -> InlineFlow { - InlineFlow { + let mut flow = InlineFlow { base: BaseFlow::new(None, writing_mode, ForceNonfloatedFlag::ForceNonfloated), fragments: fragments, lines: Vec::new(), minimum_block_size_above_baseline: Au(0), minimum_depth_below_baseline: Au(0), first_line_indentation: Au(0), + }; + + for fragment in flow.fragments.fragments.iter() { + if fragment.is_generated_content() { + flow.base.restyle_damage.insert(RESOLVE_GENERATED_CONTENT) + } } + + flow } /// Returns the distance from the baseline for the logical block-start inline-start corner of @@ -1399,6 +1406,12 @@ impl Flow for InlineFlow { .translate(stacking_context_position)) } } + + fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) { + for fragment in self.fragments.fragments.iter_mut() { + (*mutator)(fragment) + } + } } impl fmt::Debug for InlineFlow { diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index 793bc083723..3e80d90639b 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -840,6 +840,14 @@ impl LayoutTask { layout_debug::begin_trace(layout_root.clone()); } + // Resolve generated content. + profile(TimeProfilerCategory::LayoutGeneratedContent, + self.profiler_metadata(data), + self.time_profiler_chan.clone(), + || { + sequential::resolve_generated_content(&mut layout_root, &shared_layout_context) + }); + // Perform the primary layout passes over the flow tree to compute the locations of all // the boxes. profile(TimeProfilerCategory::LayoutMain, diff --git a/components/layout/lib.rs b/components/layout/lib.rs index 6eb177f6f94..00bd3896af5 100644 --- a/components/layout/lib.rs +++ b/components/layout/lib.rs @@ -67,7 +67,9 @@ pub mod flow; pub mod flow_list; pub mod flow_ref; pub mod fragment; +pub mod generated_content; pub mod layout_task; +pub mod incremental; pub mod inline; pub mod list_item; pub mod model; @@ -83,7 +85,6 @@ pub mod table_row; pub mod table_cell; pub mod text; pub mod traversal; -pub mod incremental; pub mod wrapper; pub mod css { diff --git a/components/layout/list_item.rs b/components/layout/list_item.rs index 84c713af944..e7c90257ccd 100644 --- a/components/layout/list_item.rs +++ b/components/layout/list_item.rs @@ -8,12 +8,13 @@ #![deny(unsafe_blocks)] use block::BlockFlow; -use construct::FlowConstructor; use context::LayoutContext; use display_list_builder::ListItemFlowDisplayListBuilding; use floats::FloatKind; use flow::{Flow, FlowClass}; -use fragment::{Fragment, FragmentBorderBoxIterator}; +use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, GeneratedContentInfo}; +use generated_content; +use incremental::RESOLVE_GENERATED_CONTENT; use wrapper::ThreadSafeLayoutNode; use geom::{Point2D, Rect}; @@ -36,19 +37,33 @@ pub struct ListItemFlow { } impl ListItemFlow { - pub fn from_node_marker_and_flotation(constructor: &mut FlowConstructor, - node: &ThreadSafeLayoutNode, - marker_fragment: Option<Fragment>, - flotation: Option<FloatKind>) - -> ListItemFlow { - ListItemFlow { + pub fn from_node_fragments_and_flotation(node: &ThreadSafeLayoutNode, + main_fragment: Fragment, + marker_fragment: Option<Fragment>, + flotation: Option<FloatKind>) + -> ListItemFlow { + let mut this = ListItemFlow { block_flow: if let Some(flotation) = flotation { - BlockFlow::float_from_node(constructor, node, flotation) + BlockFlow::float_from_node_and_fragment(node, main_fragment, flotation) } else { - BlockFlow::from_node(constructor, node) + BlockFlow::from_node_and_fragment(node, main_fragment) }, marker: marker_fragment, + }; + + if let Some(ref marker) = this.marker { + match marker.style().get_list().list_style_type { + list_style_type::T::disc | + list_style_type::T::none | + list_style_type::T::circle | + list_style_type::T::square | + list_style_type::T::disclosure_open | + list_style_type::T::disclosure_closed => {} + _ => this.block_flow.base.restyle_damage.insert(RESOLVE_GENERATED_CONTENT), + } } + + this } } @@ -134,24 +149,59 @@ impl Flow for ListItemFlow { fn iterate_through_fragment_border_boxes(&self, iterator: &mut FragmentBorderBoxIterator, stacking_context_position: &Point2D<Au>) { - self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) + self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position); + + if let Some(ref marker) = self.marker { + if iterator.should_process(marker) { + iterator.process( + marker, + &marker.stacking_relative_border_box(&self.block_flow + .base + .stacking_relative_position, + &self.block_flow + .base + .absolute_position_info + .relative_containing_block_size, + self.block_flow + .base + .absolute_position_info + .relative_containing_block_mode, + CoordinateSystem::Parent) + .translate(stacking_context_position)); + } + } + } + + fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) { + self.block_flow.mutate_fragments(mutator); + + if let Some(ref mut marker) = self.marker { + (*mutator)(marker) + } } } -/// Returns the static text to be used for the given value of the `list-style-type` property. -/// -/// TODO(pcwalton): Return either a string or a counter descriptor, once we support counters. -pub fn static_text_for_list_style_type(list_style_type: list_style_type::T) - -> Option<&'static str> { - // Just to keep things simple, use a nonbreaking space (Unicode 0xa0) to provide the marker - // separation. - match list_style_type { - list_style_type::T::none => None, - list_style_type::T::disc => Some("•\u{a0}"), - list_style_type::T::circle => Some("◦\u{a0}"), - list_style_type::T::square => Some("▪\u{a0}"), - list_style_type::T::disclosure_open => Some("▾\u{a0}"), - list_style_type::T::disclosure_closed => Some("‣\u{a0}"), +/// The kind of content that `list-style-type` results in. +pub enum ListStyleTypeContent { + None, + StaticText(char), + GeneratedContent(Box<GeneratedContentInfo>), +} + +impl ListStyleTypeContent { + /// Returns the content to be used for the given value of the `list-style-type` property. + pub fn from_list_style_type(list_style_type: list_style_type::T) -> ListStyleTypeContent { + // Just to keep things simple, use a nonbreaking space (Unicode 0xa0) to provide the marker + // separation. + match list_style_type { + list_style_type::T::none => ListStyleTypeContent::None, + list_style_type::T::disc | list_style_type::T::circle | list_style_type::T::square | + list_style_type::T::disclosure_open | list_style_type::T::disclosure_closed => { + let text = generated_content::static_representation(list_style_type); + ListStyleTypeContent::StaticText(text) + } + _ => ListStyleTypeContent::GeneratedContent(box GeneratedContentInfo::ListItem), + } } } diff --git a/components/layout/sequential.rs b/components/layout/sequential.rs index 7007fdfa427..3c4755c8e0c 100644 --- a/components/layout/sequential.rs +++ b/components/layout/sequential.rs @@ -5,10 +5,11 @@ //! Implements sequential traversals over the DOM and flow trees. use context::{LayoutContext, SharedLayoutContext}; -use flow::{self, Flow, ImmutableFlowUtils, MutableFlowUtils, PostorderFlowTraversal}; -use flow::{PreorderFlowTraversal}; +use flow::{self, Flow, ImmutableFlowUtils, InorderFlowTraversal, MutableFlowUtils}; +use flow::{PostorderFlowTraversal, PreorderFlowTraversal}; use flow_ref::FlowRef; use fragment::FragmentBorderBoxIterator; +use generated_content::ResolveGeneratedContent; use traversal::{BubbleISizes, RecalcStyleForNode, ConstructFlows}; use traversal::{AssignBSizesAndStoreOverflow, AssignISizes}; use traversal::{ComputeAbsolutePositions, BuildDisplayList}; @@ -39,6 +40,24 @@ pub fn traverse_dom_preorder(root: LayoutNode, doit(root, recalc_style, construct_flows); } +pub fn resolve_generated_content(root: &mut FlowRef, shared_layout_context: &SharedLayoutContext) { + fn doit(flow: &mut Flow, level: u32, traversal: &mut ResolveGeneratedContent) { + if !traversal.should_process(flow) { + return + } + + traversal.process(flow, level); + + for kid in flow::mut_base(flow).children.iter_mut() { + doit(kid, level + 1, traversal) + } + } + + let layout_context = LayoutContext::new(shared_layout_context); + let mut traversal = ResolveGeneratedContent::new(&layout_context); + doit(&mut **root, 0, &mut traversal) +} + pub fn traverse_flow_tree_preorder(root: &mut FlowRef, shared_layout_context: &SharedLayoutContext) { fn doit(flow: &mut Flow, diff --git a/components/layout/table.rs b/components/layout/table.rs index 9679938b2e7..a1fa55889ea 100644 --- a/components/layout/table.rs +++ b/components/layout/table.rs @@ -8,7 +8,6 @@ use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag}; use block::{ISizeConstraintInput, ISizeConstraintSolution}; -use construct::FlowConstructor; use context::LayoutContext; use floats::FloatKind; use flow::{self, Flow, FlowClass, IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS}; @@ -55,30 +54,12 @@ impl TableFlow { fragment: Fragment) -> TableFlow { let mut block_flow = BlockFlow::from_node_and_fragment(node, fragment); - let table_layout = if block_flow.fragment().style().get_table().table_layout == - table_layout::T::fixed { - TableLayout::Fixed - } else { - TableLayout::Auto - }; - TableFlow { - block_flow: block_flow, - column_intrinsic_inline_sizes: Vec::new(), - column_computed_inline_sizes: Vec::new(), - table_layout: table_layout - } - } - - pub fn from_node(constructor: &mut FlowConstructor, - node: &ThreadSafeLayoutNode) - -> TableFlow { - let mut block_flow = BlockFlow::from_node(constructor, node); - let table_layout = if block_flow.fragment().style().get_table().table_layout == - table_layout::T::fixed { - TableLayout::Fixed - } else { - TableLayout::Auto - }; + let table_layout = + if block_flow.fragment().style().get_table().table_layout == table_layout::T::fixed { + TableLayout::Fixed + } else { + TableLayout::Auto + }; TableFlow { block_flow: block_flow, column_intrinsic_inline_sizes: Vec::new(), @@ -87,17 +68,17 @@ impl TableFlow { } } - pub fn float_from_node(constructor: &mut FlowConstructor, - node: &ThreadSafeLayoutNode, - float_kind: FloatKind) - -> TableFlow { - let mut block_flow = BlockFlow::float_from_node(constructor, node, float_kind); - let table_layout = if block_flow.fragment().style().get_table().table_layout == - table_layout::T::fixed { - TableLayout::Fixed - } else { - TableLayout::Auto - }; + pub fn float_from_node_and_fragment(node: &ThreadSafeLayoutNode, + fragment: Fragment, + float_kind: FloatKind) + -> TableFlow { + let mut block_flow = BlockFlow::float_from_node_and_fragment(node, fragment, float_kind); + let table_layout = + if block_flow.fragment().style().get_table().table_layout == table_layout::T::fixed { + TableLayout::Fixed + } else { + TableLayout::Auto + }; TableFlow { block_flow: block_flow, column_intrinsic_inline_sizes: Vec::new(), @@ -396,6 +377,10 @@ impl Flow for TableFlow { stacking_context_position: &Point2D<Au>) { self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } + + fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) { + self.block_flow.mutate_fragments(mutator) + } } impl fmt::Debug for TableFlow { diff --git a/components/layout/table_caption.rs b/components/layout/table_caption.rs index ceb6bf7e3aa..b0b08e7cdec 100644 --- a/components/layout/table_caption.rs +++ b/components/layout/table_caption.rs @@ -7,10 +7,9 @@ #![deny(unsafe_blocks)] use block::BlockFlow; -use construct::FlowConstructor; use context::LayoutContext; use flow::{FlowClass, Flow}; -use fragment::FragmentBorderBoxIterator; +use fragment::{Fragment, FragmentBorderBoxIterator}; use wrapper::ThreadSafeLayoutNode; use geom::{Point2D, Rect}; @@ -26,11 +25,10 @@ pub struct TableCaptionFlow { } impl TableCaptionFlow { - pub fn from_node(constructor: &mut FlowConstructor, - node: &ThreadSafeLayoutNode) - -> TableCaptionFlow { + pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode, fragment: Fragment) + -> TableCaptionFlow { TableCaptionFlow { - block_flow: BlockFlow::from_node(constructor, node) + block_flow: BlockFlow::from_node_and_fragment(node, fragment) } } } @@ -96,6 +94,10 @@ impl Flow for TableCaptionFlow { stacking_context_position: &Point2D<Au>) { self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } + + fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) { + self.block_flow.mutate_fragments(mutator) + } } impl fmt::Debug for TableCaptionFlow { diff --git a/components/layout/table_cell.rs b/components/layout/table_cell.rs index f018e72c2c3..1c80c2a8f2c 100644 --- a/components/layout/table_cell.rs +++ b/components/layout/table_cell.rs @@ -178,6 +178,10 @@ impl Flow for TableCellFlow { stacking_context_position: &Point2D<Au>) { self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } + + fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) { + self.block_flow.mutate_fragments(mutator) + } } impl fmt::Debug for TableCellFlow { diff --git a/components/layout/table_colgroup.rs b/components/layout/table_colgroup.rs index f0ad4cff9ca..61008cd2506 100644 --- a/components/layout/table_colgroup.rs +++ b/components/layout/table_colgroup.rs @@ -104,6 +104,8 @@ impl Flow for TableColGroupFlow { fn iterate_through_fragment_border_boxes(&self, _: &mut FragmentBorderBoxIterator, _: &Point2D<Au>) {} + + fn mutate_fragments(&mut self, _: &mut FnMut(&mut Fragment)) {} } impl fmt::Debug for TableColGroupFlow { diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs index 3f07e401afb..1e45ba48fc1 100644 --- a/components/layout/table_row.rs +++ b/components/layout/table_row.rs @@ -8,10 +8,8 @@ use block::BlockFlow; use block::ISizeAndMarginsComputer; -use construct::FlowConstructor; use context::LayoutContext; -use flow::{FlowClass, Flow, ImmutableFlowUtils}; -use flow; +use flow::{self, FlowClass, Flow, ImmutableFlowUtils}; use fragment::{Fragment, FragmentBorderBoxIterator}; use layout_debug; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable}; @@ -49,8 +47,7 @@ pub struct CellIntrinsicInlineSize { } impl TableRowFlow { - pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode, - fragment: Fragment) + pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode, fragment: Fragment) -> TableRowFlow { TableRowFlow { block_flow: BlockFlow::from_node_and_fragment(node, fragment), @@ -59,16 +56,6 @@ impl TableRowFlow { } } - pub fn from_node(constructor: &mut FlowConstructor, - node: &ThreadSafeLayoutNode) - -> TableRowFlow { - TableRowFlow { - block_flow: BlockFlow::from_node(constructor, node), - cell_intrinsic_inline_sizes: Vec::new(), - column_computed_inline_sizes: Vec::new() - } - } - pub fn fragment<'a>(&'a mut self) -> &'a Fragment { &self.block_flow.fragment } @@ -331,6 +318,10 @@ impl Flow for TableRowFlow { stacking_context_position: &Point2D<Au>) { self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } + + fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) { + self.block_flow.mutate_fragments(mutator) + } } impl fmt::Debug for TableRowFlow { diff --git a/components/layout/table_rowgroup.rs b/components/layout/table_rowgroup.rs index 1596e1bfab0..6ea1c4aaa18 100644 --- a/components/layout/table_rowgroup.rs +++ b/components/layout/table_rowgroup.rs @@ -7,7 +7,6 @@ #![deny(unsafe_blocks)] use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag}; -use construct::FlowConstructor; use context::LayoutContext; use flow::{FlowClass, Flow}; use fragment::{Fragment, FragmentBorderBoxIterator}; @@ -45,15 +44,6 @@ impl TableRowGroupFlow { } } - pub fn from_node(constructor: &mut FlowConstructor, node: &ThreadSafeLayoutNode) - -> TableRowGroupFlow { - TableRowGroupFlow { - block_flow: BlockFlow::from_node(constructor, node), - column_intrinsic_inline_sizes: Vec::new(), - column_computed_inline_sizes: Vec::new(), - } - } - pub fn fragment<'a>(&'a mut self) -> &'a Fragment { &self.block_flow.fragment } @@ -165,6 +155,10 @@ impl Flow for TableRowGroupFlow { stacking_context_position: &Point2D<Au>) { self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } + + fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) { + self.block_flow.mutate_fragments(mutator) + } } impl fmt::Debug for TableRowGroupFlow { diff --git a/components/layout/table_wrapper.rs b/components/layout/table_wrapper.rs index 0d2b6267806..042c08b5ed3 100644 --- a/components/layout/table_wrapper.rs +++ b/components/layout/table_wrapper.rs @@ -13,8 +13,8 @@ #![deny(unsafe_blocks)] -use block::{BlockFlow, BlockNonReplaced, FloatNonReplaced, ISizeAndMarginsComputer, MarginsMayCollapseFlag}; -use construct::FlowConstructor; +use block::{BlockFlow, BlockNonReplaced, FloatNonReplaced, ISizeAndMarginsComputer}; +use block::{MarginsMayCollapseFlag}; use context::LayoutContext; use floats::FloatKind; use flow::{FlowClass, Flow, ImmutableFlowUtils}; @@ -70,23 +70,6 @@ impl TableWrapperFlow { } } - pub fn from_node(constructor: &mut FlowConstructor, - node: &ThreadSafeLayoutNode) - -> TableWrapperFlow { - let mut block_flow = BlockFlow::from_node(constructor, node); - let table_layout = if block_flow.fragment().style().get_table().table_layout == - table_layout::T::fixed { - TableLayout::Fixed - } else { - TableLayout::Auto - }; - TableWrapperFlow { - block_flow: block_flow, - column_intrinsic_inline_sizes: vec!(), - table_layout: table_layout - } - } - pub fn float_from_node_and_fragment(node: &ThreadSafeLayoutNode, fragment: Fragment, float_kind: FloatKind) @@ -383,6 +366,10 @@ impl Flow for TableWrapperFlow { stacking_context_position: &Point2D<Au>) { self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } + + fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) { + self.block_flow.mutate_fragments(mutator) + } } impl fmt::Debug for TableWrapperFlow { diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index a31f6c9d3b6..228fdd6e7f0 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -13,7 +13,7 @@ use context::LayoutContext; use flow::{Flow, MutableFlowUtils}; use flow::{PreorderFlowTraversal, PostorderFlowTraversal}; use flow; -use incremental::{RestyleDamage, BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW}; +use incremental::{self, BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, RestyleDamage}; use wrapper::{layout_node_to_unsafe_layout_node, LayoutNode}; use wrapper::{PostorderNodeMutTraversal, ThreadSafeLayoutNode, UnsafeLayoutNode}; use wrapper::{PreorderDomTraversal, PostorderDomTraversal}; @@ -171,7 +171,8 @@ impl<'a> PreorderDomTraversal for RecalcStyleForNode<'a> { &mut applicable_declarations, &mut shareable); } else { - ThreadSafeLayoutNode::new(&node).set_restyle_damage(RestyleDamage::all()) + ThreadSafeLayoutNode::new(&node).set_restyle_damage( + incremental::rebuild_and_reflow()) } // Perform the CSS cascade. @@ -377,3 +378,4 @@ impl<'a> PostorderFlowTraversal for BuildDisplayList<'a> { flow.build_display_list(self.layout_context); } } + diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs index 74b3d20ca1c..bc89f74ea1b 100644 --- a/components/layout/wrapper.rs +++ b/components/layout/wrapper.rs @@ -67,9 +67,11 @@ use std::marker::ContravariantLifetime; use std::mem; use std::sync::mpsc::Sender; use string_cache::{Atom, Namespace}; +use style::computed_values::content::ContentItem; use style::computed_values::{content, display, white_space}; use selectors::parser::{NamespaceConstraint, AttrSelector}; -use style::legacy::{LengthAttribute, SimpleColorAttribute, UnsignedIntegerAttribute, IntegerAttribute}; +use style::legacy::{IntegerAttribute, LengthAttribute, SimpleColorAttribute}; +use style::legacy::{UnsignedIntegerAttribute}; use style::node::{TElement, TElementAttributes, TNode}; use style::properties::PropertyDeclarationBlock; use url::Url; @@ -154,10 +156,11 @@ pub trait TLayoutNode { } } - /// If this is a text node, copies out the text. If this is not a text node, fails. + /// If this is a text node or generated content, copies out its content. If this is not a text + /// node, fails. /// - /// FIXME(pcwalton): Don't copy text. Atomically reference count instead. - fn text(&self) -> String; + /// FIXME(pcwalton): This might have too much copying and/or allocation. Profile this. + fn text_content(&self) -> Vec<ContentItem>; /// Returns the first child of this node. fn first_child(&self) -> Option<Self>; @@ -214,19 +217,25 @@ impl<'ln> TLayoutNode for LayoutNode<'ln> { } } - fn text(&self) -> String { + fn text_content(&self) -> Vec<ContentItem> { unsafe { let text: Option<LayoutJS<Text>> = TextCast::to_layout_js(self.get_jsmanaged()); if let Some(text) = text { - return (*text.unsafe_get()).characterdata().data_for_layout().to_owned(); + return vec![ + ContentItem::String((*text.unsafe_get()).characterdata() + .data_for_layout() + .to_owned()) + ]; } - let input: Option<LayoutJS<HTMLInputElement>> = HTMLInputElementCast::to_layout_js(self.get_jsmanaged()); + let input: Option<LayoutJS<HTMLInputElement>> = + HTMLInputElementCast::to_layout_js(self.get_jsmanaged()); if let Some(input) = input { - return input.get_value_for_layout(); + return vec![ContentItem::String(input.get_value_for_layout())]; } - let area: Option<LayoutJS<HTMLTextAreaElement>> = HTMLTextAreaElementCast::to_layout_js(self.get_jsmanaged()); + let area: Option<LayoutJS<HTMLTextAreaElement>> = + HTMLTextAreaElementCast::to_layout_js(self.get_jsmanaged()); if let Some(area) = area { - return area.get_value_for_layout(); + return vec![ContentItem::String(area.get_value_for_layout())]; } panic!("not text!") @@ -661,16 +670,10 @@ impl<'le> TElementAttributes for LayoutElement<'le> { } } -fn get_content(content_list: &content::T) -> String { +fn get_content(content_list: &content::T) -> Vec<ContentItem> { match *content_list { - content::T::Content(ref value) => { - let iter = &mut value.clone().into_iter().peekable(); - match iter.next() { - Some(content::ContentItem::StringContent(content)) => content, - _ => "".to_owned(), - } - } - _ => "".to_owned(), + content::T::Content(ref value) if !value.is_empty() => (*value).clone(), + _ => vec![], } } @@ -762,7 +765,7 @@ impl<'ln> TLayoutNode for ThreadSafeLayoutNode<'ln> { } } - fn text(&self) -> String { + fn text_content(&self) -> Vec<ContentItem> { if self.pseudo != PseudoElementType::Normal { let layout_data_ref = self.borrow_layout_data(); let node_layout_data_wrapper = layout_data_ref.as_ref().unwrap(); @@ -775,7 +778,7 @@ impl<'ln> TLayoutNode for ThreadSafeLayoutNode<'ln> { return get_content(&after_style.get_box().content) } } - self.node.text() + self.node.text_content() } } diff --git a/components/script/dom/webidls/CSSStyleDeclaration.webidl b/components/script/dom/webidls/CSSStyleDeclaration.webidl index 46ca8424b70..bde48aaaff3 100644 --- a/components/script/dom/webidls/CSSStyleDeclaration.webidl +++ b/components/script/dom/webidls/CSSStyleDeclaration.webidl @@ -103,6 +103,11 @@ partial interface CSSStyleDeclaration { [TreatNullAs=EmptyString] attribute DOMString listStyleType; [TreatNullAs=EmptyString] attribute DOMString listStyleImage; + [TreatNullAs=EmptyString] attribute DOMString quotes; + + [TreatNullAs=EmptyString] attribute DOMString counterIncrement; + [TreatNullAs=EmptyString] attribute DOMString counterReset; + [TreatNullAs=EmptyString] attribute DOMString overflow; [TreatNullAs=EmptyString] attribute DOMString overflowX; [TreatNullAs=EmptyString] attribute DOMString overflowY; diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 315c0ce897e..5363ee2a931 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -763,7 +763,7 @@ dependencies = [ [[package]] name = "selectors" version = "0.1.0" -source = "git+https://github.com/servo/rust-selectors#1e5bbf16eee3adeec1e911c7eaa8f2be02cd4c67" +source = "git+https://github.com/servo/rust-selectors#2a492d522ef596a05c3677bb74bf8a5b73d01855" dependencies = [ "bitflags 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cssparser 0.2.0 (git+https://github.com/servo/rust-cssparser)", diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs index 5e57d9a68ad..7d3b58916e6 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -759,86 +759,166 @@ pub mod longhands { ${switch_to_style_struct("Box")} <%self:longhand name="content"> - pub use self::computed_value::T as SpecifiedValue; - pub use self::computed_value::ContentItem; - use cssparser::Token; - use values::computed::ComputedValueAsSpecified; + use cssparser::Token; + use std::ascii::AsciiExt; + use values::computed::ComputedValueAsSpecified; - impl ComputedValueAsSpecified for SpecifiedValue {} + use super::list_style_type; - pub mod computed_value { - use std::borrow::IntoCow; - use cssparser::{ToCss, Token}; - use text_writer::{self, TextWriter}; + pub use self::computed_value::T as SpecifiedValue; + pub use self::computed_value::ContentItem; - #[derive(PartialEq, Eq, Clone)] - pub enum ContentItem { - StringContent(String), - } + impl ComputedValueAsSpecified for SpecifiedValue {} - impl ToCss for ContentItem { - fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { - match self { - &ContentItem::StringContent(ref s) => { - Token::QuotedString((&**s).into_cow()).to_css(dest) - } + pub mod computed_value { + use super::super::list_style_type; + + use cssparser::{self, ToCss}; + use text_writer::{self, TextWriter}; + + #[derive(PartialEq, Eq, Clone)] + pub enum ContentItem { + /// Literal string content. + String(String), + /// `counter(name, style)`. + Counter(String, list_style_type::computed_value::T), + /// `counters(name, separator, style)`. + Counters(String, String, list_style_type::computed_value::T), + /// `open-quote`. + OpenQuote, + /// `close-quote`. + CloseQuote, + /// `no-open-quote`. + NoOpenQuote, + /// `no-close-quote`. + NoCloseQuote, + } + + impl ToCss for ContentItem { + fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { + match self { + &ContentItem::String(ref s) => { + cssparser::serialize_string(&**s, dest) } + &ContentItem::Counter(ref s, ref list_style_type) => { + try!(dest.write_str("counter(")); + try!(cssparser::serialize_identifier(&**s, dest)); + try!(dest.write_str(", ")); + try!(list_style_type.to_css(dest)); + dest.write_str(")") + } + &ContentItem::Counters(ref s, ref separator, ref list_style_type) => { + try!(dest.write_str("counter(")); + try!(cssparser::serialize_identifier(&**s, dest)); + try!(dest.write_str(", ")); + try!(cssparser::serialize_string(&**separator, dest)); + try!(dest.write_str(", ")); + try!(list_style_type.to_css(dest)); + dest.write_str(")") + } + &ContentItem::OpenQuote => dest.write_str("open-quote"), + &ContentItem::CloseQuote => dest.write_str("close-quote"), + &ContentItem::NoOpenQuote => dest.write_str("no-open-quote"), + &ContentItem::NoCloseQuote => dest.write_str("no-close-quote"), } } + } - #[allow(non_camel_case_types)] - #[derive(PartialEq, Eq, Clone)] - pub enum T { - normal, - none, - Content(Vec<ContentItem>), - } - - impl ToCss for T { - fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { - match self { - &T::normal => dest.write_str("normal"), - &T::none => dest.write_str("none"), - &T::Content(ref content) => { - let mut iter = content.iter(); - try!(iter.next().unwrap().to_css(dest)); - for c in iter { - try!(c.to_css(dest)); - } - Ok(()) + #[allow(non_camel_case_types)] + #[derive(PartialEq, Eq, Clone)] + pub enum T { + normal, + none, + Content(Vec<ContentItem>), + } + + impl ToCss for T { + fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { + match self { + &T::normal => dest.write_str("normal"), + &T::none => dest.write_str("none"), + &T::Content(ref content) => { + let mut iter = content.iter(); + try!(iter.next().unwrap().to_css(dest)); + for c in iter { + try!(dest.write_str(" ")); + try!(c.to_css(dest)); } + Ok(()) } } } } - #[inline] - pub fn get_initial_value() -> computed_value::T { - computed_value::T::normal - } + } + #[inline] + pub fn get_initial_value() -> computed_value::T { + computed_value::T::normal + } - // normal | none | [ <string> ]+ - // TODO: <uri>, <counter>, attr(<identifier>), open-quote, close-quote, no-open-quote, no-close-quote - pub fn parse(_context: &ParserContext, input: &mut Parser) - -> Result<SpecifiedValue, ()> { - if input.try(|input| input.expect_ident_matching("normal")).is_ok() { - return Ok(SpecifiedValue::normal) - } - if input.try(|input| input.expect_ident_matching("none")).is_ok() { - return Ok(SpecifiedValue::none) - } - let mut content = vec![]; - loop { - match input.next() { - Ok(Token::QuotedString(value)) => { - content.push(ContentItem::StringContent(value.into_owned())) - } - Err(()) if !content.is_empty() => { - return Ok(SpecifiedValue::Content(content)) + pub fn counter_name_is_illegal(name: &str) -> bool { + name.eq_ignore_ascii_case("none") || name.eq_ignore_ascii_case("inherit") || + name.eq_ignore_ascii_case("initial") + } + + // normal | none | [ <string> | <counter> | open-quote | close-quote | no-open-quote | + // no-close-quote ]+ + // TODO: <uri>, attr(<identifier>) + pub fn parse(context: &ParserContext, input: &mut Parser) + -> Result<SpecifiedValue, ()> { + if input.try(|input| input.expect_ident_matching("normal")).is_ok() { + return Ok(SpecifiedValue::normal) + } + if input.try(|input| input.expect_ident_matching("none")).is_ok() { + return Ok(SpecifiedValue::none) + } + let mut content = vec![]; + loop { + match input.next() { + Ok(Token::QuotedString(value)) => { + content.push(ContentItem::String(value.into_owned())) + } + Ok(Token::Function(name)) => { + content.push(try!(match_ignore_ascii_case! { name, + "counter" => input.parse_nested_block(|input| { + let name = try!(input.expect_ident()).into_owned(); + let style = input.try(|input| { + try!(input.expect_comma()); + list_style_type::parse(context, input) + }).unwrap_or(list_style_type::computed_value::T::decimal); + Ok(ContentItem::Counter(name, style)) + }), + "counters" => input.parse_nested_block(|input| { + let name = try!(input.expect_ident()).into_owned(); + try!(input.expect_comma()); + let separator = try!(input.expect_string()).into_owned(); + let style = input.try(|input| { + try!(input.expect_comma()); + list_style_type::parse(context, input) + }).unwrap_or(list_style_type::computed_value::T::decimal); + Ok(ContentItem::Counters(name, separator, style)) + }) + _ => return Err(()) + })); + } + Ok(Token::Ident(ident)) => { + match_ignore_ascii_case! { ident, + "open-quote" => content.push(ContentItem::OpenQuote), + "close-quote" => content.push(ContentItem::CloseQuote), + "no-open-quote" => content.push(ContentItem::NoOpenQuote), + "no-close-quote" => content.push(ContentItem::NoCloseQuote) + _ => return Err(()) } - _ => return Err(()) } + Err(_) => break, + _ => return Err(()) } } + if !content.is_empty() { + Ok(SpecifiedValue::Content(content)) + } else { + Err(()) + } + } </%self:longhand> ${new_style_struct("List", is_inherited=True)} @@ -847,14 +927,16 @@ pub mod longhands { // TODO(pcwalton): Implement the full set of counter styles per CSS-COUNTER-STYLES [1] 6.1: // - // decimal, decimal-leading-zero, arabic-indic, armenian, upper-armenian, lower-armenian, - // bengali, cambodian, khmer, cjk-decimal, devanagiri, georgian, gujarati, gurmukhi, - // hebrew, kannada, lao, malayalam, mongolian, myanmar, oriya, persian, lower-roman, - // upper-roman, telugu, thai, tibetan + // decimal-leading-zero, armenian, upper-armenian, lower-armenian, georgian, lower-roman, + // upper-roman // // [1]: http://dev.w3.org/csswg/css-counter-styles/ - ${single_keyword("list-style-type", - "disc none circle square disclosure-open disclosure-closed")} + ${single_keyword("list-style-type", """ + disc none circle square decimal arabic-indic bengali cambodian cjk-decimal devanagari + gujarati gurmukhi kannada khmer lao malayalam mongolian myanmar oriya persian telugu thai + tibetan lower-alpha upper-alpha cjk-earthly-branch cjk-heavenly-stem lower-greek hiragana + hiragana-iroha katakana katakana-iroha disclosure-open disclosure-closed + """)} <%self:longhand name="list-style-image"> use std::borrow::IntoCow; @@ -910,6 +992,143 @@ pub mod longhands { } </%self:longhand> + <%self:longhand name="quotes"> + use text_writer::{self, TextWriter}; + use values::computed::ComputedValueAsSpecified; + + use cssparser::{ToCss, Token}; + use std::borrow::IntoCow; + + pub use self::computed_value::T as SpecifiedValue; + + pub mod computed_value { + #[derive(Clone, PartialEq)] + pub struct T(pub Vec<(String,String)>); + } + + impl ComputedValueAsSpecified for SpecifiedValue {} + + impl ToCss for SpecifiedValue { + fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { + let mut first = true; + for pair in self.0.iter() { + if !first { + try!(dest.write_str(" ")); + } + first = false; + try!(Token::QuotedString(pair.0.as_slice().into_cow()).to_css(dest)); + try!(dest.write_str(" ")); + try!(Token::QuotedString(pair.1.as_slice().into_cow()).to_css(dest)); + } + Ok(()) + } + } + + #[inline] + pub fn get_initial_value() -> computed_value::T { + computed_value::T(vec![ + ("\u{201c}".to_string(), "\u{201d}".to_string()), + ("\u{2018}".to_string(), "\u{2019}".to_string()), + ]) + } + + pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> { + if input.try(|input| input.expect_ident_matching("none")).is_ok() { + return Ok(SpecifiedValue(Vec::new())) + } + + let mut quotes = Vec::new(); + loop { + let first = match input.next() { + Ok(Token::QuotedString(value)) => value.into_owned(), + Ok(_) => return Err(()), + Err(()) => break, + }; + let second = match input.next() { + Ok(Token::QuotedString(value)) => value.into_owned(), + _ => return Err(()), + }; + quotes.push((first, second)) + } + if !quotes.is_empty() { + Ok(SpecifiedValue(quotes)) + } else { + Err(()) + } + } + </%self:longhand> + + ${new_style_struct("Counters", is_inherited=False)} + + <%self:longhand name="counter-increment"> + use super::content; + use text_writer::{self, TextWriter}; + use values::computed::ComputedValueAsSpecified; + + use cssparser::{ToCss, Token}; + use std::borrow::{IntoCow, ToOwned}; + + pub use self::computed_value::T as SpecifiedValue; + + pub mod computed_value { + #[derive(Clone, PartialEq)] + pub struct T(pub Vec<(String,i32)>); + } + + #[inline] + pub fn get_initial_value() -> computed_value::T { + computed_value::T(Vec::new()) + } + + impl ComputedValueAsSpecified for SpecifiedValue {} + + impl ToCss for SpecifiedValue { + fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { + let mut first = true; + for pair in self.0.iter() { + if !first { + try!(dest.write_str(" ")); + } + first = false; + try!(Token::QuotedString(pair.0.as_slice().into_cow()).to_css(dest)); + try!(dest.write_str(format!(" {}", pair.1).as_slice())); + } + Ok(()) + } + } + + pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> { + if input.try(|input| input.expect_ident_matching("none")).is_ok() { + return Ok(SpecifiedValue(Vec::new())) + } + + let mut counters = Vec::new(); + loop { + let counter_name = match input.next() { + Ok(Token::Ident(ident)) => (*ident).to_owned(), + Ok(_) => return Err(()), + Err(_) => break, + }; + if content::counter_name_is_illegal(counter_name.as_slice()) { + return Err(()) + } + let counter_delta = input.try(|input| input.expect_integer()).unwrap_or(1) as i32; + counters.push((counter_name, counter_delta)) + } + + if !counters.is_empty() { + Ok(SpecifiedValue(counters)) + } else { + Err(()) + } + } + </%self:longhand> + + <%self:longhand name="counter-reset"> + pub use super::counter_increment::{SpecifiedValue, computed_value, get_initial_value}; + pub use super::counter_increment::{parse}; + </%self:longhand> + // CSS 2.1, Section 13 - Paged media // CSS 2.1, Section 14 - Colors and Backgrounds diff --git a/components/util/time.rs b/components/util/time.rs index 46de5abbceb..c9cf32c0617 100644 --- a/components/util/time.rs +++ b/components/util/time.rs @@ -81,6 +81,7 @@ pub enum TimeProfilerCategory { LayoutSelectorMatch, LayoutTreeBuilder, LayoutDamagePropagate, + LayoutGeneratedContent, LayoutMain, LayoutParallelWarmup, LayoutShaping, @@ -99,6 +100,7 @@ impl Formatable for TimeProfilerCategory { TimeProfilerCategory::LayoutStyleRecalc | TimeProfilerCategory::LayoutRestyleDamagePropagation | TimeProfilerCategory::LayoutNonIncrementalReset | + TimeProfilerCategory::LayoutGeneratedContent | TimeProfilerCategory::LayoutMain | TimeProfilerCategory::LayoutDispListBuild | TimeProfilerCategory::LayoutShaping | @@ -119,6 +121,7 @@ impl Formatable for TimeProfilerCategory { TimeProfilerCategory::LayoutSelectorMatch => "Selector Matching", TimeProfilerCategory::LayoutTreeBuilder => "Tree Building", TimeProfilerCategory::LayoutDamagePropagate => "Damage Propagation", + TimeProfilerCategory::LayoutGeneratedContent => "Generated Content Resolution", TimeProfilerCategory::LayoutMain => "Primary Layout Pass", TimeProfilerCategory::LayoutParallelWarmup => "Parallel Warmup", TimeProfilerCategory::LayoutShaping => "Shaping", diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index 8ae649f776f..8504a68c52c 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -771,7 +771,7 @@ dependencies = [ [[package]] name = "selectors" version = "0.1.0" -source = "git+https://github.com/servo/rust-selectors#1e5bbf16eee3adeec1e911c7eaa8f2be02cd4c67" +source = "git+https://github.com/servo/rust-selectors#2a492d522ef596a05c3677bb74bf8a5b73d01855" dependencies = [ "bitflags 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cssparser 0.2.0 (git+https://github.com/servo/rust-cssparser)", diff --git a/ports/gonk/Cargo.lock b/ports/gonk/Cargo.lock index 12dd2979ee7..981037eb606 100644 --- a/ports/gonk/Cargo.lock +++ b/ports/gonk/Cargo.lock @@ -682,7 +682,7 @@ dependencies = [ [[package]] name = "selectors" version = "0.1.0" -source = "git+https://github.com/servo/rust-selectors#1e5bbf16eee3adeec1e911c7eaa8f2be02cd4c67" +source = "git+https://github.com/servo/rust-selectors#2a492d522ef596a05c3677bb74bf8a5b73d01855" dependencies = [ "bitflags 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cssparser 0.2.0 (git+https://github.com/servo/rust-cssparser)", diff --git a/tests/ref/basic.list b/tests/ref/basic.list index d26f5656428..4d26321575e 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -65,6 +65,8 @@ flaky_cpu == append_style_a.html append_style_b.html == case-insensitive-font-family.html case-insensitive-font-family-ref.html == clear_generated_content_table_a.html clear_generated_content_table_ref.html == clip_a.html clip_ref.html +== counters_nested_a.html counters_nested_ref.html +== counters_simple_a.html counters_simple_ref.html == data_img_a.html data_img_b.html == empty_cells_a.html empty_cells_ref.html == filter_opacity_a.html filter_opacity_ref.html @@ -177,6 +179,9 @@ flaky_cpu == linebreak_simple_a.html linebreak_simple_b.html == nth_last_of_type_pseudo_a.html nth_last_of_type_pseudo_b.html == nth_of_type_pseudo_a.html nth_of_type_pseudo_b.html == object_element_a.html object_element_b.html +== ol_japanese_iroha_a.html ol_japanese_iroha_ref.html +!= ol_japanese_iroha_bullet_styles.html ol_japanese_iroha_ref.html +== ol_simple_a.html ol_simple_ref.html == only_child_pseudo_a.html only_child_pseudo_b.html == only_of_type_pseudo_a.html only_of_type_pseudo_b.html == opacity_simple_a.html opacity_simple_ref.html @@ -220,6 +225,7 @@ experimental != overconstrained_block.html overconstrained_block_ref.html == pre_ignorable_whitespace_a.html pre_ignorable_whitespace_ref.html == pseudo_element_a.html pseudo_element_b.html == pseudo_inherit.html pseudo_inherit_ref.html +== quotes_simple_a.html quotes_simple_ref.html == root_height_a.html root_height_b.html == root_margin_collapse_a.html root_margin_collapse_b.html == root_pseudo_a.html root_pseudo_b.html diff --git a/tests/ref/counters_nested_a.html b/tests/ref/counters_nested_a.html new file mode 100644 index 00000000000..39baa0ac16b --- /dev/null +++ b/tests/ref/counters_nested_a.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<html> +<head> +<!-- Tests that `counters` works with nested counters. --> +<style> +section { + counter-reset: section 0; +} +h1, h2, h3 { + counter-increment: section 1; + font-size: 24px; +} +h1:before, h2:before, h3:before { + content: counters(section, ".") ". "; +} +</style> +</head> +<body> +<section> + <h1>Foo</h1> + <section> + <h2>Boo</h2> + <h2>Quux</h2> + <section> + <h3>Blah</h3> + </section> + </section> + <h1>Bar</h1> + <section></section> + <h2>Boo</h2> + <h2>Quux</h2> + <h1>Baz</h1> +</section> +</body> +</html> + diff --git a/tests/ref/counters_nested_ref.html b/tests/ref/counters_nested_ref.html new file mode 100644 index 00000000000..b42f6509c83 --- /dev/null +++ b/tests/ref/counters_nested_ref.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html> +<head> +<!-- Tests that `counters` works with nested counters. --> +<style> +h1, h2, h3 { + font-size: 24px; +} +</style> +</head> +<body> +<section> + <h1>1. Foo</h1> + <section> + <h2>1.1. Boo</h2> + <h2>1.2. Quux</h2> + <section> + <h3>1.2.1. Blah</h3> + </section> + </section> + <h1>1.3. Bar</h1> + <section></section> + <h2>1.1. Boo</h2> + <h2>1.2. Quux</h2> + <h1>1.3. Baz</h1> +</section> +</body> +</html> + diff --git a/tests/ref/counters_simple_a.html b/tests/ref/counters_simple_a.html new file mode 100644 index 00000000000..e8534e3da17 --- /dev/null +++ b/tests/ref/counters_simple_a.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<html> +<head> +<!-- Tests that `counter` works. --> +<style> +h1, h2, h3 { + font-size: 24px; +} +h1 { + counter-increment: section 1; + counter-reset: subsection 0; +} +h1:before { + content: counter(section) ". "; +} +h2 { + counter-increment: subsection 1; + counter-reset: subsubsection 0; +} +h2:before { + content: counter(section) "." counter(subsection) ". "; +} +h3 { + counter-increment: subsubsection; +} +h3:before { + content: counter(section) "." counter(subsection) "." counter(subsubsection) ". "; +} +</style> +</head> +<body> +<h1>Foo</h1> + <h2>Boo</h2> + <h2>Quux</h2> + <h3>Blah</h3> +<h1>Bar</h1> + <h2>Boo</h2> + <h2>Quux</h2> +<h1>Baz</h1> +</body> +</html> + diff --git a/tests/ref/counters_simple_ref.html b/tests/ref/counters_simple_ref.html new file mode 100644 index 00000000000..b1adc96fb26 --- /dev/null +++ b/tests/ref/counters_simple_ref.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> +<head> +<!-- Tests that `counter` works. --> +<style> +h1, h2, h3 { + font-size: 24px; +} +</style> +</head> +<body> +<h1>1. Foo</h1> + <h2>1.1. Boo</h2> + <h2>1.2. Quux</h2> + <h3>1.2.1. Blah</h3> +<h1>2. Bar</h1> + <h2>2.1. Boo</h2> + <h2>2.2. Quux</h2> +<h1>3. Baz</h1> +</body> +</html> + diff --git a/tests/ref/fonts/takao-p-gothic/COPYING.html b/tests/ref/fonts/takao-p-gothic/COPYING.html new file mode 100644 index 00000000000..2337aef8fce --- /dev/null +++ b/tests/ref/fonts/takao-p-gothic/COPYING.html @@ -0,0 +1,165 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" lang="ja-JP"><!-- InstanceBegin template="/Templates/master.dwt" codeOutsideHTMLIsLocked="false" --> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> +<meta http-equiv="Content-Style-Type" content="text/css" /> +<meta http-equiv="Content-Script-Type" content="text/javascript" /> + +<!-- InstanceBeginEditable name="doctitle" --> +<title>IPAフォントライセンスv1.0/IPA Font License Agreement v1.0</title> +<!-- InstanceEndEditable --> + +<meta name="Description" content="IPA(独立行政法人 情報処理推進機構)が提供する「IPAフォント」の使用許諾契約書" /> +<meta name="Keywords" content="IPAフォント,フォント" /> +<meta name="Copyright" content="Copyright © Information-technology Promotion Agency, Japan. All rights reserved 2009" /> +<meta name="Author" content="Information-technology Promotion Agency, Japan." /> +<meta http-equiv="imagetoolbar" content="false" /> + +</head> + +<body id="comment-detail"> + + <h4 id="LicenceTOP">IPA Font License Agreement v1.0 <a href="#LicenceJP">日本語/Japanese</a> <a href="#LicenceEng">English</a></h4> + + <h3 id="LicenceJP">IPAフォントライセンスv1.0 </h3> + <p>許諾者は、この使用許諾(以下「本契約」といいます。)に定める条件の下で、許諾プログラム(1条に定義するところによります。)を提供します。受領者(1条に定義するところによります。)が、許諾プログラムを使用し、複製し、または頒布する行為、その他、本契約に定める権利の利用を行った場合、受領者は本契約に同意したものと見なします。</p> + + <h4>第1条 用語の定義</h4> + <p>本契約において、次の各号に掲げる用語は、当該各号に定めるところによります。</p> + <ol class="mainclass"> + <li>「デジタル・フォント・プログラム」とは、フォントを含み、レンダリングしまたは表示するために用いられるコンピュータ・プログラムをいいます。</li> + <li>「許諾プログラム」とは、許諾者が本契約の下で許諾するデジタル・フォント・プログラムをいいます。</li> + <li>「派生プログラム」とは、許諾プログラムの一部または全部を、改変し、加除修正等し、入れ替え、その他翻案したデジタル・フォント・プログラムをいい、許諾プログラムの一部もしくは全部から文字情報を取り出し、またはデジタル・ドキュメント・ファイルからエンベッドされたフォントを取り出し、取り出された文字情報をそのまま、または改変をなして新たなデジタル・フォント・プログラムとして製作されたものを含みます。</li> + <li>「デジタル・コンテンツ」とは、デジタル・データ形式によってエンド・ユーザに提供される制作物のことをいい、動画・静止画等の映像コンテンツおよびテレビ番組等の放送コンテンツ、ならびに文字テキスト、画像、図形等を含んで構成された制作物を含みます。</li> + <li>「デジタル・ドキュメント・ファイル」とは、PDFファイルその他、各種ソフトウェア・プログラムによって製作されたデジタル・コンテンツであって、その中にフォントを表示するために許諾プログラムの全部または一部が埋め込まれた(エンベッドされた)ものをいいます。フォントが「エンベッドされた」とは、当該フォントが埋め込まれた特定の「デジタル・ドキュメント・ファイル」においてのみ表示されるために使用されている状態を指し、その特定の「デジタル・ドキュメント・ファイル」以外でフォントを表示するために使用できるデジタル・フォント・プログラムに含まれている場合と区別されます。</li> + <li>「コンピュータ」とは、本契約においては、サーバを含みます。</li> + <li>「複製その他の利用」とは、複製、譲渡、頒布、貸与、公衆送信、上映、展示、翻案その他の利用をいいます。</li> + <li>「受領者」とは、許諾プログラムを本契約の下で受領した人をいい、受領者から許諾プログラムを受領した人を含みます。</li> + </ol> + <h4>第2条 使用許諾の付与</h4> + <p>許諾者は受領者に対し、本契約の条項に従い、すべての国で、許諾プログラムを使用することを許諾します。ただし、許諾プログラムに存在する一切の権利はすべて許諾者が保有しています。本契約は、本契約で明示的に定められている場合を除き、いかなる意味においても、許諾者が保有する許諾プログラムに関する一切の権利および、いかなる商標、商号、もしくはサービス・マークに関する権利をも受領者に移転するものではありません。</p> + <ol class="mainclass"> + <li>受領者は本契約に定める条件に従い、許諾プログラムを任意の数のコンピュータにインストールし、当該コンピュータで使用することができます。</li> + <li> 受領者はコンピュータにインストールされた許諾プログラムをそのまま、または改変を行ったうえで、印刷物およびデジタル・コンテンツにおいて、文字テキスト表現等として使用することができます。</li> + <li>受領者は前項の定めに従い作成した印刷物およびデジタル・コンテンツにつき、その商用・非商用の別、および放送、通信、各種記録メディアなどの媒体の形式を問わず、複製その他の利用をすることができます。</li> + <li>受領者がデジタル・ドキュメント・ファイルからエンベッドされたフォントを取り出して派生プログラムを作成した場合には、かかる派生プログラムは本契約に定める条件に従う必要があります。</li> + <li>許諾プログラムのエンベッドされたフォントがデジタル・ドキュメント・ファイル内のデジタル・コンテンツをレンダリングするためにのみ使用される場合において、受領者が当該デジタル・ドキュメント・ファイルを複製その他の利用をする場合には、受領者はかかる行為に関しては本契約の下ではいかなる義務をも負いません。</li> + <li>受領者は、3条2項の定めに従い、商用・非商用を問わず、許諾プログラムをそのままの状態で改変することなく複製して第三者への譲渡し、公衆送信し、その他の方法で再配布することができます(以下、「再配布」といいます。)。</li> + <li>受領者は、上記の許諾プログラムについて定められた条件と同様の条件に従って、派生プログラムを作成し、使用し、複製し、再配布することができます。ただし、受領者が派生プログラムを再配布する場合には、3条1項の定めに従うものとします。</li> + </ol> + + <h4>第3条 制限</h4> + <p>前条により付与された使用許諾は、以下の制限に服します。</p> + <ol class="mainclass"> + <li>派生プログラムが前条4項及び7項に基づき再配布される場合には、以下の全ての条件を満たさなければなりません。 + <ul style="list-style-type:none;"> + <li>(1) 派生プログラムを再配布する際には、下記もまた、当該派生プログラムと一緒に再配布され、オンラインで提供され、または、郵送費・媒体及び取扱手数料の合計を超えない実費と引き換えに媒体を郵送する方法により提供されなければなりません。 + <ul style="list-style-type:none;"> + <li>(a) 派生プログラムの写し; および</li> + <li>(b) 派生プログラムを作成する過程でフォント開発プログラムによって作成された追加のファイルであって派生プログラムをさらに加工するにあたって利用できるファイルが存在すれば、当該ファイル</li> + </ul > + </li> + + <li>(2) 派生プログラムの受領者が、派生プログラムを、このライセンスの下で最初にリリースされた許諾プログラム(以下、「オリジナル・プログラム」といいます。)に置き換えることができる方法を再配布するものとします。かかる方法は、オリジナル・ファイルからの差分ファイルの提供、または、派生プログラムをオリジナル・プログラムに置き換える方法を示す指示の提供などが考えられます。</li> + <li>(3) 派生プログラムを、本契約書に定められた条件の下でライセンスしなければなりません。</li> + <li>(4) 派生プログラムのプログラム名、フォント名またはファイル名として、許諾プログラムが用いているのと同一の名称、またはこれを含む名称を使用してはなりません。</li> + <li>(5) 本項の要件を満たすためにオンラインで提供し、または媒体を郵送する方法で提供されるものは、その提供を希望するいかなる者によっても提供が可能です。</li> + </ul > + </li> + <li>受領者が前条6項に基づき許諾プログラムを再配布する場合には、以下の全ての条件を満たさなければなりません。 + <ul style="list-style-type:none;"> + <li>(1) 許諾プログラムの名称を変更してはなりません。</li> + <li>(2) 許諾プログラムに加工その他の改変を加えてはなりません。</li> + <li>(3) 本契約の写しを許諾プログラムに添付しなければなりません。</li> + </ul > + </li> + <li>許諾プログラムは、現状有姿で提供されており、許諾プログラムまたは派生プログラムについて、許諾者は一切の明示または黙示の保証(権利の所在、非侵害、商品性、特定目的への適合性を含むがこれに限られません)を行いません。いかなる場合にも、その原因を問わず、契約上の責任か厳格責任か過失その他の不法行為責任かにかかわらず、また事前に通知されたか否かにかかわらず、許諾者は、許諾プログラムまたは派生プログラムのインストール、使用、複製その他の利用または本契約上の権利の行使によって生じた一切の損害(直接・間接・付随的・特別・拡大・懲罰的または結果的損害)(商品またはサービスの代替品の調達、システム障害から生じた損害、現存するデータまたはプログラムの紛失または破損、逸失利益を含むがこれに限られません)について責任を負いません。</li> + <li>許諾プログラムまたは派生プログラムのインストール、使用、複製その他の利用に関して、許諾者は技術的な質問や問い合わせ等に対する対応その他、いかなるユーザ・サポートをも行う義務を負いません。</li> + </ol> + + <h4>第4条 契約の終了</h4> + <ol class="mainclass"> + <li>本契約の有効期間は、受領者が許諾プログラムを受領した時に開始し、受領者が許諾プログラムを何らかの方法で保持する限り続くものとします。</li> + <li>前項の定めにかかわらず、受領者が本契約に定める各条項に違反したときは、本契約は、何らの催告を要することなく、自動的に終了し、当該受領者はそれ以後、許諾プログラムおよび派生プログラムを一切使用しまたは複製その他の利用をすることができないものとします。ただし、かかる契約の終了は、当該違反した受領者から許諾プログラムまたは派生プログラムの配布を受けた受領者の権利に影響を及ぼすものではありません。</li> + </ol> + + <h4>第5条 準拠法</h4> + <ol class="mainclass"> + <li>IPAは、本契約の変更バージョンまたは新しいバージョンを公表することができます。その場合には、受領者は、許諾プログラムまたは派生プログラムの使用、複製その他の利用または再配布にあたり、本契約または変更後の契約のいずれかを選択することができます。その他、上記に記載されていない条項に関しては日本の著作権法および関連法規に従うものとします。</li> + <li>本契約は、日本法に基づき解釈されます。</li> + </ol> + <p><a href="#LicenceTOP">TOP</a></p> + + <hr/> + <h3 id="LicenceEng">IPA Font License Agreement v1.0 </h3> + <p>The Licensor provides the Licensed Program (as defined in Article 1 below) under the terms of this license agreement (“Agreement”). Any use, reproduction or distribution of the Licensed Program, or any exercise of rights under this Agreement by a Recipient (as defined in Article 1 below) constitutes the Recipient’s acceptance of this Agreement.</p> + + <h4>Article 1 (Definitions)</h4> + <ol class="mainclass"> + <li>“Digital Font Program” shall mean + a computer program containing, or used to render or display fonts.</li> + <li>“Licensed Program” shall mean a + Digital Font Program licensed by the Licensor under this Agreement.</li> + <li>“Derived Program” shall mean a Digital Font Program created as a result of a modification, addition, deletion, replacement or any other adaptation to or of a part or all of the Licensed Program, and includes a case where a Digital Font Program newly created by retrieving font information from a part or all of the Licensed Program or Embedded Fonts from a Digital Document File with or without modification of the retrieved font information. </li> + <li>“Digital Content” shall mean products provided to end users in the form of digital data, including video content, motion and/or still pictures, TV programs or other broadcasting content and products consisting of character text, pictures, photographic images, graphic symbols and/or the like.</li> + <li>“Digital Document File” shall mean a PDF file or other Digital Content created by various software programs in which a part or all of the Licensed Program becomes embedded or contained in the file for the display of the font (“Embedded Fonts”). Embedded Fonts are used only in the display of characters in the particular Digital Document File within which they are embedded, and shall be distinguished from those in any Digital Font Program, which may be used for display of characters outside that particular Digital Document File.</li> + <li>“Computer” shall include a server in this Agreement.</li> + <li>“Reproduction and Other Exploitation” shall mean reproduction, transfer, distribution, lease, public transmission, presentation, exhibition, adaptation and any other exploitation.</li> + <li>“Recipient” shall mean anyone who receives the Licensed Program under this Agreement, including one that receives the Licensed Program from a Recipient.</li> + </ol> + + <h4>Article 2 (Grant of License)</h4> + <p>The Licensor grants to the Recipient a license to use the Licensed Program in any and all countries in accordance with each of the provisions set forth in this Agreement. However, any and all rights underlying in the Licensed Program shall be held by the Licensor. In no sense is this Agreement intended to transfer any right relating to the Licensed Program held by the Licensor except as specifically set forth herein or any right relating to any trademark, trade name, or service mark to the Recipient.</p> + <ol class="mainclass"> + <li>The Recipient may install the Licensed Program on any number of Computers and use the same in accordance with the provisions set forth in this Agreement.</li> + <li>The Recipient may use the Licensed Program, with or without modification in printed materials or in Digital Content as an expression of character texts or the like.</li> + <li>The Recipient may conduct Reproduction and Other Exploitation of the printed materials and Digital Content created in accordance with the preceding Paragraph, for commercial or non-commercial purposes and in any form of media including but not limited to broadcasting, communication and various recording media.</li> + <li>If any Recipient extracts Embedded Fonts from a Digital Document File to create a Derived Program, such Derived Program shall be subject to the terms of this agreement.</li> + <li>If any Recipient performs Reproduction or Other Exploitation of a Digital Document File in which Embedded Fonts of the Licensed Program are used only for rendering the Digital Content within such Digital Document File then such Recipient shall have no further obligations under this Agreement in relation to such actions.</li> + <li>The Recipient may reproduce the Licensed Program as is without modification and transfer such copies, publicly transmit or otherwise redistribute the Licensed Program to a third party for commercial or non-commercial purposes (“Redistribute”), in accordance with the provisions set forth in Article 3 Paragraph 2.</li> + <li>The Recipient may create, use, reproduce and/or Redistribute a Derived Program under the terms stated above for the Licensed Program: provided, that the Recipient shall follow the provisions set forth in Article 3 Paragraph 1 when Redistributing the Derived Program. </li> + </ol> + + <h4>Article 3 (Restriction)</h4> + <p>The license granted in the preceding Article shall be subject to the following restrictions:</p> + <ol class="mainclass"> + <li>If a Derived Program is Redistributed pursuant to Paragraph 4 and 7 of the preceding Article, the following conditions must be met : + <ul style="list-style-type:none;"> + <li>(1)The following must be also Redistributed together with the Derived Program, or be made available online or by means of mailing mechanisms in exchange for a cost which does not exceed the total costs of postage, storage medium and handling fees: + <ul style="list-style-type:none;"> + <li>(a)a copy of the Derived Program; and</li> + <li>(b)any additional file created by the font developing program in the course of creating the Derived Program that can be used for further modification of the Derived Program, if any.</li> + </ul > + </li> + <li>(2)It is required to also Redistribute means to enable recipients of the Derived Program to replace the Derived Program with the Licensed Program first released under this License (the “Original Program”). Such means may be to provide a difference file from the Original Program, or instructions setting out a method to replace the Derived Program with the Original Program.</li> + <li>(3)The Recipient must license the Derived Program under the terms and conditions of this Agreement.</li> + <li>(4)No one may use or include the name of the Licensed Program as a program name, font name or file name of the Derived Program.</li> + <li>(5) Any material to be made available online or by means of mailing a medium to satisfy the requirements of this paragraph may be provided, verbatim, by any party wishing to do so.</li> + </ul > + </li> + <li>If the Recipient Redistributes the Licensed Program pursuant to Paragraph 6 of the preceding Article, the Recipient shall meet all of the following conditions: + <ul style="list-style-type:none;"> + <li>(1)The Recipient may not change the name of the Licensed Program.</li> + <li>(2)The Recipient may not alter or otherwise modify the Licensed Program.</li> + <li>(3)The Recipient must attach a copy of this Agreement to the Licensed Program.</li> + </ul > + </li> + <li>THIS LICENSED PROGRAM IS PROVIDED BY THE LICENSOR “AS IS” AND ANY EXPRESSED OR IMPLIED WARRANTY AS TO THE LICENSED PROGRAM OR ANY DERIVED PROGRAM, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXTENDED, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO; PROCUREMENT OF SUBSTITUTED GOODS OR SERVICE; DAMAGES ARISING FROM SYSTEM FAILURE; LOSS OR CORRUPTION OF EXISTING DATA OR PROGRAM; LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE INSTALLATION, USE, THE REPRODUCTION OR OTHER EXPLOITATION OF THE LICENSED PROGRAM OR ANY DERIVED PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</li> + <li>The Licensor is under no obligation to respond to any technical questions or inquiries, or provide any other user support in connection with the installation, use or the Reproduction and Other Exploitation of the Licensed Program or Derived Programs thereof.</li> + </ol> + + <h4>Article 4 (Termination of Agreement)</h4> + <ol class="mainclass"> + <li>The term of this Agreement shall begin from the time of receipt of the Licensed Program by the Recipient and shall continue as long as the Recipient retains any such Licensed Program in any way.</li> + <li>Notwithstanding the provision set forth in the preceding Paragraph, in the event of the breach of any of the provisions set forth in this Agreement by the Recipient, this Agreement shall automatically terminate without any notice. In the case of such termination, the Recipient may not use or conduct Reproduction and Other Exploitation of the Licensed Program or a Derived Program: provided that such termination shall not affect any rights of any other Recipient receiving the Licensed Program or the Derived Program from such Recipient who breached this Agreement.</li> + </ol> + + <h4>Article 5 (Governing Law)</h4> + <ol class="mainclass"> + <li>IPA may publish revised and/or new versions of this License. In such an event, the Recipient may select either this Agreement or any subsequent version of the Agreement in using, conducting the Reproduction and Other Exploitation of, or Redistributing the Licensed Program or a Derived Program. Other matters not specified above shall be subject to the Copyright Law of Japan and other related laws and regulations of Japan.</li> + <li>This Agreement shall be construed under the laws of Japan.</li> + </ol> + <p><a href="#LicenceTOP">TOP</a></p> + + </body> +</html> diff --git a/tests/ref/fonts/takao-p-gothic/TakaoPGothic.ttf b/tests/ref/fonts/takao-p-gothic/TakaoPGothic.ttf Binary files differnew file mode 100644 index 00000000000..24d8145de00 --- /dev/null +++ b/tests/ref/fonts/takao-p-gothic/TakaoPGothic.ttf diff --git a/tests/ref/ol_japanese_iroha_a.html b/tests/ref/ol_japanese_iroha_a.html new file mode 100644 index 00000000000..8c1aedd9965 --- /dev/null +++ b/tests/ref/ol_japanese_iroha_a.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<html> +<head> +<!-- Tests that Japanese iroha ordering works in list items. --> +<style> +@font-face { + font-family: TakaoPGothic; + src: url(fonts/takao-p-gothic/TakaoPGothic.ttf); +} +body { + font-family: TakaoPGothic; +} +ol { + list-style-position: inside; + list-style-type: hiragana-iroha; +} +li { +} +</style> +</head> +<body> +<ol> + <li>Gryffindor</li> + <li>Hufflepuff</li> + <li>Ravenclaw</li> + <li>Slytherin</li> +</ol> +</body> +</html> + + diff --git a/tests/ref/ol_japanese_iroha_bullet_styles.html b/tests/ref/ol_japanese_iroha_bullet_styles.html new file mode 100644 index 00000000000..0ed76f5c871 --- /dev/null +++ b/tests/ref/ol_japanese_iroha_bullet_styles.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html> +<head> +<!-- Ensures that U+3044, U+308D, U+306F, and U+306B are all supported in the current font. --> +<style> +@font-face { + font-family: TakaoPGothic; + src: url(fonts/takao-p-gothic/TakaoPGothic.ttf); +} +body { + font-family: TakaoPGothic; +} +ol { + list-style-position: inside; + list-style-type: none; +} +</style> +</head> +<body> +<ol> + <li>い. Gryffindor</li> + <li>い. Hufflepuff</li> + <li>い. Ravenclaw</li> + <li>い. Slytherin</li> +</ol> +</body> +</html> + + diff --git a/tests/ref/ol_japanese_iroha_ref.html b/tests/ref/ol_japanese_iroha_ref.html new file mode 100644 index 00000000000..582fdc96efc --- /dev/null +++ b/tests/ref/ol_japanese_iroha_ref.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<html> +<head> +<!-- Tests that Japanese iroha ordering works in list items. + + FIXME(pcwalton): This shouldn't have a "." after the kana. --> +<style> +@font-face { + font-family: TakaoPGothic; + src: url(fonts/takao-p-gothic/TakaoPGothic.ttf); +} +body { + font-family: TakaoPGothic; +} +ol { + list-style-position: inside; + list-style-type: none; +} +</style> +</head> +<body> +<ol> + <li>い. Gryffindor</li> + <li>ろ. Hufflepuff</li> + <li>は. Ravenclaw</li> + <li>に. Slytherin</li> +</ol> +</body> +</html> + + diff --git a/tests/ref/ol_simple_a.html b/tests/ref/ol_simple_a.html new file mode 100644 index 00000000000..e63c00c4ceb --- /dev/null +++ b/tests/ref/ol_simple_a.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html> +<head> +<style> +li { + list-style-type: decimal; + list-style-position: inside; +} +</style> +</head> +<body> +<ol> + <li>Foo</li> + <li>Bar</li> + <li>Baz</li> +</ol> +</body> +</html> + diff --git a/tests/ref/ol_simple_ref.html b/tests/ref/ol_simple_ref.html new file mode 100644 index 00000000000..d3411bb2e6d --- /dev/null +++ b/tests/ref/ol_simple_ref.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html> +<head> +<style> +li { + list-style-type: none; + list-style-position: inside; +} +</style> +</head> +<body> +<ol> + <li>1. Foo</li> + <li>2. Bar</li> + <li>3. Baz</li> +</ol> +</body> +</html> + diff --git a/tests/ref/quotes_simple_a.html b/tests/ref/quotes_simple_a.html new file mode 100644 index 00000000000..257be1276e0 --- /dev/null +++ b/tests/ref/quotes_simple_a.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> +<head> +<!-- Tests that the initial value of `quotes` works. --> +</head> +<body> +I remember when I first read <q>Hagrid said, <q>You're a wizard, Harry!</q></q> +</body> +</html> + diff --git a/tests/ref/quotes_simple_ref.html b/tests/ref/quotes_simple_ref.html new file mode 100644 index 00000000000..b378efaa4ab --- /dev/null +++ b/tests/ref/quotes_simple_ref.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> +<head> +<!-- Tests that the initial value of `quotes` works. --> +</head> +<body> +I remember when I first read “Hagrid said, ‘You're a wizard, Harry!’” +</body> +</html> + + diff --git a/tests/wpt/metadata/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002.html.ini b/tests/wpt/metadata/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002.html.ini index 847b0a14070..091ee28df85 100644 --- a/tests/wpt/metadata/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002.html.ini +++ b/tests/wpt/metadata/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002.html.ini @@ -2,4 +2,4 @@ type: reftest reftype: == refurl: /html/semantics/grouping-content/the-li-element/grouping-li-reftest-002-ref.html - expected: FAIL + expected: PASS diff --git a/tests/wpt/metadata/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001.html.ini b/tests/wpt/metadata/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001.html.ini index beb07b038e9..0785f9e5b8c 100644 --- a/tests/wpt/metadata/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001.html.ini +++ b/tests/wpt/metadata/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001.html.ini @@ -2,4 +2,4 @@ type: reftest reftype: == refurl: /html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001-ref.html - expected: FAIL + expected: PASS |