From a2fea4c093c06bc2ecb4c0cc75b3a96bee344eaa Mon Sep 17 00:00:00 2001 From: Adenilson Cavalcanti Date: Mon, 2 Mar 2015 17:03:06 -0800 Subject: [PATCH 01/29] Cleanup compilation warning. --- components/script/dom/canvasrenderingcontext2d.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index d0ae869d847..8367e27d05e 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -151,7 +151,7 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D> } fn LineTo(self, x: f64, y: f64) { - self.renderer.send(CanvasMsg::LineTo(Point2D(x as f32, y as f32))); + self.renderer.send(CanvasMsg::LineTo(Point2D(x as f32, y as f32))).unwrap(); } fn BezierCurveTo(self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) { From a07a0cf39f1417754327247f7f9aa6c4daf72070 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 3 Mar 2015 11:39:37 +0800 Subject: [PATCH 02/29] Added type parameter to PartialEq on JSRef (fixes #5112, #3960) --- components/script/dom/bindings/js.rs | 2 +- components/script/dom/document.rs | 2 +- components/script/dom/node.rs | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/components/script/dom/bindings/js.rs b/components/script/dom/bindings/js.rs index b370b3a073f..7ada9f45d14 100644 --- a/components/script/dom/bindings/js.rs +++ b/components/script/dom/bindings/js.rs @@ -727,7 +727,7 @@ impl<'a, T> Clone for JSRef<'a, T> { } } -impl<'a, T> PartialEq for JSRef<'a, T> { +impl<'a, 'b, T> PartialEq> for JSRef<'a, T> { fn eq(&self, other: &JSRef) -> bool { self.ptr == other.ptr } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 6e5413ebdc8..95649de58d1 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -241,7 +241,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { let browser_context = browser_context.as_ref().unwrap(); let active_document = browser_context.active_document().root(); - if self.clone() != active_document.r() { + if self != active_document.r() { return false; } // FIXME: It should also check whether the browser context is top-level or not diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index a7b5e5484d6..f04420728e8 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -409,7 +409,7 @@ pub trait NodeHelpers<'a> { fn child_elements(self) -> ChildElementIterator<'a>; fn following_siblings(self) -> NodeChildrenIterator<'a>; fn is_in_doc(self) -> bool; - fn is_inclusive_ancestor_of(self, parent: JSRef<'a, Node>) -> bool; // FIXME: See #3960 + fn is_inclusive_ancestor_of(self, parent: JSRef) -> bool; fn is_parent_of(self, child: JSRef) -> bool; fn type_id(self) -> NodeTypeId; @@ -715,7 +715,7 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { } } - fn is_inclusive_ancestor_of(self, parent: JSRef<'a, Node>) -> bool { + fn is_inclusive_ancestor_of(self, parent: JSRef) -> bool { self == parent || parent.ancestors().any(|ancestor| ancestor == self) } @@ -1376,7 +1376,7 @@ impl Node { // Step 7-8. let reference_child = match child { - Some(child) if child.clone() == node => node.next_sibling().map(|node| node.root().get_unsound_ref_forever()), + Some(child) if child == node => node.next_sibling().map(|node| node.root().get_unsound_ref_forever()), _ => child }; @@ -1946,7 +1946,7 @@ impl<'a> NodeMethods for JSRef<'a, Node> { } // Ok if not caught by previous error checks. - if node.clone() == child { + if node == child { return Ok(Temporary::from_rooted(child)); } @@ -2106,7 +2106,7 @@ impl<'a> NodeMethods for JSRef<'a, Node> { // http://dom.spec.whatwg.org/#dom-node-comparedocumentposition fn CompareDocumentPosition(self, other: JSRef) -> u16 { - if self.clone() == other { // FIXME: See issue #3960 + if self == other { // step 2. 0 } else { From a82fc00806edf63672a660827117c743ef03c63f Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 7 Jan 2015 16:05:10 -0800 Subject: [PATCH 03/29] =?UTF-8?q?layout:=20Implement=20`overflow-x`=20and?= =?UTF-8?q?=20`overflow-y`=20per=20CSS-OVERFLOW=20=C2=A7=203.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fragmentation is not yet supported. --- components/layout/block.rs | 10 +- components/layout/display_list_builder.rs | 39 ++++++-- components/layout/inline.rs | 6 +- .../dom/webidls/CSSStyleDeclaration.webidl | 2 + components/style/properties.mako.rs | 91 ++++++++++++++++++- components/style/values.rs | 2 + tests/ref/basic.list | 1 + tests/ref/overflow_xy_a.html | 36 ++++++++ tests/ref/overflow_xy_ref.html | 29 ++++++ 9 files changed, 199 insertions(+), 17 deletions(-) create mode 100644 tests/ref/overflow_xy_a.html create mode 100644 tests/ref/overflow_xy_ref.html diff --git a/components/layout/block.rs b/components/layout/block.rs index e26fb5f8b8e..822ec6bfb82 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -57,9 +57,10 @@ use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize}; use servo_util::opts; use std::cmp::{max, min}; use std::fmt; +use style::computed_values::{overflow_x, overflow_y, position, box_sizing, display, float}; use style::properties::ComputedValues; -use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; -use style::computed_values::{overflow, position, box_sizing, display, float}; +use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; +use style::values::computed::{LengthOrPercentageOrNone}; use std::sync::Arc; /// Information specific to floated blocks. @@ -1431,7 +1432,10 @@ impl BlockFlow { display::T::inline_block => { FormattingContextType::Other } - _ if style.get_box().overflow != overflow::T::visible => FormattingContextType::Block, + _ if style.get_box().overflow_x != overflow_x::T::visible || + style.get_box().overflow_y != overflow_y::T(overflow_x::T::visible) => { + FormattingContextType::Block + } _ => FormattingContextType::None, } } diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 039dbc9c376..e20ade0ce19 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -41,6 +41,7 @@ use servo_util::cursor::Cursor; use servo_util::geometry::{self, Au, ZERO_POINT, to_px, to_frac_px}; use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize}; use servo_util::opts; +use std::cmp; use std::default::Default; use std::iter::repeat; use std::num::Float; @@ -48,7 +49,7 @@ use style::values::specified::{AngleOrCorner, HorizontalDirection, VerticalDirec use style::values::computed::{Image, LinearGradient, LengthOrPercentage}; use style::values::RGBA; use style::computed_values::filter::Filter; -use style::computed_values::{background_attachment, background_repeat, border_style, overflow}; +use style::computed_values::{background_attachment, background_repeat, border_style, overflow_x}; use style::computed_values::{position, visibility}; use style::properties::style_structs::Border; use style::properties::ComputedValues; @@ -990,17 +991,37 @@ impl FragmentDisplayListBuilding for Fragment { } // Account for style-specified `clip`. - let current_clip = self.calculate_style_specified_clip(current_clip, - stacking_relative_border_box); + let mut current_clip = self.calculate_style_specified_clip(current_clip, + stacking_relative_border_box); - // Only clip if `overflow` tells us to. - match self.style.get_box().overflow { - overflow::T::hidden | overflow::T::auto | overflow::T::scroll => { - // Create a new clip rect. - current_clip.intersect_rect(stacking_relative_border_box) + // Clip according to the values of `overflow-x` and `overflow-y`. + // + // TODO(pcwalton): Support scrolling. + // FIXME(pcwalton): This may be more complex than it needs to be, since it seems to be + // impossible with the computed value rules as they are to have `overflow-x: visible` with + // `overflow-y: ` or vice versa! + match self.style.get_box().overflow_x { + overflow_x::T::hidden | overflow_x::T::auto | overflow_x::T::scroll => { + let mut bounds = current_clip.bounding_rect(); + let max_x = cmp::min(bounds.max_x(), stacking_relative_border_box.max_x()); + bounds.origin.x = cmp::max(bounds.origin.x, stacking_relative_border_box.origin.x); + bounds.size.width = max_x - bounds.origin.x; + current_clip = current_clip.intersect_rect(&bounds) } - _ => current_clip, + _ => {} } + match self.style.get_box().overflow_y.0 { + overflow_x::T::hidden | overflow_x::T::auto | overflow_x::T::scroll => { + let mut bounds = current_clip.bounding_rect(); + let max_y = cmp::min(bounds.max_y(), stacking_relative_border_box.max_y()); + bounds.origin.y = cmp::max(bounds.origin.y, stacking_relative_border_box.origin.y); + bounds.size.height = max_y - bounds.origin.y; + current_clip = current_clip.intersect_rect(&bounds) + } + _ => {} + } + + current_clip } fn build_display_list_for_text_fragment(&self, diff --git a/components/layout/inline.rs b/components/layout/inline.rs index a5ca5038c30..0a4b575c3c4 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -34,7 +34,7 @@ use std::mem; use std::num::ToPrimitive; use std::ops::{Add, Sub, Mul, Div, Rem, Neg, Shl, Shr, Not, BitOr, BitAnd, BitXor}; use std::u16; -use style::computed_values::{overflow, text_align, text_justify, text_overflow, vertical_align}; +use style::computed_values::{overflow_x, text_align, text_justify, text_overflow, vertical_align}; use style::computed_values::{white_space}; use style::properties::ComputedValues; use std::sync::Arc; @@ -653,8 +653,8 @@ impl LineBreaker { let available_inline_size = self.pending_line.green_zone.inline - self.pending_line.bounds.size.inline - indentation; match (fragment.style().get_inheritedtext().text_overflow, - fragment.style().get_box().overflow) { - (text_overflow::T::clip, _) | (_, overflow::T::visible) => {} + fragment.style().get_box().overflow_x) { + (text_overflow::T::clip, _) | (_, overflow_x::T::visible) => {} (text_overflow::T::ellipsis, _) => { need_ellipsis = fragment.border_box.size.inline > available_inline_size; } diff --git a/components/script/dom/webidls/CSSStyleDeclaration.webidl b/components/script/dom/webidls/CSSStyleDeclaration.webidl index 1d642acf17c..d6a4420b2a9 100644 --- a/components/script/dom/webidls/CSSStyleDeclaration.webidl +++ b/components/script/dom/webidls/CSSStyleDeclaration.webidl @@ -103,6 +103,8 @@ partial interface CSSStyleDeclaration { [TreatNullAs=EmptyString] attribute DOMString listStyleImage; [TreatNullAs=EmptyString] attribute DOMString overflow; + [TreatNullAs=EmptyString] attribute DOMString overflowX; + [TreatNullAs=EmptyString] attribute DOMString overflowY; [TreatNullAs=EmptyString] attribute DOMString overflowWrap; [TreatNullAs=EmptyString] attribute DOMString tableLayout; diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs index 2ff84b2107c..83d6570a800 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -676,8 +676,77 @@ pub mod longhands { // CSS 2.1, Section 11 - Visual effects - // FIXME: Implement scrolling for `scroll` and `auto` (#2742). - ${single_keyword("overflow", "visible hidden scroll auto")} + + // FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`. + <%self:single_keyword_computed name="overflow-x" values="visible hidden scroll auto"> + use values::computed::{Context, ToComputedValue}; + + pub fn compute_with_other_overflow_direction(value: SpecifiedValue, + other_direction: SpecifiedValue) + -> computed_value::T { + // CSS-OVERFLOW 3 states "Otherwise, if one cascaded values is one of the scrolling + // values and the other is `visible`, then computed values are the cascaded values with + // `visible` changed to `auto`." + match (value, other_direction) { + (SpecifiedValue::visible, SpecifiedValue::hidden) | + (SpecifiedValue::visible, SpecifiedValue::scroll) | + (SpecifiedValue::visible, SpecifiedValue::auto) => computed_value::T::auto, + _ => value, + } + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value(&self, context: &Context) -> computed_value::T { + compute_with_other_overflow_direction(*self, context.overflow_y.0) + } + } + + + // FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`. + <%self:longhand name="overflow-y"> + use super::overflow_x; + use values::computed::{Context, ToComputedValue}; + + use cssparser::ToCss; + use text_writer::{self, TextWriter}; + + pub use self::computed_value::T as SpecifiedValue; + + impl ToCss for SpecifiedValue { + fn to_css(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { + self.0.to_css(dest) + } + } + + pub mod computed_value { + #[derive(Clone, Copy, PartialEq)] + pub struct T(pub super::super::overflow_x::computed_value::T); + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value(&self, context: &Context) -> computed_value::T { + let computed_value::T(this) = *self; + computed_value::T(overflow_x::compute_with_other_overflow_direction( + this, + context.overflow_x)) + } + } + + pub fn get_initial_value() -> computed_value::T { + computed_value::T(overflow_x::get_initial_value()) + } + + pub fn parse(context: &ParserContext, input: &mut Parser) -> Result { + overflow_x::parse(context, input).map(|value| SpecifiedValue(value)) + } + + ${switch_to_style_struct("InheritedBox")} @@ -2830,6 +2899,16 @@ pub mod shorthands { _ => Err(()), } + + <%self:shorthand name="overflow" sub_properties="overflow-x overflow-y"> + use properties::longhands::{overflow_x, overflow_y}; + + let overflow = try!(overflow_x::parse(context, input)); + Ok(Longhands { + overflow_x: Some(overflow), + overflow_y: Some(overflow_y::SpecifiedValue(overflow)), + }) + } @@ -3474,6 +3553,8 @@ pub fn cascade(applicable_declarations: &[DeclarationBlock false, } } + PropertyDeclaration::OverflowX(ref value) => { + context.overflow_x = get_specified!(get_box, overflow_x, value); + } + PropertyDeclaration::OverflowY(ref value) => { + context.overflow_y = get_specified!(get_box, overflow_y, value); + } PropertyDeclaration::Float(ref value) => { context.floated = get_specified!(get_box, float, value) != longhands::float::SpecifiedValue::none; diff --git a/components/style/values.rs b/components/style/values.rs index 450e82cfa61..ae1e4bc2600 100644 --- a/components/style/values.rs +++ b/components/style/values.rs @@ -690,6 +690,8 @@ pub mod computed { pub font_size: longhands::font_size::computed_value::T, pub root_font_size: longhands::font_size::computed_value::T, pub display: longhands::display::computed_value::T, + pub overflow_x: longhands::overflow_x::computed_value::T, + pub overflow_y: longhands::overflow_y::computed_value::T, pub positioned: bool, pub floated: bool, pub border_top_present: bool, diff --git a/tests/ref/basic.list b/tests/ref/basic.list index 0d83cc16d1f..2361eb2e7df 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -255,6 +255,7 @@ fragment=top != ../html/acid2.html acid2_ref.html == canvas_lineto_a.html canvas_lineto_ref.html != text_decoration_smoke_a.html text_decoration_smoke_ref.html == hide_after_create.html hide_after_create_ref.html +== overflow_xy_a.html overflow_xy_ref.html == text_shadow_simple_a.html text_shadow_simple_ref.html == text_shadow_decorations_a.html text_shadow_decorations_ref.html == text_shadow_blur_a.html text_shadow_blur_ref.html diff --git a/tests/ref/overflow_xy_a.html b/tests/ref/overflow_xy_a.html new file mode 100644 index 00000000000..eee0c79eec8 --- /dev/null +++ b/tests/ref/overflow_xy_a.html @@ -0,0 +1,36 @@ + + + + + + + +
+
+ + + diff --git a/tests/ref/overflow_xy_ref.html b/tests/ref/overflow_xy_ref.html new file mode 100644 index 00000000000..777f18bbebe --- /dev/null +++ b/tests/ref/overflow_xy_ref.html @@ -0,0 +1,29 @@ + + + + + + + +
+
+ + + + From 30fd28d1077fbb3f47140f6ab1252c0d24f44d23 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 2 Jan 2015 17:40:54 -0500 Subject: [PATCH 04/29] =?UTF-8?q?layout:=20Implement=20ordered=20lists,=20?= =?UTF-8?q?CSS=20counters,=20and=20`quotes`=20per=20CSS=202.1=20=C2=A7=201?= =?UTF-8?q?2.3-12.5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- components/layout/block.rs | 39 +- components/layout/construct.rs | 254 ++++---- components/layout/display_list_builder.rs | 1 + components/layout/flow.rs | 118 ++-- components/layout/fragment.rs | 162 +++-- components/layout/generated_content.rs | 573 ++++++++++++++++++ components/layout/incremental.rs | 48 +- components/layout/inline.rs | 22 +- components/layout/layout_task.rs | 8 + components/layout/lib.rs | 3 +- components/layout/list_item.rs | 97 ++- components/layout/sequential.rs | 24 +- components/layout/table.rs | 61 +- components/layout/table_caption.rs | 14 +- components/layout/table_cell.rs | 6 +- components/layout/table_colgroup.rs | 4 +- components/layout/table_row.rs | 23 +- components/layout/table_rowgroup.rs | 16 +- components/layout/table_wrapper.rs | 27 +- components/layout/traversal.rs | 5 +- components/layout/wrapper.rs | 47 +- .../dom/webidls/CSSStyleDeclaration.webidl | 5 + components/style/properties.mako.rs | 423 +++++++++++-- components/util/time.rs | 3 + ports/cef/Cargo.lock | 1 + tests/ref/basic.list | 5 + tests/ref/counters_nested_a.html | 36 ++ tests/ref/counters_nested_ref.html | 29 + tests/ref/counters_simple_a.html | 42 ++ tests/ref/counters_simple_ref.html | 22 + tests/ref/ol_japanese_iroha_a.html | 25 + tests/ref/ol_japanese_iroha_ref.html | 27 + tests/ref/ol_simple_a.html | 19 + tests/ref/ol_simple_ref.html | 19 + tests/ref/quotes_simple_a.html | 10 + tests/ref/quotes_simple_ref.html | 11 + 36 files changed, 1734 insertions(+), 495 deletions(-) create mode 100644 components/layout/generated_content.rs create mode 100644 tests/ref/counters_nested_a.html create mode 100644 tests/ref/counters_nested_ref.html create mode 100644 tests/ref/counters_simple_a.html create mode 100644 tests/ref/counters_simple_ref.html create mode 100644 tests/ref/ol_japanese_iroha_a.html create mode 100644 tests/ref/ol_japanese_iroha_ref.html create mode 100644 tests/ref/ol_simple_a.html create mode 100644 tests/ref/ol_simple_ref.html create mode 100644 tests/ref/quotes_simple_a.html create mode 100644 tests/ref/quotes_simple_ref.html diff --git a/components/layout/block.rs b/components/layout/block.rs index e26fb5f8b8e..ab9a3f570c8 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}; @@ -40,7 +39,8 @@ use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS}; use flow::{LAYERS_NEEDED_FOR_DESCENDANTS, NEEDS_LAYER}; use flow::{IS_ABSOLUTELY_POSITIONED}; use flow::{CLEARS_LEFT, CLEARS_RIGHT}; -use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo}; +use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, FragmentMutator}; +use fragment::{SpecificFragmentInfo}; use incremental::{REFLOW, REFLOW_OUT_OF_FLOW}; use layout_debug; use model::{IntrinsicISizes, MarginCollapseInfo}; @@ -568,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 { @@ -596,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) @@ -1915,6 +1884,10 @@ impl Flow for BlockFlow { CoordinateSystem::Parent) .translate(stacking_context_position)); } + + fn mutate_fragments(&mut self, mutator: &mut FragmentMutator) { + mutator.process(&mut self.fragment) + } } impl fmt::Debug for BlockFlow { diff --git a/components/layout/construct.rs b/components/layout/construct.rs index b2ea8090ea6..82c941b4d67 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -13,33 +13,27 @@ #![deny(unsafe_blocks)] -use css::node_style::StyledNode; use block::BlockFlow; use context::LayoutContext; +use css::node_style::StyledNode; use floats::FloatKind; -use flow::{Flow, ImmutableFlowUtils, MutableOwnedFlowUtils}; -use flow::{Descendants, AbsDescendants}; -use flow::{IS_ABSOLUTELY_POSITIONED}; -use flow; +use flow::{self, AbsDescendants, Descendants, Flow, IS_ABSOLUTELY_POSITIONED, ImmutableFlowUtils}; +use flow::{MutableOwnedFlowUtils}; use flow_ref::FlowRef; -use fragment::{Fragment, IframeFragmentInfo}; -use fragment::ImageFragmentInfo; -use fragment::CanvasFragmentInfo; -use fragment::InlineAbsoluteHypotheticalFragmentInfo; -use fragment::{InlineBlockFragmentInfo, SpecificFragmentInfo}; -use fragment::TableColumnFragmentInfo; -use fragment::UnscannedTextFragmentInfo; +use fragment::{CanvasFragmentInfo, Fragment, GeneratedContentInfo, IframeFragmentInfo}; +use fragment::{ImageFragmentInfo, InlineAbsoluteHypotheticalFragmentInfo, InlineBlockFragmentInfo}; +use fragment::{SpecificFragmentInfo, TableColumnFragmentInfo, UnscannedTextFragmentInfo}; use incremental::{RECONSTRUCT_FLOW, RestyleDamage}; use inline::InlineFlow; -use list_item::{self, ListItemFlow}; +use list_item::{ListItemFlow, ListStyleTypeContent}; use parallel; -use table_wrapper::TableWrapperFlow; use table::TableFlow; use table_caption::TableCaptionFlow; -use table_colgroup::TableColGroupFlow; -use table_rowgroup::TableRowGroupFlow; -use table_row::TableRowFlow; use table_cell::TableCellFlow; +use table_colgroup::TableColGroupFlow; +use table_row::TableRowFlow; +use table_rowgroup::TableRowGroupFlow; +use table_wrapper::TableWrapperFlow; use text::TextRunScanner; use util::{HAS_NEWLY_CONSTRUCTED_FLOW, LayoutDataAccess, OpaqueNodeMethods, LayoutDataWrapper}; use wrapper::{PostorderNodeMutTraversal, PseudoElementType, TLayoutNode, ThreadSafeLayoutNode}; @@ -54,9 +48,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; @@ -250,41 +245,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 @@ -324,7 +329,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()) } @@ -458,8 +465,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, @@ -555,18 +562,22 @@ impl<'a> FlowConstructor<'a> { 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))) { + 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() { kid.set_flow_construction_result(ConstructionResult::None) } } - Some(Fragment::new_from_specific_info( - node, - SpecificFragmentInfo::UnscannedText(UnscannedTextFragmentInfo::new(node)))) + + let mut fragments = DList::new(); + self.create_fragments_for_node_text_content(&mut fragments, node, node.style()); + fragments.into_iter().next() } else { None }; @@ -574,12 +585,39 @@ impl<'a> FlowConstructor<'a> { self.build_flow_for_block_starting_with_fragment(flow, node, initial_fragment) } + /// 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, + node: &ThreadSafeLayoutNode, + style: &Arc) { + 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; + let flow = box BlockFlow::from_node_and_fragment(node, self.build_fragment_for_block(node)) + as Box; self.build_flow_for_block(FlowRef::new(flow), node) } @@ -587,7 +625,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; + let fragment = self.build_fragment_for_block(node); + let flow = box BlockFlow::float_from_node_and_fragment(node, fragment, float_kind) as + Box; self.build_flow_for_block(FlowRef::new(flow), node) } @@ -655,8 +695,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, @@ -710,30 +750,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 { @@ -754,7 +786,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); @@ -780,7 +812,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); @@ -865,7 +897,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), _ => { @@ -875,7 +907,7 @@ impl<'a> FlowConstructor<'a> { }; let mut wrapper_flow = FlowRef::new(wrapper_flow as Box); - 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); @@ -909,7 +941,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); @@ -932,7 +965,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; + let fragment = self.build_fragment_for_block(node); + let flow = box TableCaptionFlow::from_node_and_fragment(node, fragment) as Box; self.build_flow_for_block(FlowRef::new(flow), node) } @@ -940,7 +974,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; self.build_flow_for_block(FlowRef::new(flow), node) @@ -949,7 +983,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; self.build_flow_for_block(FlowRef::new(flow), node) } @@ -957,7 +991,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 @@ -985,19 +1019,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) => { + ListStyleTypeContent::None => None, + ListStyleTypeContent::StaticText(text) => { let text = text.to_owned(); 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)))); @@ -1007,6 +1040,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))) + } } } }; @@ -1018,19 +1054,20 @@ impl<'a> FlowConstructor<'a> { // there. let flow; let initial_fragment; + let main_fragment = self.build_fragment_for_block(node); 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); + flow = box ListItemFlow::from_node_fragments_and_flotation(node, + main_fragment, + marker_fragment, + flotation); initial_fragment = None; } list_style_position::T::inside => { - flow = box ListItemFlow::from_node_marker_and_flotation(self, - node, - None, - flotation); + flow = box ListItemFlow::from_node_fragments_and_flotation(node, + main_fragment, + None, + flotation); initial_fragment = marker_fragment; } } @@ -1049,9 +1086,8 @@ impl<'a> FlowConstructor<'a> { } 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) } @@ -1059,9 +1095,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` @@ -1077,7 +1113,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); @@ -1293,25 +1329,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 039dbc9c376..4c486d8d50a 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -884,6 +884,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 19f1df86c3a..b9dacc872d1 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -32,10 +32,10 @@ use display_list_builder::DisplayListBuildingResult; 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 fragment::{Fragment, FragmentBorderBoxIterator, FragmentMutator, SpecificFragmentInfo}; +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; @@ -235,6 +235,15 @@ pub trait Flow: fmt::Debug + Sync { iterator: &mut FragmentBorderBoxIterator, stacking_context_position: &Point2D); + /// Mutably iterates through fragments in this flow. + fn mutate_fragments(&mut self, mutator: &mut FragmentMutator); + + 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) {} @@ -474,51 +483,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 } } @@ -544,13 +569,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] @@ -870,39 +895,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::all(); damage.remove(RECONSTRUCT_FLOW); BaseFlow { @@ -976,10 +1003,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, } } @@ -1314,3 +1343,4 @@ impl ContainingBlockLink { } } } + diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 26a91139b31..88204e5042c 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}; @@ -28,11 +27,11 @@ use geom::{Point2D, Rect, Size2D}; use gfx::display_list::{BLUR_INFLATION_FACTOR, OpaqueNode}; use gfx::text::glyph::CharIndex; use gfx::text::text_run::{TextRun, TextRunSlice}; -use script_traits::UntrustedNodeAddress; -use rustc_serialize::{Encodable, Encoder}; use msg::constellation_msg::{PipelineId, SubpageId}; use net::image::holder::ImageHolder; use net::local_image_cache::LocalImageCache; +use rustc_serialize::{Encodable, Encoder}; +use script_traits::UntrustedNodeAddress; use servo_util::geometry::{self, Au, ZERO_POINT}; use servo_util::logical_geometry::{LogicalRect, LogicalSize, LogicalMargin}; use servo_util::range::*; @@ -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), + Iframe(Box), Image(Box), Canvas(Box), @@ -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::all(), 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() } @@ -1220,6 +1191,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 { @@ -1233,6 +1212,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 | @@ -1302,6 +1282,7 @@ impl Fragment { pub fn content_inline_size(&self) -> Au { match self.specific { SpecificFragmentInfo::Generic | + SpecificFragmentInfo::GeneratedContent(_) | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell | @@ -1333,6 +1314,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 | @@ -1351,7 +1333,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!") @@ -1381,6 +1363,7 @@ impl Fragment { match self.specific { SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Generic | + SpecificFragmentInfo::GeneratedContent(_) | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::Table | @@ -1688,22 +1671,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(); @@ -1767,22 +1751,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(); @@ -1920,6 +1905,7 @@ impl Fragment { SpecificFragmentInfo::TableWrapper => false, SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Generic | + SpecificFragmentInfo::GeneratedContent(_) | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::ScannedText(_) | @@ -2133,3 +2119,9 @@ fn strip_trailing_whitespace(text_run: &TextRun, range: &mut Range) - return true } +/// A mutable iterator over fragments. +pub trait FragmentMutator { + /// The operation to perform. + fn process(&mut self, fragment: &mut Fragment); +} + diff --git a/components/layout/generated_content.rs b/components/layout/generated_content.rs new file mode 100644 index 00000000000..d4ec6e5a534 --- /dev/null +++ b/components/layout/generated_content.rs @@ -0,0 +1,573 @@ +/* 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, FragmentMutator, GeneratedContentInfo, SpecificFragmentInfo}; +use fragment::{UnscannedTextFragmentInfo}; +use incremental::{self, RESOLVE_GENERATED_CONTENT}; +use text::TextRunScanner; + +use gfx::display_list::OpaqueNode; +use servo_util::smallvec::{SmallVec, SmallVec8}; +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; + +// 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, + /// 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 mutator); + } + + #[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> FragmentMutator for ResolveGeneratedContentFragmentMutator<'a,'b> { + fn process(&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, + list_style_type)) => { + 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(), + list_style_type, + RenderingMode::Plain) + } + GeneratedContentInfo::ContentItem(ContentItem::Counters(ref counter_name, + ref separator, + list_style_type)) => { + 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(), + list_style_type, + 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 + } + } +} + +impl<'a,'b> ResolveGeneratedContentFragmentMutator<'a,'b> { + 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, +} + +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. + match self.values.last_mut() { + Some(ref mut existing_value) 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) { + let mut position = None; + for (i, value) in self.values.iter().enumerate() { + if value.level > level { + position = Some(i); + break + } + } + + if let Some(position) = position { + 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, + list_style_type: list_style_type::T, + mode: RenderingMode) + -> Option { + 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, + 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::all(), + 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_str(static_representation(list_style_type).unwrap()) + } + 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 string that represents the value rendered using the given list-style, if +/// possible. +pub fn static_representation(list_style_type: list_style_type::T) -> Option<&'static str> { + match list_style_type { + list_style_type::T::disc => Some("•\u{00a0}"), + list_style_type::T::circle => Some("◦\u{00a0}"), + list_style_type::T::square => Some("▪\u{00a0}"), + list_style_type::T::disclosure_open => Some("▾\u{00a0}"), + list_style_type::T::disclosure_closed => Some("‣\u{00a0}"), + _ => None, + } +} + +/// 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 i in range(0, string.len()).rev() { + accumulator.push(*string.get(i)) + } +} + diff --git a/components/layout/incremental.rs b/components/layout/incremental.rs index b163a8fbf29..2bf7a67ee7a 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 fully damaged flow. +/// +/// Use this instead of `RestyleDamage::all()` because `RestyleDamage::all()` will result in +/// unnecessary sequential resolution of generated content. +pub fn all() -> RestyleDamage { + REPAINT | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW | RECONSTRUCT_FLOW +} + pub fn compute_damage(old: &Option>, new: &ComputedValues) -> RestyleDamage { let old: &ComputedValues = match old.as_ref() { - None => return RestyleDamage::all(), + None => return all(), 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(all()); 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 a5ca5038c30..7a089095484 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -11,10 +11,10 @@ use floats::{FloatKind, Floats, PlacementInfo}; 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::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, FragmentMutator}; +use fragment::{ScannedTextFragmentInfo, SpecificFragmentInfo}; use fragment::SplitInfo; -use incremental::{REFLOW, REFLOW_OUT_OF_FLOW}; +use incremental::{REFLOW, REFLOW_OUT_OF_FLOW, RESOLVE_GENERATED_CONTENT}; use layout_debug; use model::IntrinsicISizesContribution; use text; @@ -789,14 +789,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 @@ -1391,6 +1399,12 @@ impl Flow for InlineFlow { .translate(stacking_context_position)) } } + + fn mutate_fragments(&mut self, mutator: &mut FragmentMutator) { + for fragment in self.fragments.fragments.iter_mut() { + mutator.process(fragment) + } + } } impl fmt::Debug for InlineFlow { diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index ca321f23526..3a0959d6b48 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -834,6 +834,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 837945eff81..0985a51997e 100644 --- a/components/layout/lib.rs +++ b/components/layout/lib.rs @@ -66,7 +66,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; @@ -82,7 +84,6 @@ pub mod table_cell; pub mod text; pub mod traversal; pub mod util; -pub mod incremental; pub mod wrapper; pub mod css { diff --git a/components/layout/list_item.rs b/components/layout/list_item.rs index eedb2d3b6b6..23748480cf1 100644 --- a/components/layout/list_item.rs +++ b/components/layout/list_item.rs @@ -8,12 +8,14 @@ #![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, FragmentMutator}; +use fragment::{GeneratedContentInfo}; +use generated_content; +use incremental::RESOLVE_GENERATED_CONTENT; use wrapper::ThreadSafeLayoutNode; use geom::{Point2D, Rect}; @@ -36,19 +38,33 @@ pub struct ListItemFlow { } impl ListItemFlow { - pub fn from_node_marker_and_flotation(constructor: &mut FlowConstructor, - node: &ThreadSafeLayoutNode, - marker_fragment: Option, - flotation: Option) - -> ListItemFlow { - ListItemFlow { + pub fn from_node_fragments_and_flotation(node: &ThreadSafeLayoutNode, + main_fragment: Fragment, + marker_fragment: Option, + flotation: Option) + -> 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 +150,55 @@ impl Flow for ListItemFlow { fn iterate_through_fragment_border_boxes(&self, iterator: &mut FragmentBorderBoxIterator, stacking_context_position: &Point2D) { - 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, + CoordinateSystem::Parent) + .translate(stacking_context_position)); + } + } + } + + fn mutate_fragments(&mut self, mutator: &mut FragmentMutator) { + self.block_flow.mutate_fragments(mutator); + + if let Some(ref mut marker) = self.marker { + mutator.process(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(&'static str), + GeneratedContent(Box), +} + +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).unwrap(); + ListStyleTypeContent::StaticText(text) + } + _ => ListStyleTypeContent::GeneratedContent(box GeneratedContentInfo::ListItem), + } } } diff --git a/components/layout/sequential.rs b/components/layout/sequential.rs index ff2bd948c51..c33441b6faf 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, @@ -119,3 +138,4 @@ pub fn iterate_through_flow_tree_fragment_border_boxes(root: &mut FlowRef, doit(&mut **root, iterator, &ZERO_POINT); } + diff --git a/components/layout/table.rs b/components/layout/table.rs index 270143a21da..4a23db3f532 100644 --- a/components/layout/table.rs +++ b/components/layout/table.rs @@ -8,12 +8,11 @@ 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}; -use flow::ImmutableFlowUtils; -use fragment::{Fragment, FragmentBorderBoxIterator}; +use flow::{ImmutableFlowUtils}; +use fragment::{Fragment, FragmentBorderBoxIterator, FragmentMutator}; use layout_debug; use model::{IntrinsicISizes, IntrinsicISizesContribution}; use table_row::CellIntrinsicInlineSize; @@ -55,12 +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 - }; + 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(), @@ -69,35 +68,17 @@ impl TableFlow { } } - 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 - }; - TableFlow { - block_flow: block_flow, - column_intrinsic_inline_sizes: Vec::new(), - column_computed_inline_sizes: Vec::new(), - table_layout: table_layout - } - } - - 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) { self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } + + fn mutate_fragments(&mut self, mutator: &mut FragmentMutator) { + 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 43eaacf9384..f892929932b 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, FragmentMutator}; 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) { self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } + + fn mutate_fragments(&mut self, mutator: &mut FragmentMutator) { + 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 670dcf7b344..2735f5294df 100644 --- a/components/layout/table_cell.rs +++ b/components/layout/table_cell.rs @@ -9,7 +9,7 @@ use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag}; use context::LayoutContext; use flow::{Flow, FlowClass}; -use fragment::{Fragment, FragmentBorderBoxIterator}; +use fragment::{Fragment, FragmentBorderBoxIterator, FragmentMutator}; use model::{MaybeAuto}; use layout_debug; use table::InternalTable; @@ -178,6 +178,10 @@ impl Flow for TableCellFlow { stacking_context_position: &Point2D) { self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } + + fn mutate_fragments(&mut self, mutator: &mut FragmentMutator) { + 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 6d19a0c5778..2bf02db4428 100644 --- a/components/layout/table_colgroup.rs +++ b/components/layout/table_colgroup.rs @@ -9,7 +9,7 @@ use context::LayoutContext; use css::node_style::StyledNode; use flow::{BaseFlow, FlowClass, Flow, ForceNonfloatedFlag}; -use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo}; +use fragment::{Fragment, FragmentBorderBoxIterator, FragmentMutator, SpecificFragmentInfo}; use layout_debug; use wrapper::ThreadSafeLayoutNode; @@ -104,6 +104,8 @@ impl Flow for TableColGroupFlow { fn iterate_through_fragment_border_boxes(&self, _: &mut FragmentBorderBoxIterator, _: &Point2D) {} + + fn mutate_fragments(&mut self, _: &mut FragmentMutator) {} } impl fmt::Debug for TableColGroupFlow { diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs index d712d7e4945..6425b63d1ea 100644 --- a/components/layout/table_row.rs +++ b/components/layout/table_row.rs @@ -8,11 +8,9 @@ use block::BlockFlow; use block::ISizeAndMarginsComputer; -use construct::FlowConstructor; use context::LayoutContext; -use flow::{FlowClass, Flow, ImmutableFlowUtils}; -use flow; -use fragment::{Fragment, FragmentBorderBoxIterator}; +use flow::{self, FlowClass, Flow, ImmutableFlowUtils}; +use fragment::{Fragment, FragmentBorderBoxIterator, FragmentMutator}; use layout_debug; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable}; use model::MaybeAuto; @@ -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) { self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } + + fn mutate_fragments(&mut self, mutator: &mut FragmentMutator) { + 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 9728fde36a8..ac86cd8987e 100644 --- a/components/layout/table_rowgroup.rs +++ b/components/layout/table_rowgroup.rs @@ -7,10 +7,9 @@ #![deny(unsafe_blocks)] use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag}; -use construct::FlowConstructor; use context::LayoutContext; use flow::{FlowClass, Flow}; -use fragment::{Fragment, FragmentBorderBoxIterator}; +use fragment::{Fragment, FragmentBorderBoxIterator, FragmentMutator}; use layout_debug; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable}; use wrapper::ThreadSafeLayoutNode; @@ -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) { self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } + + fn mutate_fragments(&mut self, mutator: &mut FragmentMutator) { + 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 e849150bede..fbe124af1bf 100644 --- a/components/layout/table_wrapper.rs +++ b/components/layout/table_wrapper.rs @@ -13,13 +13,13 @@ #![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}; use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS}; -use fragment::{Fragment, FragmentBorderBoxIterator}; +use fragment::{Fragment, FragmentBorderBoxIterator, FragmentMutator}; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize}; use wrapper::ThreadSafeLayoutNode; @@ -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) { self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } + + fn mutate_fragments(&mut self, mutator: &mut FragmentMutator) { + self.block_flow.mutate_fragments(mutator) + } } impl fmt::Debug for TableWrapperFlow { diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index 2211cf2fcce..04a99ee4b77 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,7 @@ 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::all()) } // Perform the CSS cascade. @@ -376,3 +376,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 54f341d1d09..e43603d0dc1 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, display, white_space}; use selectors::parser::{NamespaceConstraint, AttrSelector}; -use style::legacy::{LengthAttribute, SimpleColorAttribute, UnsignedIntegerAttribute, IntegerAttribute}; +use style::computed_values::content::ContentItem; +use style::computed_values::{content, display, white_space}; +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; /// Returns the first child of this node. fn first_child(&self) -> Option; @@ -214,19 +217,25 @@ impl<'ln> TLayoutNode for LayoutNode<'ln> { } } - fn text(&self) -> String { + fn text_content(&self) -> Vec { unsafe { let text: Option> = 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> = HTMLInputElementCast::to_layout_js(self.get_jsmanaged()); + let input: Option> = + 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> = HTMLTextAreaElementCast::to_layout_js(self.get_jsmanaged()); + let area: Option> = + 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 { 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![ContentItem::String("".to_owned())], } } @@ -762,7 +765,7 @@ impl<'ln> TLayoutNode for ThreadSafeLayoutNode<'ln> { } } - fn text(&self) -> String { + fn text_content(&self) -> Vec { 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 1d642acf17c..2bb10ba3ead 100644 --- a/components/script/dom/webidls/CSSStyleDeclaration.webidl +++ b/components/script/dom/webidls/CSSStyleDeclaration.webidl @@ -102,6 +102,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 overflowWrap; diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs index 2ff84b2107c..9869d478fc8 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -689,86 +689,241 @@ 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::{ToCss, Token}; + use std::ascii::AsciiExt; + use std::borrow::ToOwned; + 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 {} + + pub mod computed_value { + use super::super::list_style_type; + + use cssparser::{ToCss, Token}; + use std::borrow::IntoCow; + 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(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { + match self { + &ContentItem::String(ref s) => { + Token::QuotedString((&**s).into_cow()).to_css(dest) + } + &ContentItem::Counter(ref s, _) => { + // FIXME(pcwalton) + Token::QuotedString((&**s).into_cow()).to_css(dest) + } + &ContentItem::Counters(ref s, _, _) => { + // FIXME(pcwalton) + Token::QuotedString((&**s).into_cow()).to_css(dest) + } + &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"), + } } + } - impl ToCss for ContentItem { - fn to_css(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { - match self { - &ContentItem::StringContent(ref s) => { - Token::QuotedString((&**s).into_cow()).to_css(dest) + #[allow(non_camel_case_types)] + #[derive(PartialEq, Eq, Clone)] + pub enum T { + normal, + none, + Content(Vec), + } + + impl ToCss for T { + fn to_css(&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(()) } } } + } + } + #[inline] + pub fn get_initial_value() -> computed_value::T { + computed_value::T::normal + } - #[allow(non_camel_case_types)] - #[derive(PartialEq, Eq, Clone)] - pub enum T { - normal, - none, - Content(Vec), - } + 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") + } - impl ToCss for T { - fn to_css(&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)); + // normal | none | [ | | open-quote | close-quote | no-open-quote | + // no-close-quote ]+ + // TODO: , attr() + pub fn parse(context: &ParserContext, input: &mut Parser) + -> Result { + 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)) => { + if name.eq_ignore_ascii_case("counter") { + let (mut counter_name, mut counter_style) = (None, None); + match input.parse_nested_block(|input| { + input.parse_comma_separated(|input| { + if counter_name.is_none() { + match input.next() { + Ok(Token::Ident(value)) => { + counter_name = Some((*value).to_owned()); + Ok(()) + } + _ => Err(()) + } + } else if counter_style.is_none() { + match list_style_type::parse(context, input) { + Ok(style) => { + counter_style = Some(style); + Ok(()) + } + _ => Err(()) + } + } else { + Err(()) + } + }) + }) { + Ok(_) => { + match (counter_name, counter_style) { + (Some(name), Some(style)) => { + content.push(ContentItem::Counter(name, style)) + } + (Some(name), None) => { + content.push(ContentItem::Counter( + name, + list_style_type::computed_value::T::decimal)) + } + _ => return Err(()), + } } - Ok(()) + Err(_) => return Err(()), } + } else if name.eq_ignore_ascii_case("counters") { + let mut counter_name = None; + let mut counter_separator = None; + let mut counter_style = None; + match input.parse_nested_block(|input| { + input.parse_comma_separated(|input| { + if counter_name.is_none() { + match input.next() { + Ok(Token::Ident(value)) => { + counter_name = Some((*value).to_owned()); + Ok(()) + } + _ => Err(()) + } + } else if counter_separator.is_none() { + match input.next() { + Ok(Token::QuotedString(value)) => { + counter_separator = Some((*value).to_owned()); + Ok(()) + } + _ => Err(()) + } + } else if counter_style.is_none() { + match input.try(|input| { + list_style_type::parse(context, input) + }) { + Ok(style) => { + counter_style = Some(style); + Ok(()) + } + _ => Err(()), + } + } else { + Err(()) + } + }) + }) { + Ok(_) => { + match (counter_name, counter_separator, counter_style) { + (Some(name), Some(separator), Some(style)) => { + content.push(ContentItem::Counters(name, + separator, + style)) + } + (Some(name), Some(separator), None) => { + content.push(ContentItem::Counters( + name, + separator, + list_style_type::computed_value::T::decimal)) + } + _ => return Err(()), + } + } + Err(_) => return Err(()), + } + } else { + return Err(()) } } - } - } - #[inline] - pub fn get_initial_value() -> computed_value::T { - computed_value::T::normal - } - - // normal | none | [ ]+ - // TODO: , , attr(), open-quote, close-quote, no-open-quote, no-close-quote - pub fn parse(_context: &ParserContext, input: &mut Parser) - -> Result { - 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())) + Ok(Token::Ident(ident)) => { + if ident.eq_ignore_ascii_case("open-quote") { + content.push(ContentItem::OpenQuote) + } else if ident.eq_ignore_ascii_case("close-quote") { + content.push(ContentItem::CloseQuote) + } else if ident.eq_ignore_ascii_case("no-open-quote") { + content.push(ContentItem::NoOpenQuote) + } else if ident.eq_ignore_ascii_case("no-close-quote") { + content.push(ContentItem::NoCloseQuote) + } else { + return Err(()) } - Err(()) if !content.is_empty() => { - return Ok(SpecifiedValue::Content(content)) - } - _ => return Err(()) } + Err(()) if !content.is_empty() => { + let mut result = String::new(); + for content in content.iter() { + content.to_css(&mut result).unwrap() + } + return Ok(SpecifiedValue::Content(content)) + } + _ => return Err(()) } } + } ${new_style_struct("List", is_inherited=True)} @@ -777,14 +932,12 @@ 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")} + "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; @@ -840,6 +993,144 @@ pub mod longhands { } + <%self:longhand name="quotes"> + use cssparser::{ToCss, Token}; + use text_writer::{self, TextWriter}; + use values::computed::{ToComputedValue, Context}; + + pub use self::computed_value::T as SpecifiedValue; + + pub mod computed_value { + #[derive(Clone, PartialEq)] + pub struct T(pub Vec<(String,String)>); + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + fn to_computed_value(&self, _: &Context) -> computed_value::T { + (*self).clone() + } + } + + impl ToCss for SpecifiedValue { + fn to_css(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { + // TODO(pcwalton) + dest.write_str("") + } + } + + #[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 { + 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(()) + } + } + + + ${new_style_struct("Counters", is_inherited=False)} + + <%self:longhand name="counter-increment"> + use cssparser::{NumericValue, ToCss, Token}; + use super::content; + use text_writer::{self, TextWriter}; + use values::computed::{ToComputedValue, Context}; + + use std::borrow::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 ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + fn to_computed_value(&self, _: &Context) -> computed_value::T { + (*self).clone() + } + } + + impl ToCss for SpecifiedValue { + fn to_css(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { + // TODO(pcwalton) + dest.write_str("") + } + } + + pub fn parse(_: &ParserContext, input: &mut Parser) -> Result { + 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| { + match input.next() { + Ok(Token::Number(NumericValue { + int_value: Some(int_value), + .. + })) => Ok(int_value as i32), + _ => Err(()), + } + }).unwrap_or(1); + counters.push((counter_name, counter_delta)) + } + + if !counters.is_empty() { + Ok(SpecifiedValue(counters)) + } else { + Err(()) + } + } + + + <%self:longhand name="counter-reset"> + pub use super::counter_increment::{SpecifiedValue, computed_value, get_initial_value}; + pub use super::counter_increment::{parse}; + + // 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 c67a240d535..15370ead80b 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -902,6 +902,7 @@ dependencies = [ "regex 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "selectors 0.1.0 (git+https://github.com/servo/rust-selectors)", + "plugins 0.0.1", "string_cache 0.0.0 (git+https://github.com/servo/string-cache)", "string_cache_macros 0.0.0 (git+https://github.com/servo/string-cache)", "task_info 0.0.1", diff --git a/tests/ref/basic.list b/tests/ref/basic.list index 0d83cc16d1f..0acad1bc5ff 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -172,6 +172,11 @@ fragment=top != ../html/acid2.html acid2_ref.html == block_formatting_context_containing_floats_a.html block_formatting_context_containing_floats_ref.html == clear_generated_content_table_a.html clear_generated_content_table_ref.html == inline_block_border_a.html inline_block_border_ref.html +== counters_simple_a.html counters_simple_ref.html +== counters_nested_a.html counters_nested_ref.html +== quotes_simple_a.html quotes_simple_ref.html +== ol_simple_a.html ol_simple_ref.html +== ol_japanese_iroha_a.html ol_japanese_iroha_ref.html == vertical_align_top_a.html vertical_align_top_ref.html == vertical_align_bottom_a.html vertical_align_bottom_ref.html == vertical_align_top_span_a.html vertical_align_top_span_ref.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 @@ + + + + + + + +
+

Foo

+
+

Boo

+

Quux

+
+

Blah

+
+
+

Bar

+
+

Boo

+

Quux

+

Baz

+
+ + + 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 @@ + + + + + + + +
+

1. Foo

+
+

1.1. Boo

+

1.2. Quux

+
+

1.2.1. Blah

+
+
+

1.3. Bar

+
+

1.1. Boo

+

1.2. Quux

+

1.3. Baz

+
+ + + 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 @@ + + + + + + + +

Foo

+

Boo

+

Quux

+

Blah

+

Bar

+

Boo

+

Quux

+

Baz

+ + + 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 @@ + + + + + + + +

1. Foo

+

1.1. Boo

+

1.2. Quux

+

1.2.1. Blah

+

2. Bar

+

2.1. Boo

+

2.2. Quux

+

3. Baz

+ + + diff --git a/tests/ref/ol_japanese_iroha_a.html b/tests/ref/ol_japanese_iroha_a.html new file mode 100644 index 00000000000..92cbc34e006 --- /dev/null +++ b/tests/ref/ol_japanese_iroha_a.html @@ -0,0 +1,25 @@ + + + + + + + +
    +
  1. Gryffindor
  2. +
  3. Hufflepuff
  4. +
  5. Ravenclaw
  6. +
  7. Slytherin
  8. +
+ + + + diff --git a/tests/ref/ol_japanese_iroha_ref.html b/tests/ref/ol_japanese_iroha_ref.html new file mode 100644 index 00000000000..6accdac4b63 --- /dev/null +++ b/tests/ref/ol_japanese_iroha_ref.html @@ -0,0 +1,27 @@ + + + + + + + +
    +
  1. い. Gryffindor
  2. +
  3. ろ. Hufflepuff
  4. +
  5. は. Ravenclaw
  6. +
  7. に. Slytherin
  8. +
+ + + + 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 @@ + + + + + + +
    +
  1. Foo
  2. +
  3. Bar
  4. +
  5. Baz
  6. +
+ + + 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 @@ + + + + + + +
    +
  1. 1. Foo
  2. +
  3. 2. Bar
  4. +
  5. 3. Baz
  6. +
+ + + 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 @@ + + + + + + +I remember when I first read Hagrid said, You're a wizard, Harry! + + + 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 @@ + + + + + + +I remember when I first read “Hagrid said, ‘You're a wizard, Harry!’” + + + + From 50800940dc5c8cf424d5786c447f7ff1d0432a97 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 25 Feb 2015 15:51:23 +0100 Subject: [PATCH 05/29] Refactor content property parsing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It wasn’t wrong, but it could be a lot shorter. * Use try! and match_ignore_ascii_case! macros whenever possible * Use expect_comma() instead of parse_comma_separated() when comma-separated values don’t have the same syntax * Prefer Parser::expect_* methods over doing the same with Parser::next * Take advantage of parse_nested_block returnin the return value of the closure * Use try! more. --- components/style/properties.mako.rs | 136 ++++++---------------------- 1 file changed, 27 insertions(+), 109 deletions(-) diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs index 9869d478fc8..0dfd232cb4a 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -691,7 +691,6 @@ pub mod longhands { <%self:longhand name="content"> use cssparser::{ToCss, Token}; use std::ascii::AsciiExt; - use std::borrow::ToOwned; use values::computed::ComputedValueAsSpecified; use super::list_style_type; @@ -801,116 +800,35 @@ pub mod longhands { content.push(ContentItem::String(value.into_owned())) } Ok(Token::Function(name)) => { - if name.eq_ignore_ascii_case("counter") { - let (mut counter_name, mut counter_style) = (None, None); - match input.parse_nested_block(|input| { - input.parse_comma_separated(|input| { - if counter_name.is_none() { - match input.next() { - Ok(Token::Ident(value)) => { - counter_name = Some((*value).to_owned()); - Ok(()) - } - _ => Err(()) - } - } else if counter_style.is_none() { - match list_style_type::parse(context, input) { - Ok(style) => { - counter_style = Some(style); - Ok(()) - } - _ => Err(()) - } - } else { - Err(()) - } - }) - }) { - Ok(_) => { - match (counter_name, counter_style) { - (Some(name), Some(style)) => { - content.push(ContentItem::Counter(name, style)) - } - (Some(name), None) => { - content.push(ContentItem::Counter( - name, - list_style_type::computed_value::T::decimal)) - } - _ => return Err(()), - } - } - Err(_) => return Err(()), - } - } else if name.eq_ignore_ascii_case("counters") { - let mut counter_name = None; - let mut counter_separator = None; - let mut counter_style = None; - match input.parse_nested_block(|input| { - input.parse_comma_separated(|input| { - if counter_name.is_none() { - match input.next() { - Ok(Token::Ident(value)) => { - counter_name = Some((*value).to_owned()); - Ok(()) - } - _ => Err(()) - } - } else if counter_separator.is_none() { - match input.next() { - Ok(Token::QuotedString(value)) => { - counter_separator = Some((*value).to_owned()); - Ok(()) - } - _ => Err(()) - } - } else if counter_style.is_none() { - match input.try(|input| { - list_style_type::parse(context, input) - }) { - Ok(style) => { - counter_style = Some(style); - Ok(()) - } - _ => Err(()), - } - } else { - Err(()) - } - }) - }) { - Ok(_) => { - match (counter_name, counter_separator, counter_style) { - (Some(name), Some(separator), Some(style)) => { - content.push(ContentItem::Counters(name, - separator, - style)) - } - (Some(name), Some(separator), None) => { - content.push(ContentItem::Counters( - name, - separator, - list_style_type::computed_value::T::decimal)) - } - _ => return Err(()), - } - } - Err(_) => return Err(()), - } - } else { - return Err(()) - } + 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)) => { - if ident.eq_ignore_ascii_case("open-quote") { - content.push(ContentItem::OpenQuote) - } else if ident.eq_ignore_ascii_case("close-quote") { - content.push(ContentItem::CloseQuote) - } else if ident.eq_ignore_ascii_case("no-open-quote") { - content.push(ContentItem::NoOpenQuote) - } else if ident.eq_ignore_ascii_case("no-close-quote") { - content.push(ContentItem::NoCloseQuote) - } else { - return Err(()) + 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(()) } } Err(()) if !content.is_empty() => { From e209f5e0da5a3dd3b188d11e884b2859daf053d3 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Mar 2015 17:46:14 +0100 Subject: [PATCH 06/29] Tidy. --- components/style/properties.mako.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs index 0dfd232cb4a..7fc7b2ae515 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -854,8 +854,12 @@ pub mod longhands { // upper-roman // // [1]: http://dev.w3.org/csswg/css-counter-styles/ - ${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")} + ${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; From 09c53f461dadf0c1f2896754c329ee6ad3e9359e Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Sat, 13 Dec 2014 19:09:06 -0800 Subject: [PATCH 07/29] =?UTF-8?q?layout:=20Implement=20`image-rendering`?= =?UTF-8?q?=20per=20CSS-IMAGES-3=20=C2=A7=205.3=20and=20`background-size`?= =?UTF-8?q?=20per=20CSS-BACKGROUNDS=20=C2=A7=203.9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nearest neighbor interpolation is used for `crisp-edges`, like Firefox. A note has been added that we could do better if we wanted to. Multiple backgrounds are not yet supported. --- components/gfx/display_list/mod.rs | 11 +- components/gfx/paint_context.rs | 19 +- components/layout/display_list_builder.rs | 122 ++++++++--- .../dom/webidls/CSSStyleDeclaration.webidl | 3 + components/style/properties.mako.rs | 193 +++++++++++++++++- tests/ref/2x4.png | Bin 0 -> 171 bytes tests/ref/4x2.png | Bin 0 -> 180 bytes tests/ref/background_size.png | Bin 0 -> 510 bytes tests/ref/background_size_a.html | 62 ++++++ tests/ref/background_size_ref.html | 19 ++ tests/ref/background_size_shorthand_a.html | 28 +++ tests/ref/background_size_shorthand_ref.html | 23 +++ tests/ref/basic.list | 4 + tests/ref/image_rendering_auto_a.html | 11 + tests/ref/image_rendering_pixelated_a.html | 20 ++ tests/ref/image_rendering_pixelated_ref.html | 40 ++++ 16 files changed, 521 insertions(+), 34 deletions(-) create mode 100644 tests/ref/2x4.png create mode 100644 tests/ref/4x2.png create mode 100644 tests/ref/background_size.png create mode 100644 tests/ref/background_size_a.html create mode 100644 tests/ref/background_size_ref.html create mode 100644 tests/ref/background_size_shorthand_a.html create mode 100644 tests/ref/background_size_shorthand_ref.html create mode 100644 tests/ref/image_rendering_auto_a.html create mode 100644 tests/ref/image_rendering_pixelated_a.html create mode 100644 tests/ref/image_rendering_pixelated_ref.html diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index 9ba4d93e9e9..fde17c0aed5 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -42,8 +42,9 @@ use util::smallvec::{SmallVec, SmallVec8}; use std::fmt; use std::slice::Iter; use std::sync::Arc; +use style::computed_values::{border_style, cursor, filter, image_rendering, mix_blend_mode}; +use style::computed_values::{pointer_events}; use style::properties::ComputedValues; -use style::computed_values::{border_style, cursor, filter, mix_blend_mode, pointer_events}; // It seems cleaner to have layout code not mention Azure directly, so let's just reexport this for // layout to use. @@ -763,6 +764,10 @@ pub struct ImageDisplayItem { /// the bounds of this display item, then the image will be repeated in the appropriate /// direction to tile the entire bounds. pub stretch_size: Size2D, + + /// The algorithm we should use to stretch the image. See `image_rendering` in CSS-IMAGES-3 § + /// 5.3. + pub image_rendering: image_rendering::T, } /// Paints a gradient. @@ -937,7 +942,9 @@ impl DisplayItem { bounds.origin.y = bounds.origin.y + y_offset; bounds.size = image_item.stretch_size; - paint_context.draw_image(&bounds, image_item.image.clone()); + paint_context.draw_image(&bounds, + image_item.image.clone(), + image_item.image_rendering.clone()); x_offset = x_offset + image_item.stretch_size.width; } diff --git a/components/gfx/paint_context.rs b/components/gfx/paint_context.rs index 82184bf06de..442e13f7333 100644 --- a/components/gfx/paint_context.rs +++ b/components/gfx/paint_context.rs @@ -37,7 +37,7 @@ use std::mem; use std::num::Float; use std::ptr; use std::sync::Arc; -use style::computed_values::{border_style, filter, mix_blend_mode}; +use style::computed_values::{border_style, filter, image_rendering, mix_blend_mode}; use util::geometry::{self, Au, MAX_RECT, ZERO_RECT}; use util::opts; use util::range::Range; @@ -127,7 +127,10 @@ impl<'a> PaintContext<'a> { self.draw_target.pop_clip(); } - pub fn draw_image(&self, bounds: &Rect, image: Arc>) { + pub fn draw_image(&self, + bounds: &Rect, + image: Arc>, + image_rendering: image_rendering::T) { let size = Size2D(image.width as i32, image.height as i32); let (pixel_width, pixels, source_format) = match image.pixels { PixelsByColorType::RGBA8(ref pixels) => (4, pixels.as_slice(), SurfaceFormat::B8G8R8A8), @@ -146,7 +149,17 @@ impl<'a> PaintContext<'a> { let source_rect = Rect(Point2D(0.0, 0.0), Size2D(image.width as AzFloat, image.height as AzFloat)); let dest_rect = bounds.to_azure_rect(); - let draw_surface_options = DrawSurfaceOptions::new(Filter::Linear, true); + + // TODO(pcwalton): According to CSS-IMAGES-3 § 5.3, nearest-neighbor interpolation is a + // conforming implementation of `crisp-edges`, but it is not the best we could do. + // Something like Scale2x would be ideal. + let draw_surface_options = match image_rendering { + image_rendering::T::Auto => DrawSurfaceOptions::new(Filter::Linear, true), + image_rendering::T::CrispEdges | image_rendering::T::Pixelated => { + DrawSurfaceOptions::new(Filter::Point, true) + } + }; + let draw_options = DrawOptions::new(1.0, 0); draw_target_ref.draw_surface(azure_surface, dest_rect, diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index e20ade0ce19..e61e8728eb6 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -18,7 +18,7 @@ use fragment::{CoordinateSystem, Fragment, IframeFragmentInfo, ImageFragmentInfo use fragment::{ScannedTextFragmentInfo, SpecificFragmentInfo}; use inline::InlineFlow; use list_item::ListItemFlow; -use model; +use model::{self, MaybeAuto}; use util::{OpaqueNodeMethods, ToGfxColor}; use geom::{Point2D, Rect, Size2D, SideOffsets2D}; @@ -31,8 +31,7 @@ use gfx::display_list::{GradientStop, ImageDisplayItem, LineDisplayItem}; use gfx::display_list::{OpaqueNode, SolidColorDisplayItem}; use gfx::display_list::{StackingContext, TextDisplayItem, TextOrientation}; use gfx::paint_task::{PaintLayer, THREAD_TINT_COLORS}; -use png; -use png::PixelsByColorType; +use png::{self, PixelsByColorType}; use msg::compositor_msg::ScrollPolicy; use msg::constellation_msg::Msg as ConstellationMsg; use msg::constellation_msg::ConstellationChan; @@ -46,11 +45,11 @@ use std::default::Default; use std::iter::repeat; use std::num::Float; use style::values::specified::{AngleOrCorner, HorizontalDirection, VerticalDirection}; -use style::values::computed::{Image, LinearGradient, LengthOrPercentage}; +use style::values::computed::{Image, LinearGradient, LengthOrPercentage, LengthOrPercentageOrAuto}; use style::values::RGBA; use style::computed_values::filter::Filter; -use style::computed_values::{background_attachment, background_repeat, border_style, overflow_x}; -use style::computed_values::{position, visibility}; +use style::computed_values::{background_attachment, background_repeat, background_size}; +use style::computed_values::{border_style, image_rendering, overflow_x, position, visibility}; use style::properties::style_structs::Border; use style::properties::ComputedValues; use std::num::ToPrimitive; @@ -93,6 +92,14 @@ pub trait FragmentDisplayListBuilding { absolute_bounds: &Rect, clip: &ClippingRegion); + /// Computes the background size for an image with the given background area according to the + /// rules in CSS-BACKGROUNDS § 3.9. + fn compute_background_image_size(&self, + style: &ComputedValues, + bounds: &Rect, + image: &png::Image) + -> Size2D; + /// Adds the display items necessary to paint the background image of this fragment to the /// display list at the appropriate stacking level. fn build_display_list_for_background_image(&self, @@ -326,6 +333,59 @@ impl FragmentDisplayListBuilding for Fragment { } } + fn compute_background_image_size(&self, + style: &ComputedValues, + bounds: &Rect, + image: &png::Image) + -> Size2D { + // If `image_aspect_ratio` < `bounds_aspect_ratio`, the image is tall; otherwise, it is + // wide. + let image_aspect_ratio = (image.width as f64) / (image.height as f64); + let bounds_aspect_ratio = bounds.size.width.to_subpx() / bounds.size.height.to_subpx(); + let intrinsic_size = Size2D(Au::from_px(image.width as int), + Au::from_px(image.height as int)); + match (style.get_background().background_size.clone(), + image_aspect_ratio < bounds_aspect_ratio) { + (background_size::T::Contain, false) | (background_size::T::Cover, true) => { + Size2D(bounds.size.width, + Au::from_frac_px(bounds.size.width.to_subpx() / image_aspect_ratio)) + } + + (background_size::T::Contain, true) | (background_size::T::Cover, false) => { + Size2D(Au::from_frac_px(bounds.size.height.to_subpx() * image_aspect_ratio), + bounds.size.height) + } + + (background_size::T::Explicit(background_size::ExplicitSize { + width, + height: LengthOrPercentageOrAuto::Auto, + }), _) => { + let width = MaybeAuto::from_style(width, bounds.size.width) + .specified_or_default(intrinsic_size.width); + Size2D(width, Au::from_frac_px(width.to_subpx() / image_aspect_ratio)) + } + + (background_size::T::Explicit(background_size::ExplicitSize { + width: LengthOrPercentageOrAuto::Auto, + height + }), _) => { + let height = MaybeAuto::from_style(height, bounds.size.height) + .specified_or_default(intrinsic_size.height); + Size2D(Au::from_frac_px(height.to_subpx() * image_aspect_ratio), height) + } + + (background_size::T::Explicit(background_size::ExplicitSize { + width, + height + }), _) => { + Size2D(MaybeAuto::from_style(width, bounds.size.width) + .specified_or_default(intrinsic_size.width), + MaybeAuto::from_style(height, bounds.size.height) + .specified_or_default(intrinsic_size.height)) + } + } + } + fn build_display_list_for_background_image(&self, style: &ComputedValues, display_list: &mut DisplayList, @@ -349,16 +409,16 @@ impl FragmentDisplayListBuilding for Fragment { }; debug!("(building display list) building background image"); - let image_width = Au::from_px(image.width as int); - let image_height = Au::from_px(image.height as int); + // Use `background-size` to get the size. let mut bounds = *absolute_bounds; + let image_size = self.compute_background_image_size(style, &bounds, &**image); // Clip. // // TODO: Check the bounds to see if a clip item is actually required. let clip = clip.clone().intersect_rect(&bounds); - // Use background-attachment to get the initial virtual origin + // Use `background-attachment` to get the initial virtual origin let (virtual_origin_x, virtual_origin_y) = match background.background_attachment { background_attachment::T::scroll => { (absolute_bounds.origin.x, absolute_bounds.origin.y) @@ -368,11 +428,11 @@ impl FragmentDisplayListBuilding for Fragment { } }; - // Use background-position to get the offset + // Use `background-position` to get the offset. let horizontal_position = model::specified(background.background_position.horizontal, - bounds.size.width - image_width); + bounds.size.width - image_size.width); let vertical_position = model::specified(background.background_position.vertical, - bounds.size.height - image_height); + bounds.size.height - image_size.height); let abs_x = virtual_origin_x + horizontal_position; let abs_y = virtual_origin_y + vertical_position; @@ -382,26 +442,34 @@ impl FragmentDisplayListBuilding for Fragment { background_repeat::T::no_repeat => { bounds.origin.x = abs_x; bounds.origin.y = abs_y; - bounds.size.width = image_width; - bounds.size.height = image_height; + bounds.size.width = image_size.width; + bounds.size.height = image_size.height; } background_repeat::T::repeat_x => { bounds.origin.y = abs_y; - bounds.size.height = image_height; - ImageFragmentInfo::tile_image(&mut bounds.origin.x, &mut bounds.size.width, - abs_x, image.width); + bounds.size.height = image_size.height; + ImageFragmentInfo::tile_image(&mut bounds.origin.x, + &mut bounds.size.width, + abs_x, + image_size.width.to_nearest_px() as u32); } background_repeat::T::repeat_y => { bounds.origin.x = abs_x; - bounds.size.width = image_width; - ImageFragmentInfo::tile_image(&mut bounds.origin.y, &mut bounds.size.height, - abs_y, image.height); + bounds.size.width = image_size.width; + ImageFragmentInfo::tile_image(&mut bounds.origin.y, + &mut bounds.size.height, + abs_y, + image_size.height.to_nearest_px() as u32); } background_repeat::T::repeat => { - ImageFragmentInfo::tile_image(&mut bounds.origin.x, &mut bounds.size.width, - abs_x, image.width); - ImageFragmentInfo::tile_image(&mut bounds.origin.y, &mut bounds.size.height, - abs_y, image.height); + ImageFragmentInfo::tile_image(&mut bounds.origin.x, + &mut bounds.size.width, + abs_x, + image_size.width.to_nearest_px() as u32); + ImageFragmentInfo::tile_image(&mut bounds.origin.y, + &mut bounds.size.height, + abs_y, + image_size.height.to_nearest_px() as u32); } }; @@ -413,8 +481,8 @@ impl FragmentDisplayListBuilding for Fragment { Cursor::DefaultCursor), clip), image: image.clone(), - stretch_size: Size2D(Au::from_px(image.width as int), - Au::from_px(image.height as int)), + stretch_size: Size2D(image_size.width, image_size.height), + image_rendering: style.get_effects().image_rendering.clone(), }), level); } @@ -912,6 +980,7 @@ impl FragmentDisplayListBuilding for Fragment { (*clip).clone()), image: image.clone(), stretch_size: stacking_relative_content_box.size, + image_rendering: self.style.get_effects().image_rendering.clone(), })); } else { // No image data at all? Do nothing. @@ -947,6 +1016,7 @@ impl FragmentDisplayListBuilding for Fragment { pixels: PixelsByColorType::RGBA8(canvas_data), }), stretch_size: stacking_relative_content_box.size, + image_rendering: image_rendering::T::Auto, }; display_list.content.push_back(DisplayItem::ImageClass(canvas_display_item)); diff --git a/components/script/dom/webidls/CSSStyleDeclaration.webidl b/components/script/dom/webidls/CSSStyleDeclaration.webidl index d6a4420b2a9..46ca8424b70 100644 --- a/components/script/dom/webidls/CSSStyleDeclaration.webidl +++ b/components/script/dom/webidls/CSSStyleDeclaration.webidl @@ -38,6 +38,7 @@ partial interface CSSStyleDeclaration { [TreatNullAs=EmptyString] attribute DOMString backgroundRepeat; [TreatNullAs=EmptyString] attribute DOMString backgroundImage; [TreatNullAs=EmptyString] attribute DOMString backgroundAttachment; + [TreatNullAs=EmptyString] attribute DOMString backgroundSize; [TreatNullAs=EmptyString] attribute DOMString border; [TreatNullAs=EmptyString] attribute DOMString borderColor; @@ -173,4 +174,6 @@ partial interface CSSStyleDeclaration { [TreatNullAs=EmptyString] attribute DOMString maxWidth; [TreatNullAs=EmptyString] attribute DOMString zIndex; + + [TreatNullAs=EmptyString] attribute DOMString imageRendering; }; diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs index 83d6570a800..5330de59a93 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -1073,6 +1073,126 @@ pub mod longhands { ${single_keyword("background-attachment", "scroll fixed")} + <%self:longhand name="background-size"> + use cssparser::{ToCss, Token}; + use std::ascii::AsciiExt; + use text_writer::{self, TextWriter}; + use values::computed::{Context, ToComputedValue}; + + pub mod computed_value { + use values::computed::LengthOrPercentageOrAuto; + + #[derive(PartialEq, Clone, Debug)] + pub struct ExplicitSize { + pub width: LengthOrPercentageOrAuto, + pub height: LengthOrPercentageOrAuto, + } + + #[derive(PartialEq, Clone, Debug)] + pub enum T { + Explicit(ExplicitSize), + Cover, + Contain, + } + } + + #[derive(Clone, PartialEq, Debug)] + pub struct SpecifiedExplicitSize { + pub width: specified::LengthOrPercentageOrAuto, + pub height: specified::LengthOrPercentageOrAuto, + } + + impl ToCss for SpecifiedExplicitSize { + fn to_css(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { + try!(self.width.to_css(dest)); + try!(dest.write_str(" ")); + self.height.to_css(dest) + } + } + + #[derive(Clone, PartialEq, Debug)] + pub enum SpecifiedValue { + Explicit(SpecifiedExplicitSize), + Cover, + Contain, + } + + impl ToCss for SpecifiedValue { + fn to_css(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { + match *self { + SpecifiedValue::Explicit(ref size) => size.to_css(dest), + SpecifiedValue::Cover => dest.write_str("cover"), + SpecifiedValue::Contain => dest.write_str("contain"), + } + } + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value(&self, context: &computed::Context) -> computed_value::T { + match *self { + SpecifiedValue::Explicit(ref size) => { + computed_value::T::Explicit(computed_value::ExplicitSize { + width: size.width.to_computed_value(context), + height: size.height.to_computed_value(context), + }) + } + SpecifiedValue::Cover => computed_value::T::Cover, + SpecifiedValue::Contain => computed_value::T::Contain, + } + } + } + + #[inline] + pub fn get_initial_value() -> computed_value::T { + computed_value::T::Explicit(computed_value::ExplicitSize { + width: computed::LengthOrPercentageOrAuto::Auto, + height: computed::LengthOrPercentageOrAuto::Auto, + }) + } + + pub fn parse(_: &ParserContext, input: &mut Parser) -> Result { + let width; + if let Ok(value) = input.try(|input| { + match input.next() { + Err(_) => Err(()), + Ok(Token::Ident(ref ident)) if ident.as_slice() + .eq_ignore_ascii_case("cover") => { + Ok(SpecifiedValue::Cover) + } + Ok(Token::Ident(ref ident)) if ident.as_slice() + .eq_ignore_ascii_case("contain") => { + Ok(SpecifiedValue::Contain) + } + Ok(_) => Err(()), + } + }) { + return Ok(value) + } else { + width = try!(specified::LengthOrPercentageOrAuto::parse(input)) + } + + let height; + if let Ok(value) = input.try(|input| { + match input.next() { + Err(_) => Ok(specified::LengthOrPercentageOrAuto::Auto), + Ok(_) => Err(()), + } + }) { + height = value + } else { + height = try!(specified::LengthOrPercentageOrAuto::parse(input)); + } + + Ok(SpecifiedValue::Explicit(SpecifiedExplicitSize { + width: width, + height: height, + })) + } + + ${new_style_struct("Color", is_inherited=True)} <%self:raw_longhand name="color"> @@ -2415,6 +2535,62 @@ pub mod longhands { """normal multiply screen overlay darken lighten color-dodge color-burn hard-light soft-light difference exclusion hue saturation color luminosity""")} + + <%self:longhand name="image-rendering"> + use values::computed::{Context, ToComputedValue}; + + pub mod computed_value { + use cssparser::ToCss; + use text_writer::{self, TextWriter}; + + #[derive(Copy, Clone, Debug, PartialEq)] + pub enum T { + Auto, + CrispEdges, + Pixelated, + } + + impl ToCss for T { + fn to_css(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { + match *self { + T::Auto => dest.write_str("auto"), + T::CrispEdges => dest.write_str("crisp-edges"), + T::Pixelated => dest.write_str("pixelated"), + } + } + } + } + + pub type SpecifiedValue = computed_value::T; + + #[inline] + pub fn get_initial_value() -> computed_value::T { + computed_value::T::Auto + } + + pub fn parse(_: &ParserContext, input: &mut Parser) -> Result { + // According to to CSS-IMAGES-3, `optimizespeed` and `optimizequality` are synonyms for + // `auto`. + match_ignore_ascii_case! { + try!(input.expect_ident()), + "auto" => Ok(computed_value::T::Auto), + "optimizespeed" => Ok(computed_value::T::Auto), + "optimizequality" => Ok(computed_value::T::Auto), + "crisp-edges" => Ok(computed_value::T::CrispEdges), + "pixelated" => Ok(computed_value::T::Pixelated) + _ => Err(()) + } + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value(&self, _: &Context) -> computed_value::T { + *self + } + } + } @@ -2507,14 +2683,17 @@ pub mod shorthands { // TODO: other background-* properties <%self:shorthand name="background" - sub_properties="background-color background-position background-repeat background-attachment background-image"> - use properties::longhands::{background_color, background_position, background_repeat, - background_attachment, background_image}; + sub_properties="background-color background-position background-repeat background-attachment background-image background-size"> + use properties::longhands::{background_color, background_position, background_repeat}; + use properties::longhands::{background_attachment, background_image, background_size}; + + use cssparser::Token; let mut color = None; let mut image = None; let mut position = None; let mut repeat = None; + let mut size = None; let mut attachment = None; let mut any = false; @@ -2523,6 +2702,13 @@ pub mod shorthands { if let Ok(value) = input.try(|input| background_position::parse(context, input)) { position = Some(value); any = true; + + // Parse background size, if applicable. + size = input.try(|input| { + try!(input.expect_delim('/')); + background_size::parse(context, input) + }).ok(); + continue } } @@ -2564,6 +2750,7 @@ pub mod shorthands { background_position: position, background_repeat: repeat, background_attachment: attachment, + background_size: size, }) } else { Err(()) diff --git a/tests/ref/2x4.png b/tests/ref/2x4.png new file mode 100644 index 0000000000000000000000000000000000000000..7efc762459bd0eeda7d7ab94570d9be223c39b21 GIT binary patch literal 171 zcmeAS@N?(olHy`uVBq!ia0vp^OhC-S0V1_~EAxO9XMsm#F#`j)FbFd;%$g$s6l5>) z^mS#w$HT{HE%RdElwP2aY-UJAiF1B#Zfaf$kjuc}T$GwvlA5AWo>`Ki;O^-gkfN8$ z4is1Pba4#fxXyZTBQFC32eZN>&b(v)>q}NE++A{q@ubhecg*=^Y_%&3+75$E@pScb JS?83{1OONqEdKxi literal 0 HcmV?d00001 diff --git a/tests/ref/4x2.png b/tests/ref/4x2.png new file mode 100644 index 0000000000000000000000000000000000000000..eb0c5cda97128f52cb2815e80955cb77c1744a91 GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^EI`b}!2~2foO)#fq}Y|gW!U_%O?XxI14-? ziy0WWg+Z8+Vb&Z8pdfpRr>`sfJstreN%rjXYDa)VvY8S|xv6<2KrRD=b5Uwy zNotBhd1gt5g1e`0K#E=}J5XH0)5S4_<2vg>Mn)j7N#T$Et_6)ehCUWnax9D!405$W Onmt|pT-G@yGywpDxGS~* literal 0 HcmV?d00001 diff --git a/tests/ref/background_size.png b/tests/ref/background_size.png new file mode 100644 index 0000000000000000000000000000000000000000..8d5824940c094d591eec5f2ab62426ee1eb8dc98 GIT binary patch literal 510 zcmeAS@N?(olHy`uVBq!ia0vp^0YH3$g9%7}aQk=#NO2Z;L>4nJa0`PlBg3pY5H=O_Io@6LbAqs;^L(Y42*L)kB}CqVEC{U{En($=1uA6<`ZMDr_7t-CFr(OOFZAO+Z($ zc)qUbE4P2VcIK8RTu}4jlt1siw{yaty<4aaF&shKnEp6yx%QuZ+S=3Qd#>m6_d<>P zI2ousaS@Wp!`0LB9=)``xnbM + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/tests/ref/background_size_ref.html b/tests/ref/background_size_ref.html new file mode 100644 index 00000000000..7967b10ae09 --- /dev/null +++ b/tests/ref/background_size_ref.html @@ -0,0 +1,19 @@ + + + + + + + + + + + diff --git a/tests/ref/background_size_shorthand_a.html b/tests/ref/background_size_shorthand_a.html new file mode 100644 index 00000000000..30ce3a9cef2 --- /dev/null +++ b/tests/ref/background_size_shorthand_a.html @@ -0,0 +1,28 @@ + + + + + + + +
 
+
 
+
 
+ + + diff --git a/tests/ref/background_size_shorthand_ref.html b/tests/ref/background_size_shorthand_ref.html new file mode 100644 index 00000000000..48766310e1c --- /dev/null +++ b/tests/ref/background_size_shorthand_ref.html @@ -0,0 +1,23 @@ + + + + + + + +
 
+
 
+
 
+ + + diff --git a/tests/ref/basic.list b/tests/ref/basic.list index 2361eb2e7df..2aec5daa48d 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -259,3 +259,7 @@ fragment=top != ../html/acid2.html acid2_ref.html == text_shadow_simple_a.html text_shadow_simple_ref.html == text_shadow_decorations_a.html text_shadow_decorations_ref.html == text_shadow_blur_a.html text_shadow_blur_ref.html +!= image_rendering_auto_a.html image_rendering_pixelated_a.html +== image_rendering_pixelated_a.html image_rendering_pixelated_ref.html +== background_size_a.html background_size_ref.html +== background_size_shorthand_a.html background_size_shorthand_ref.html diff --git a/tests/ref/image_rendering_auto_a.html b/tests/ref/image_rendering_auto_a.html new file mode 100644 index 00000000000..3b7282e5ff9 --- /dev/null +++ b/tests/ref/image_rendering_auto_a.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/tests/ref/image_rendering_pixelated_a.html b/tests/ref/image_rendering_pixelated_a.html new file mode 100644 index 00000000000..b4086e2fd33 --- /dev/null +++ b/tests/ref/image_rendering_pixelated_a.html @@ -0,0 +1,20 @@ + + + + + + + + + + + + diff --git a/tests/ref/image_rendering_pixelated_ref.html b/tests/ref/image_rendering_pixelated_ref.html new file mode 100644 index 00000000000..06b2f3249f6 --- /dev/null +++ b/tests/ref/image_rendering_pixelated_ref.html @@ -0,0 +1,40 @@ + + + + + + + +
+
+
+
+ + + + + From 4c8bde5736853ae68332d1f97cc6f0b02499e35f Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Mar 2015 18:24:33 +0100 Subject: [PATCH 08/29] Remove expected failure markers on a couple of WPT reftests that now pass. --- .../the-li-element/grouping-li-reftest-002.html.ini | 5 ----- .../the-ol-element/grouping-ol-type-reftest-001.html.ini | 5 ----- 2 files changed, 10 deletions(-) delete mode 100644 tests/wpt/metadata/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002.html.ini delete mode 100644 tests/wpt/metadata/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001.html.ini 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 deleted file mode 100644 index 847b0a14070..00000000000 --- a/tests/wpt/metadata/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[grouping-li-reftest-002.html] - type: reftest - reftype: == - refurl: /html/semantics/grouping-content/the-li-element/grouping-li-reftest-002-ref.html - expected: FAIL 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 deleted file mode 100644 index beb07b038e9..00000000000 --- a/tests/wpt/metadata/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[grouping-ol-type-reftest-001.html] - type: reftest - reftype: == - refurl: /html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001-ref.html - expected: FAIL From 1c4b0dda5517627e81fe9fb2df35a7c76281b31c Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Mar 2015 18:48:17 +0100 Subject: [PATCH 09/29] Fix up libstyle unit test for background-size. --- components/style/stylesheets.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs index c0efee55d8a..1e7e78ac9cc 100644 --- a/components/style/stylesheets.rs +++ b/components/style/stylesheets.rs @@ -435,6 +435,7 @@ fn test_parse_stylesheet() { ], declarations: PropertyDeclarationBlock { normal: Arc::new(vec![ + PropertyDeclaration::BackgroundSize(DeclaredValue::Initial), PropertyDeclaration::BackgroundImage(DeclaredValue::Initial), PropertyDeclaration::BackgroundAttachment(DeclaredValue::Initial), PropertyDeclaration::BackgroundRepeat(DeclaredValue::Initial), From e1b5acb56c1c6efc1cf4235efbc7feeaac5fc0e7 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Mar 2015 21:15:49 +0100 Subject: [PATCH 10/29] Revert "Remove expected failure markers on a couple of WPT reftests that now pass." This reverts commit 4c8bde5736853ae68332d1f97cc6f0b02499e35f. --- .../the-li-element/grouping-li-reftest-002.html.ini | 5 +++++ .../the-ol-element/grouping-ol-type-reftest-001.html.ini | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 tests/wpt/metadata/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002.html.ini create mode 100644 tests/wpt/metadata/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001.html.ini 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 new file mode 100644 index 00000000000..847b0a14070 --- /dev/null +++ b/tests/wpt/metadata/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002.html.ini @@ -0,0 +1,5 @@ +[grouping-li-reftest-002.html] + type: reftest + reftype: == + refurl: /html/semantics/grouping-content/the-li-element/grouping-li-reftest-002-ref.html + expected: FAIL 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 new file mode 100644 index 00000000000..beb07b038e9 --- /dev/null +++ b/tests/wpt/metadata/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001.html.ini @@ -0,0 +1,5 @@ +[grouping-ol-type-reftest-001.html] + type: reftest + reftype: == + refurl: /html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001-ref.html + expected: FAIL From e590f9b7f4167ed9a2b46d97b282b99a0fa47d5f Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Mar 2015 21:16:01 +0100 Subject: [PATCH 11/29] Revert "Tidy." This reverts commit e209f5e0da5a3dd3b188d11e884b2859daf053d3. --- components/style/properties.mako.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs index b15c3150206..6cb61bdc16e 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -923,12 +923,8 @@ pub mod longhands { // upper-roman // // [1]: http://dev.w3.org/csswg/css-counter-styles/ - ${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 - """)} + ${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; From 7a218b3f08c95b5c1a14ef1bbf09658d61b8d342 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Mar 2015 21:16:03 +0100 Subject: [PATCH 12/29] Revert "Refactor content property parsing." This reverts commit 50800940dc5c8cf424d5786c447f7ff1d0432a97. --- components/style/properties.mako.rs | 136 ++++++++++++++++++++++------ 1 file changed, 109 insertions(+), 27 deletions(-) diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs index 6cb61bdc16e..b3ccef6f95a 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -760,6 +760,7 @@ pub mod longhands { <%self:longhand name="content"> use cssparser::{ToCss, Token}; use std::ascii::AsciiExt; + use std::borrow::ToOwned; use values::computed::ComputedValueAsSpecified; use super::list_style_type; @@ -869,35 +870,116 @@ pub mod longhands { 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(()) - })); + if name.eq_ignore_ascii_case("counter") { + let (mut counter_name, mut counter_style) = (None, None); + match input.parse_nested_block(|input| { + input.parse_comma_separated(|input| { + if counter_name.is_none() { + match input.next() { + Ok(Token::Ident(value)) => { + counter_name = Some((*value).to_owned()); + Ok(()) + } + _ => Err(()) + } + } else if counter_style.is_none() { + match list_style_type::parse(context, input) { + Ok(style) => { + counter_style = Some(style); + Ok(()) + } + _ => Err(()) + } + } else { + Err(()) + } + }) + }) { + Ok(_) => { + match (counter_name, counter_style) { + (Some(name), Some(style)) => { + content.push(ContentItem::Counter(name, style)) + } + (Some(name), None) => { + content.push(ContentItem::Counter( + name, + list_style_type::computed_value::T::decimal)) + } + _ => return Err(()), + } + } + Err(_) => return Err(()), + } + } else if name.eq_ignore_ascii_case("counters") { + let mut counter_name = None; + let mut counter_separator = None; + let mut counter_style = None; + match input.parse_nested_block(|input| { + input.parse_comma_separated(|input| { + if counter_name.is_none() { + match input.next() { + Ok(Token::Ident(value)) => { + counter_name = Some((*value).to_owned()); + Ok(()) + } + _ => Err(()) + } + } else if counter_separator.is_none() { + match input.next() { + Ok(Token::QuotedString(value)) => { + counter_separator = Some((*value).to_owned()); + Ok(()) + } + _ => Err(()) + } + } else if counter_style.is_none() { + match input.try(|input| { + list_style_type::parse(context, input) + }) { + Ok(style) => { + counter_style = Some(style); + Ok(()) + } + _ => Err(()), + } + } else { + Err(()) + } + }) + }) { + Ok(_) => { + match (counter_name, counter_separator, counter_style) { + (Some(name), Some(separator), Some(style)) => { + content.push(ContentItem::Counters(name, + separator, + style)) + } + (Some(name), Some(separator), None) => { + content.push(ContentItem::Counters( + name, + separator, + list_style_type::computed_value::T::decimal)) + } + _ => return Err(()), + } + } + Err(_) => return Err(()), + } + } else { + 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(()) + if ident.eq_ignore_ascii_case("open-quote") { + content.push(ContentItem::OpenQuote) + } else if ident.eq_ignore_ascii_case("close-quote") { + content.push(ContentItem::CloseQuote) + } else if ident.eq_ignore_ascii_case("no-open-quote") { + content.push(ContentItem::NoOpenQuote) + } else if ident.eq_ignore_ascii_case("no-close-quote") { + content.push(ContentItem::NoCloseQuote) + } else { + return Err(()) } } Err(()) if !content.is_empty() => { From 4c1d778ced267eeef790d4166e361d9348b933d3 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Mar 2015 21:16:24 +0100 Subject: [PATCH 13/29] Revert "layout: Implement ordered lists, CSS counters, and `quotes` per CSS 2.1" This reverts commit 30fd28d1077fbb3f47140f6ab1252c0d24f44d23. --- components/layout/block.rs | 39 +- components/layout/construct.rs | 252 ++++---- components/layout/display_list_builder.rs | 1 - components/layout/flow.rs | 118 ++-- components/layout/fragment.rs | 162 ++--- components/layout/generated_content.rs | 573 ------------------ components/layout/incremental.rs | 48 +- components/layout/inline.rs | 22 +- components/layout/layout_task.rs | 8 - components/layout/lib.rs | 3 +- components/layout/list_item.rs | 97 +-- components/layout/sequential.rs | 24 +- components/layout/table.rs | 61 +- components/layout/table_caption.rs | 14 +- components/layout/table_cell.rs | 6 +- components/layout/table_colgroup.rs | 4 +- components/layout/table_row.rs | 23 +- components/layout/table_rowgroup.rs | 16 +- components/layout/table_wrapper.rs | 27 +- components/layout/traversal.rs | 5 +- components/layout/wrapper.rs | 47 +- .../dom/webidls/CSSStyleDeclaration.webidl | 5 - components/style/properties.mako.rs | 427 +++---------- components/util/time.rs | 3 - ports/cef/Cargo.lock | 1 - tests/ref/basic.list | 5 - tests/ref/counters_nested_a.html | 36 -- tests/ref/counters_nested_ref.html | 29 - tests/ref/counters_simple_a.html | 42 -- tests/ref/counters_simple_ref.html | 22 - tests/ref/ol_japanese_iroha_a.html | 25 - tests/ref/ol_japanese_iroha_ref.html | 27 - tests/ref/ol_simple_a.html | 19 - tests/ref/ol_simple_ref.html | 19 - tests/ref/quotes_simple_a.html | 10 - tests/ref/quotes_simple_ref.html | 11 - 36 files changed, 496 insertions(+), 1735 deletions(-) delete mode 100644 components/layout/generated_content.rs delete mode 100644 tests/ref/counters_nested_a.html delete mode 100644 tests/ref/counters_nested_ref.html delete mode 100644 tests/ref/counters_simple_a.html delete mode 100644 tests/ref/counters_simple_ref.html delete mode 100644 tests/ref/ol_japanese_iroha_a.html delete mode 100644 tests/ref/ol_japanese_iroha_ref.html delete mode 100644 tests/ref/ol_simple_a.html delete mode 100644 tests/ref/ol_simple_ref.html delete mode 100644 tests/ref/quotes_simple_a.html delete mode 100644 tests/ref/quotes_simple_ref.html diff --git a/components/layout/block.rs b/components/layout/block.rs index a6a4a3c07a1..822ec6bfb82 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -27,6 +27,7 @@ #![deny(unsafe_blocks)] +use construct::FlowConstructor; use context::LayoutContext; use css::node_style::StyledNode; use display_list_builder::{BlockFlowDisplayListBuilding, FragmentDisplayListBuilding}; @@ -39,8 +40,7 @@ use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS}; use flow::{LAYERS_NEEDED_FOR_DESCENDANTS, NEEDS_LAYER}; use flow::{IS_ABSOLUTELY_POSITIONED}; use flow::{CLEARS_LEFT, CLEARS_RIGHT}; -use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, FragmentMutator}; -use fragment::{SpecificFragmentInfo}; +use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo}; use incremental::{REFLOW, REFLOW_OUT_OF_FLOW}; use layout_debug; use model::{IntrinsicISizes, MarginCollapseInfo}; @@ -569,6 +569,20 @@ 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 { @@ -583,6 +597,23 @@ 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) @@ -1888,10 +1919,6 @@ impl Flow for BlockFlow { CoordinateSystem::Parent) .translate(stacking_context_position)); } - - fn mutate_fragments(&mut self, mutator: &mut FragmentMutator) { - mutator.process(&mut self.fragment) - } } impl fmt::Debug for BlockFlow { diff --git a/components/layout/construct.rs b/components/layout/construct.rs index 82c941b4d67..b2ea8090ea6 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -13,27 +13,33 @@ #![deny(unsafe_blocks)] +use css::node_style::StyledNode; use block::BlockFlow; use context::LayoutContext; -use css::node_style::StyledNode; use floats::FloatKind; -use flow::{self, AbsDescendants, Descendants, Flow, IS_ABSOLUTELY_POSITIONED, ImmutableFlowUtils}; -use flow::{MutableOwnedFlowUtils}; +use flow::{Flow, ImmutableFlowUtils, MutableOwnedFlowUtils}; +use flow::{Descendants, AbsDescendants}; +use flow::{IS_ABSOLUTELY_POSITIONED}; +use flow; use flow_ref::FlowRef; -use fragment::{CanvasFragmentInfo, Fragment, GeneratedContentInfo, IframeFragmentInfo}; -use fragment::{ImageFragmentInfo, InlineAbsoluteHypotheticalFragmentInfo, InlineBlockFragmentInfo}; -use fragment::{SpecificFragmentInfo, TableColumnFragmentInfo, UnscannedTextFragmentInfo}; +use fragment::{Fragment, IframeFragmentInfo}; +use fragment::ImageFragmentInfo; +use fragment::CanvasFragmentInfo; +use fragment::InlineAbsoluteHypotheticalFragmentInfo; +use fragment::{InlineBlockFragmentInfo, SpecificFragmentInfo}; +use fragment::TableColumnFragmentInfo; +use fragment::UnscannedTextFragmentInfo; use incremental::{RECONSTRUCT_FLOW, RestyleDamage}; use inline::InlineFlow; -use list_item::{ListItemFlow, ListStyleTypeContent}; +use list_item::{self, ListItemFlow}; use parallel; +use table_wrapper::TableWrapperFlow; use table::TableFlow; use table_caption::TableCaptionFlow; -use table_cell::TableCellFlow; use table_colgroup::TableColGroupFlow; -use table_row::TableRowFlow; use table_rowgroup::TableRowGroupFlow; -use table_wrapper::TableWrapperFlow; +use table_row::TableRowFlow; +use table_cell::TableCellFlow; use text::TextRunScanner; use util::{HAS_NEWLY_CONSTRUCTED_FLOW, LayoutDataAccess, OpaqueNodeMethods, LayoutDataWrapper}; use wrapper::{PostorderNodeMutTraversal, PseudoElementType, TLayoutNode, ThreadSafeLayoutNode}; @@ -48,10 +54,9 @@ 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::{self, ComputedValues}; +use style::properties::{ComputedValues, make_inline}; use std::sync::Arc; use url::Url; @@ -245,51 +250,41 @@ impl<'a> FlowConstructor<'a> { } } - /// 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))) => { + /// 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))) => { 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::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::Text) => SpecificFragmentInfo::UnscannedText(UnscannedTextFragmentInfo::new(node)), + 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 @@ -329,9 +324,7 @@ 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()) } @@ -465,8 +458,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, @@ -562,22 +555,18 @@ impl<'a> FlowConstructor<'a> { 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))) { + 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() { kid.set_flow_construction_result(ConstructionResult::None) } } - - let mut fragments = DList::new(); - self.create_fragments_for_node_text_content(&mut fragments, node, node.style()); - fragments.into_iter().next() + Some(Fragment::new_from_specific_info( + node, + SpecificFragmentInfo::UnscannedText(UnscannedTextFragmentInfo::new(node)))) } else { None }; @@ -585,39 +574,12 @@ impl<'a> FlowConstructor<'a> { self.build_flow_for_block_starting_with_fragment(flow, node, initial_fragment) } - /// 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, - node: &ThreadSafeLayoutNode, - style: &Arc) { - 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_and_fragment(node, self.build_fragment_for_block(node)) - as Box; + let flow = box BlockFlow::from_node(self, node) as Box; self.build_flow_for_block(FlowRef::new(flow), node) } @@ -625,9 +587,7 @@ impl<'a> FlowConstructor<'a> { /// a `BlockFlow` underneath it. fn build_flow_for_floated_block(&mut self, node: &ThreadSafeLayoutNode, float_kind: FloatKind) -> ConstructionResult { - let fragment = self.build_fragment_for_block(node); - let flow = box BlockFlow::float_from_node_and_fragment(node, fragment, float_kind) as - Box; + let flow = box BlockFlow::float_from_node(self, node, float_kind) as Box; self.build_flow_for_block(FlowRef::new(flow), node) } @@ -695,8 +655,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, @@ -750,22 +710,30 @@ 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(properties::make_inline(&*style)) + style = Arc::new(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(); - 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), - } + fragments.push_back(fragment); let construction_item = ConstructionItem::InlineFragments(InlineFragmentsConstructionResult { @@ -786,7 +754,7 @@ impl<'a> FlowConstructor<'a> { let fragment_info = SpecificFragmentInfo::InlineBlock(InlineBlockFragmentInfo::new( block_flow)); - let fragment = Fragment::new(node, fragment_info); + let fragment = Fragment::new_from_specific_info(node, fragment_info); let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node); fragment_accumulator.fragments.push_back(fragment); @@ -812,7 +780,7 @@ impl<'a> FlowConstructor<'a> { let fragment_info = SpecificFragmentInfo::InlineAbsoluteHypothetical( InlineAbsoluteHypotheticalFragmentInfo::new(block_flow)); - let fragment = Fragment::new(node, fragment_info); + let fragment = Fragment::new_from_specific_info(node, fragment_info); let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node); fragment_accumulator.fragments.push_back(fragment); @@ -897,7 +865,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(node, SpecificFragmentInfo::TableWrapper); + let fragment = Fragment::new_from_specific_info(node, SpecificFragmentInfo::TableWrapper); let wrapper_flow = match float_value { float::T::none => box TableWrapperFlow::from_node_and_fragment(node, fragment), _ => { @@ -907,7 +875,7 @@ impl<'a> FlowConstructor<'a> { }; let mut wrapper_flow = FlowRef::new(wrapper_flow as Box); - let table_fragment = Fragment::new(node, SpecificFragmentInfo::Table); + let table_fragment = Fragment::new_from_specific_info(node, SpecificFragmentInfo::Table); let table_flow = box TableFlow::from_node_and_fragment(node, table_fragment); let table_flow = FlowRef::new(table_flow as Box); @@ -941,8 +909,7 @@ 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); @@ -965,8 +932,7 @@ 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 fragment = self.build_fragment_for_block(node); - let flow = box TableCaptionFlow::from_node_and_fragment(node, fragment) as Box; + let flow = box TableCaptionFlow::from_node(self, node) as Box; self.build_flow_for_block(FlowRef::new(flow), node) } @@ -974,7 +940,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(node, SpecificFragmentInfo::TableRow); + let fragment = Fragment::new_from_specific_info(node, SpecificFragmentInfo::TableRow); let flow = box TableRowGroupFlow::from_node_and_fragment(node, fragment); let flow = flow as Box; self.build_flow_for_block(FlowRef::new(flow), node) @@ -983,7 +949,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(node, SpecificFragmentInfo::TableRow); + let fragment = Fragment::new_from_specific_info(node, SpecificFragmentInfo::TableRow); let flow = box TableRowFlow::from_node_and_fragment(node, fragment) as Box; self.build_flow_for_block(FlowRef::new(flow), node) } @@ -991,7 +957,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(node, SpecificFragmentInfo::TableCell); + let fragment = Fragment::new_from_specific_info(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 @@ -1019,18 +985,19 @@ impl<'a> FlowConstructor<'a> { }; let marker_fragment = match node.style().get_list().list_style_image { Some(ref url) => { - Some(Fragment::new(node, - self.build_fragment_info_for_image(node, Some((*url).clone())))) + Some(Fragment::new_from_specific_info( + node, + self.build_fragment_info_for_image(node, Some((*url).clone())))) } None => { - match ListStyleTypeContent::from_list_style_type(node.style() + match list_item::static_text_for_list_style_type(node.style() .get_list() .list_style_type) { - ListStyleTypeContent::None => None, - ListStyleTypeContent::StaticText(text) => { + None => None, + Some(text) => { let text = text.to_owned(); let mut unscanned_marker_fragments = DList::new(); - unscanned_marker_fragments.push_back(Fragment::new( + unscanned_marker_fragments.push_back(Fragment::new_from_specific_info( node, SpecificFragmentInfo::UnscannedText( UnscannedTextFragmentInfo::from_text(text)))); @@ -1040,9 +1007,6 @@ 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))) - } } } }; @@ -1054,20 +1018,19 @@ impl<'a> FlowConstructor<'a> { // there. let flow; let initial_fragment; - let main_fragment = self.build_fragment_for_block(node); match node.style().get_list().list_style_position { list_style_position::T::outside => { - flow = box ListItemFlow::from_node_fragments_and_flotation(node, - main_fragment, - marker_fragment, - flotation); + flow = box ListItemFlow::from_node_marker_and_flotation(self, + node, + marker_fragment, + flotation); initial_fragment = None; } list_style_position::T::inside => { - flow = box ListItemFlow::from_node_fragments_and_flotation(node, - main_fragment, - None, - flotation); + flow = box ListItemFlow::from_node_marker_and_flotation(self, + node, + None, + flotation); initial_fragment = marker_fragment; } } @@ -1086,8 +1049,9 @@ impl<'a> FlowConstructor<'a> { } let specific = SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node)); - let construction_item = ConstructionItem::TableColumnFragment(Fragment::new(node, - specific)); + let construction_item = ConstructionItem::TableColumnFragment( + Fragment::new_from_specific_info(node, specific) + ); ConstructionResult::ConstructionItem(construction_item) } @@ -1095,9 +1059,9 @@ impl<'a> FlowConstructor<'a> { /// This yields a `TableColGroupFlow`. fn build_flow_for_table_colgroup(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult { - let fragment = - Fragment::new(node, - SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node))); + let fragment = Fragment::new_from_specific_info( + 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` @@ -1113,7 +1077,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(node, specific)); + col_fragments.push(Fragment::new_from_specific_info(node, specific)); } let flow = box TableColGroupFlow::from_node_and_fragments(node, fragment, col_fragments); let mut flow = FlowRef::new(flow as Box); @@ -1329,29 +1293,25 @@ 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) | - 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(), + 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(_)) => false, } } diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 58eabd2580b..e61e8728eb6 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -953,7 +953,6 @@ 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 b9dacc872d1..19f1df86c3a 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -32,10 +32,10 @@ use display_list_builder::DisplayListBuildingResult; use floats::Floats; use flow_list::{FlowList, FlowListIterator, MutFlowListIterator}; use flow_ref::FlowRef; -use fragment::{Fragment, FragmentBorderBoxIterator, FragmentMutator, SpecificFragmentInfo}; -use incremental::{self, RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW, RestyleDamage}; +use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo}; +use incremental::{RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW, RestyleDamage}; use inline::InlineFlow; -use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo}; +use model::{CollapsibleMargins, IntrinsicISizes}; use parallel::FlowParallelInfo; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, TableFlow}; use table_caption::TableCaptionFlow; @@ -235,15 +235,6 @@ pub trait Flow: fmt::Debug + Sync { iterator: &mut FragmentBorderBoxIterator, stacking_context_position: &Point2D); - /// Mutably iterates through fragments in this flow. - fn mutate_fragments(&mut self, mutator: &mut FragmentMutator); - - 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) {} @@ -483,67 +474,51 @@ 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: u32 { + flags FlowFlags: u16 { // 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_0000_0001, + const HAS_LEFT_FLOATED_DESCENDANTS = 0b0000_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_0000_0010, + const HAS_RIGHT_FLOATED_DESCENDANTS = 0b0000_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_0000_0100, + const IMPACTED_BY_LEFT_FLOATS = 0b0000_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_0000_1000, + const IMPACTED_BY_RIGHT_FLOATS = 0b0000_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_0000_0001_0000, + const LAYERS_NEEDED_FOR_DESCENDANTS = 0b0000_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_0000_0010_0000, + const NEEDS_LAYER = 0b0000_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_0000_0100_0000, + const IS_ABSOLUTELY_POSITIONED = 0b0000_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_0000_1000_0000, + const CLEARS_LEFT = 0b0000_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_0000_0001_0000_0000, + const CLEARS_RIGHT = 0b0000_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_0000_0010_0000_0000, + const FLOATS_LEFT = 0b0000_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_0000_0100_0000_0000, + const FLOATS_RIGHT = 0b0000_0100_0000_0000, #[doc = "Text alignment. \ NB: If you update this, update `TEXT_ALIGN_SHIFT` below."] - 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 + const TEXT_ALIGN = 0b0111_1000_0000_0000, } } @@ -569,13 +544,13 @@ impl FlowFlags { #[inline] pub fn text_align(self) -> text_align::T { - FromPrimitive::from_u32((self & TEXT_ALIGN).bits() >> TEXT_ALIGN_SHIFT).unwrap() + FromPrimitive::from_u16((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 u32) << TEXT_ALIGN_SHIFT).unwrap(); + FlowFlags::from_bits((value as u16) << TEXT_ALIGN_SHIFT).unwrap(); } #[inline] @@ -895,41 +870,39 @@ impl BaseFlow { force_nonfloated: ForceNonfloatedFlag) -> BaseFlow { let mut flags = FlowFlags::empty(); - 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) + 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 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 = incremental::all(); + let mut damage = RestyleDamage::all(); damage.remove(RECONSTRUCT_FLOW); BaseFlow { @@ -1003,12 +976,10 @@ impl BaseFlow { } impl<'a> ImmutableFlowUtils for &'a (Flow + 'a) { - /// Returns true if this flow is a block flow or subclass thereof. + /// Returns true if this flow is a block flow. fn is_block_like(self) -> bool { match self.class() { - FlowClass::Block | FlowClass::ListItem | FlowClass::Table | FlowClass::TableRowGroup | - FlowClass::TableRow | FlowClass::TableCaption | FlowClass::TableCell | - FlowClass::TableWrapper => true, + FlowClass::Block => true, _ => false, } } @@ -1343,4 +1314,3 @@ impl ContainingBlockLink { } } } - diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 88204e5042c..26a91139b31 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -8,12 +8,13 @@ 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::{self, RestyleDamage}; +use incremental::RestyleDamage; use inline::{InlineFragmentContext, InlineMetrics}; use layout_debug; use model::{IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, specified}; @@ -27,11 +28,11 @@ use geom::{Point2D, Rect, Size2D}; use gfx::display_list::{BLUR_INFLATION_FACTOR, OpaqueNode}; use gfx::text::glyph::CharIndex; use gfx::text::text_run::{TextRun, TextRunSlice}; +use script_traits::UntrustedNodeAddress; +use rustc_serialize::{Encodable, Encoder}; use msg::constellation_msg::{PipelineId, SubpageId}; use net::image::holder::ImageHolder; use net::local_image_cache::LocalImageCache; -use rustc_serialize::{Encodable, Encoder}; -use script_traits::UntrustedNodeAddress; use servo_util::geometry::{self, Au, ZERO_POINT}; use servo_util::logical_geometry::{LogicalRect, LogicalSize, LogicalMargin}; use servo_util::range::*; @@ -43,17 +44,15 @@ use std::collections::DList; use std::fmt; use std::num::ToPrimitive; use std::str::FromStr; -use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; +use std::sync::mpsc::Sender; use string_cache::Atom; -use style::computed_values::content::ContentItem; +use style::properties::{ComputedValues, cascade_anonymous, make_border}; +use style::node::{TElement, TNode}; +use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; 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; @@ -137,11 +136,6 @@ 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), - Iframe(Box), Image(Box), Canvas(Box), @@ -164,18 +158,17 @@ impl SpecificFragmentInfo { fn restyle_damage(&self) -> RestyleDamage { let flow = match *self { - 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::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::InlineAbsoluteHypothetical(ref info) => &info.flow_ref, SpecificFragmentInfo::InlineBlock(ref info) => &info.flow_ref, }; @@ -187,12 +180,9 @@ 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", @@ -206,11 +196,8 @@ 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); @@ -220,13 +207,6 @@ fn clamp_size(size: Au, }) } -/// 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;`. /// @@ -657,6 +637,14 @@ 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 { @@ -690,8 +678,34 @@ impl TableColumnFragmentInfo { } impl Fragment { - /// Constructs a new `Fragment` instance. - pub fn new(node: &ThreadSafeLayoutNode, specific: SpecificFragmentInfo) -> 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 { let style = node.style().clone(); let writing_mode = style.writing_mode; Fragment { @@ -707,6 +721,24 @@ 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) @@ -805,7 +837,7 @@ impl Fragment { Fragment { node: self.node, style: self.style.clone(), - restyle_damage: incremental::all(), + restyle_damage: RestyleDamage::all(), border_box: new_border_box, border_padding: self.border_padding, margin: self.margin, @@ -880,7 +912,6 @@ impl Fragment { match self.specific { SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Generic | - SpecificFragmentInfo::GeneratedContent(_) | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::InlineBlock(_) => { @@ -900,9 +931,7 @@ 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() } @@ -1191,14 +1220,6 @@ 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 { @@ -1212,7 +1233,6 @@ impl Fragment { let mut result = self.style_specified_intrinsic_inline_size(); match self.specific { SpecificFragmentInfo::Generic | - SpecificFragmentInfo::GeneratedContent(_) | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell | @@ -1282,7 +1302,6 @@ impl Fragment { pub fn content_inline_size(&self) -> Au { match self.specific { SpecificFragmentInfo::Generic | - SpecificFragmentInfo::GeneratedContent(_) | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell | @@ -1314,7 +1333,6 @@ 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 | @@ -1333,7 +1351,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!") @@ -1363,7 +1381,6 @@ impl Fragment { match self.specific { SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Generic | - SpecificFragmentInfo::GeneratedContent(_) | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::Table | @@ -1671,23 +1688,22 @@ 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::Iframe(_) | + SpecificFragmentInfo::ScannedText(_) | SpecificFragmentInfo::InlineBlock(_) | SpecificFragmentInfo::InlineAbsoluteHypothetical(_) | - SpecificFragmentInfo::ScannedText(_) => {} + SpecificFragmentInfo::Iframe(_) => {} }; let style = self.style().clone(); @@ -1751,23 +1767,22 @@ 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::ScannedText(_) => {} + SpecificFragmentInfo::Iframe(_) => {} } let style = self.style().clone(); @@ -1905,7 +1920,6 @@ impl Fragment { SpecificFragmentInfo::TableWrapper => false, SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Generic | - SpecificFragmentInfo::GeneratedContent(_) | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::ScannedText(_) | @@ -2119,9 +2133,3 @@ fn strip_trailing_whitespace(text_run: &TextRun, range: &mut Range) - return true } -/// A mutable iterator over fragments. -pub trait FragmentMutator { - /// The operation to perform. - fn process(&mut self, fragment: &mut Fragment); -} - diff --git a/components/layout/generated_content.rs b/components/layout/generated_content.rs deleted file mode 100644 index d4ec6e5a534..00000000000 --- a/components/layout/generated_content.rs +++ /dev/null @@ -1,573 +0,0 @@ -/* 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, FragmentMutator, GeneratedContentInfo, SpecificFragmentInfo}; -use fragment::{UnscannedTextFragmentInfo}; -use incremental::{self, RESOLVE_GENERATED_CONTENT}; -use text::TextRunScanner; - -use gfx::display_list::OpaqueNode; -use servo_util::smallvec::{SmallVec, SmallVec8}; -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; - -// 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, - /// 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 mutator); - } - - #[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> FragmentMutator for ResolveGeneratedContentFragmentMutator<'a,'b> { - fn process(&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, - list_style_type)) => { - 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(), - list_style_type, - RenderingMode::Plain) - } - GeneratedContentInfo::ContentItem(ContentItem::Counters(ref counter_name, - ref separator, - list_style_type)) => { - 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(), - list_style_type, - 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 - } - } -} - -impl<'a,'b> ResolveGeneratedContentFragmentMutator<'a,'b> { - 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, -} - -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. - match self.values.last_mut() { - Some(ref mut existing_value) 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) { - let mut position = None; - for (i, value) in self.values.iter().enumerate() { - if value.level > level { - position = Some(i); - break - } - } - - if let Some(position) = position { - 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, - list_style_type: list_style_type::T, - mode: RenderingMode) - -> Option { - 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, - 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::all(), - 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_str(static_representation(list_style_type).unwrap()) - } - 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 string that represents the value rendered using the given list-style, if -/// possible. -pub fn static_representation(list_style_type: list_style_type::T) -> Option<&'static str> { - match list_style_type { - list_style_type::T::disc => Some("•\u{00a0}"), - list_style_type::T::circle => Some("◦\u{00a0}"), - list_style_type::T::square => Some("▪\u{00a0}"), - list_style_type::T::disclosure_open => Some("▾\u{00a0}"), - list_style_type::T::disclosure_closed => Some("‣\u{00a0}"), - _ => None, - } -} - -/// 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 i in range(0, string.len()).rev() { - accumulator.push(*string.get(i)) - } -} - diff --git a/components/layout/incremental.rs b/components/layout/incremental.rs index 2bf7a67ee7a..b163a8fbf29 100644 --- a/components/layout/incremental.rs +++ b/components/layout/incremental.rs @@ -2,7 +2,8 @@ * 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, AFFECTS_COUNTERS, Flow, HAS_COUNTER_AFFECTING_CHILDREN, IS_ABSOLUTELY_POSITIONED}; +use flow::{self, Flow}; +use flow::{IS_ABSOLUTELY_POSITIONED}; use std::fmt; use std::sync::Arc; @@ -31,12 +32,8 @@ 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 = 0x20 + const RECONSTRUCT_FLOW = 0x10 } } @@ -53,9 +50,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 | RESOLVE_GENERATED_CONTENT) + self & (REPAINT | REFLOW_OUT_OF_FLOW) } else { - self & (REPAINT | REFLOW | REFLOW_OUT_OF_FLOW | RESOLVE_GENERATED_CONTENT) + self & (REPAINT | REFLOW | REFLOW_OUT_OF_FLOW) } } @@ -94,11 +91,10 @@ 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") - , (RESOLVE_GENERATED_CONTENT, "ResolveGeneratedContent") + , (REFLOW, "Reflow") , (RECONSTRUCT_FLOW, "ReconstructFlow") ]; @@ -130,18 +126,10 @@ macro_rules! add_if_not_equal( }) ); -/// Returns a bitmask that represents a fully damaged flow. -/// -/// Use this instead of `RestyleDamage::all()` because `RestyleDamage::all()` will result in -/// unnecessary sequential resolution of generated content. -pub fn all() -> RestyleDamage { - REPAINT | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW | RECONSTRUCT_FLOW -} - pub fn compute_damage(old: &Option>, new: &ComputedValues) -> RestyleDamage { let old: &ComputedValues = match old.as_ref() { - None => return all(), + None => return RestyleDamage::all(), Some(cv) => &**cv, }; @@ -198,9 +186,6 @@ 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() { @@ -214,32 +199,21 @@ 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::mut_base(self); + let self_base = flow::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(all()); + self_base.restyle_damage.insert(RestyleDamage::all()); 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 5c25d5ab00f..0a4b575c3c4 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -11,10 +11,10 @@ use floats::{FloatKind, Floats, PlacementInfo}; use flow::{BaseFlow, FlowClass, Flow, MutableFlowUtils, ForceNonfloatedFlag}; use flow::{IS_ABSOLUTELY_POSITIONED}; use flow; -use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, FragmentMutator}; -use fragment::{ScannedTextFragmentInfo, SpecificFragmentInfo}; +use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, ScannedTextFragmentInfo}; +use fragment::{SpecificFragmentInfo}; use fragment::SplitInfo; -use incremental::{REFLOW, REFLOW_OUT_OF_FLOW, RESOLVE_GENERATED_CONTENT}; +use incremental::{REFLOW, REFLOW_OUT_OF_FLOW}; use layout_debug; use model::IntrinsicISizesContribution; use text; @@ -789,22 +789,14 @@ pub struct InlineFlow { impl InlineFlow { pub fn from_fragments(fragments: InlineFragments, writing_mode: WritingMode) -> InlineFlow { - let mut flow = InlineFlow { + 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,12 +1391,6 @@ impl Flow for InlineFlow { .translate(stacking_context_position)) } } - - fn mutate_fragments(&mut self, mutator: &mut FragmentMutator) { - for fragment in self.fragments.fragments.iter_mut() { - mutator.process(fragment) - } - } } impl fmt::Debug for InlineFlow { diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index 3a0959d6b48..ca321f23526 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -834,14 +834,6 @@ 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 0985a51997e..837945eff81 100644 --- a/components/layout/lib.rs +++ b/components/layout/lib.rs @@ -66,9 +66,7 @@ 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; @@ -84,6 +82,7 @@ pub mod table_cell; pub mod text; pub mod traversal; pub mod util; +pub mod incremental; pub mod wrapper; pub mod css { diff --git a/components/layout/list_item.rs b/components/layout/list_item.rs index 23748480cf1..eedb2d3b6b6 100644 --- a/components/layout/list_item.rs +++ b/components/layout/list_item.rs @@ -8,14 +8,12 @@ #![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::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, FragmentMutator}; -use fragment::{GeneratedContentInfo}; -use generated_content; -use incremental::RESOLVE_GENERATED_CONTENT; +use fragment::{Fragment, FragmentBorderBoxIterator}; use wrapper::ThreadSafeLayoutNode; use geom::{Point2D, Rect}; @@ -38,33 +36,19 @@ pub struct ListItemFlow { } impl ListItemFlow { - pub fn from_node_fragments_and_flotation(node: &ThreadSafeLayoutNode, - main_fragment: Fragment, - marker_fragment: Option, - flotation: Option) - -> ListItemFlow { - let mut this = ListItemFlow { + pub fn from_node_marker_and_flotation(constructor: &mut FlowConstructor, + node: &ThreadSafeLayoutNode, + marker_fragment: Option, + flotation: Option) + -> ListItemFlow { + ListItemFlow { block_flow: if let Some(flotation) = flotation { - BlockFlow::float_from_node_and_fragment(node, main_fragment, flotation) + BlockFlow::float_from_node(constructor, node, flotation) } else { - BlockFlow::from_node_and_fragment(node, main_fragment) + BlockFlow::from_node(constructor, node) }, 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 } } @@ -150,55 +134,24 @@ impl Flow for ListItemFlow { fn iterate_through_fragment_border_boxes(&self, iterator: &mut FragmentBorderBoxIterator, stacking_context_position: &Point2D) { - 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, - CoordinateSystem::Parent) - .translate(stacking_context_position)); - } - } - } - - fn mutate_fragments(&mut self, mutator: &mut FragmentMutator) { - self.block_flow.mutate_fragments(mutator); - - if let Some(ref mut marker) = self.marker { - mutator.process(marker) - } + self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } } -/// The kind of content that `list-style-type` results in. -pub enum ListStyleTypeContent { - None, - StaticText(&'static str), - GeneratedContent(Box), -} - -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).unwrap(); - ListStyleTypeContent::StaticText(text) - } - _ => ListStyleTypeContent::GeneratedContent(box GeneratedContentInfo::ListItem), - } +/// 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}"), } } diff --git a/components/layout/sequential.rs b/components/layout/sequential.rs index c33441b6faf..ff2bd948c51 100644 --- a/components/layout/sequential.rs +++ b/components/layout/sequential.rs @@ -5,11 +5,10 @@ //! Implements sequential traversals over the DOM and flow trees. use context::{LayoutContext, SharedLayoutContext}; -use flow::{self, Flow, ImmutableFlowUtils, InorderFlowTraversal, MutableFlowUtils}; -use flow::{PostorderFlowTraversal, PreorderFlowTraversal}; +use flow::{self, Flow, ImmutableFlowUtils, MutableFlowUtils, PostorderFlowTraversal}; +use flow::{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}; @@ -40,24 +39,6 @@ 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, @@ -138,4 +119,3 @@ pub fn iterate_through_flow_tree_fragment_border_boxes(root: &mut FlowRef, doit(&mut **root, iterator, &ZERO_POINT); } - diff --git a/components/layout/table.rs b/components/layout/table.rs index 4a23db3f532..270143a21da 100644 --- a/components/layout/table.rs +++ b/components/layout/table.rs @@ -8,11 +8,12 @@ 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}; -use flow::{ImmutableFlowUtils}; -use fragment::{Fragment, FragmentBorderBoxIterator, FragmentMutator}; +use flow::ImmutableFlowUtils; +use fragment::{Fragment, FragmentBorderBoxIterator}; use layout_debug; use model::{IntrinsicISizes, IntrinsicISizesContribution}; use table_row::CellIntrinsicInlineSize; @@ -54,12 +55,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 - }; + 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(), @@ -68,17 +69,35 @@ impl TableFlow { } } - 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 - }; + 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 + }; + TableFlow { + block_flow: block_flow, + column_intrinsic_inline_sizes: Vec::new(), + column_computed_inline_sizes: Vec::new(), + table_layout: table_layout + } + } + + 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 + }; TableFlow { block_flow: block_flow, column_intrinsic_inline_sizes: Vec::new(), @@ -377,10 +396,6 @@ impl Flow for TableFlow { stacking_context_position: &Point2D) { self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } - - fn mutate_fragments(&mut self, mutator: &mut FragmentMutator) { - 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 f892929932b..43eaacf9384 100644 --- a/components/layout/table_caption.rs +++ b/components/layout/table_caption.rs @@ -7,9 +7,10 @@ #![deny(unsafe_blocks)] use block::BlockFlow; +use construct::FlowConstructor; use context::LayoutContext; use flow::{FlowClass, Flow}; -use fragment::{Fragment, FragmentBorderBoxIterator, FragmentMutator}; +use fragment::FragmentBorderBoxIterator; use wrapper::ThreadSafeLayoutNode; use geom::{Point2D, Rect}; @@ -25,10 +26,11 @@ pub struct TableCaptionFlow { } impl TableCaptionFlow { - pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode, fragment: Fragment) - -> TableCaptionFlow { + pub fn from_node(constructor: &mut FlowConstructor, + node: &ThreadSafeLayoutNode) + -> TableCaptionFlow { TableCaptionFlow { - block_flow: BlockFlow::from_node_and_fragment(node, fragment) + block_flow: BlockFlow::from_node(constructor, node) } } } @@ -94,10 +96,6 @@ impl Flow for TableCaptionFlow { stacking_context_position: &Point2D) { self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } - - fn mutate_fragments(&mut self, mutator: &mut FragmentMutator) { - 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 2735f5294df..670dcf7b344 100644 --- a/components/layout/table_cell.rs +++ b/components/layout/table_cell.rs @@ -9,7 +9,7 @@ use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag}; use context::LayoutContext; use flow::{Flow, FlowClass}; -use fragment::{Fragment, FragmentBorderBoxIterator, FragmentMutator}; +use fragment::{Fragment, FragmentBorderBoxIterator}; use model::{MaybeAuto}; use layout_debug; use table::InternalTable; @@ -178,10 +178,6 @@ impl Flow for TableCellFlow { stacking_context_position: &Point2D) { self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } - - fn mutate_fragments(&mut self, mutator: &mut FragmentMutator) { - 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 2bf02db4428..6d19a0c5778 100644 --- a/components/layout/table_colgroup.rs +++ b/components/layout/table_colgroup.rs @@ -9,7 +9,7 @@ use context::LayoutContext; use css::node_style::StyledNode; use flow::{BaseFlow, FlowClass, Flow, ForceNonfloatedFlag}; -use fragment::{Fragment, FragmentBorderBoxIterator, FragmentMutator, SpecificFragmentInfo}; +use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo}; use layout_debug; use wrapper::ThreadSafeLayoutNode; @@ -104,8 +104,6 @@ impl Flow for TableColGroupFlow { fn iterate_through_fragment_border_boxes(&self, _: &mut FragmentBorderBoxIterator, _: &Point2D) {} - - fn mutate_fragments(&mut self, _: &mut FragmentMutator) {} } impl fmt::Debug for TableColGroupFlow { diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs index 6425b63d1ea..d712d7e4945 100644 --- a/components/layout/table_row.rs +++ b/components/layout/table_row.rs @@ -8,9 +8,11 @@ use block::BlockFlow; use block::ISizeAndMarginsComputer; +use construct::FlowConstructor; use context::LayoutContext; -use flow::{self, FlowClass, Flow, ImmutableFlowUtils}; -use fragment::{Fragment, FragmentBorderBoxIterator, FragmentMutator}; +use flow::{FlowClass, Flow, ImmutableFlowUtils}; +use flow; +use fragment::{Fragment, FragmentBorderBoxIterator}; use layout_debug; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable}; use model::MaybeAuto; @@ -47,7 +49,8 @@ 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), @@ -56,6 +59,16 @@ 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 } @@ -318,10 +331,6 @@ impl Flow for TableRowFlow { stacking_context_position: &Point2D) { self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } - - fn mutate_fragments(&mut self, mutator: &mut FragmentMutator) { - 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 ac86cd8987e..9728fde36a8 100644 --- a/components/layout/table_rowgroup.rs +++ b/components/layout/table_rowgroup.rs @@ -7,9 +7,10 @@ #![deny(unsafe_blocks)] use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag}; +use construct::FlowConstructor; use context::LayoutContext; use flow::{FlowClass, Flow}; -use fragment::{Fragment, FragmentBorderBoxIterator, FragmentMutator}; +use fragment::{Fragment, FragmentBorderBoxIterator}; use layout_debug; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable}; use wrapper::ThreadSafeLayoutNode; @@ -44,6 +45,15 @@ 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 } @@ -155,10 +165,6 @@ impl Flow for TableRowGroupFlow { stacking_context_position: &Point2D) { self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } - - fn mutate_fragments(&mut self, mutator: &mut FragmentMutator) { - 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 fbe124af1bf..e849150bede 100644 --- a/components/layout/table_wrapper.rs +++ b/components/layout/table_wrapper.rs @@ -13,13 +13,13 @@ #![deny(unsafe_blocks)] -use block::{BlockFlow, BlockNonReplaced, FloatNonReplaced, ISizeAndMarginsComputer}; -use block::{MarginsMayCollapseFlag}; +use block::{BlockFlow, BlockNonReplaced, FloatNonReplaced, ISizeAndMarginsComputer, MarginsMayCollapseFlag}; +use construct::FlowConstructor; use context::LayoutContext; use floats::FloatKind; use flow::{FlowClass, Flow, ImmutableFlowUtils}; use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS}; -use fragment::{Fragment, FragmentBorderBoxIterator, FragmentMutator}; +use fragment::{Fragment, FragmentBorderBoxIterator}; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize}; use wrapper::ThreadSafeLayoutNode; @@ -70,6 +70,23 @@ 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) @@ -366,10 +383,6 @@ impl Flow for TableWrapperFlow { stacking_context_position: &Point2D) { self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } - - fn mutate_fragments(&mut self, mutator: &mut FragmentMutator) { - self.block_flow.mutate_fragments(mutator) - } } impl fmt::Debug for TableWrapperFlow { diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index 04a99ee4b77..2211cf2fcce 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::{self, BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, RestyleDamage}; +use incremental::{RestyleDamage, BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW}; use wrapper::{layout_node_to_unsafe_layout_node, LayoutNode}; use wrapper::{PostorderNodeMutTraversal, ThreadSafeLayoutNode, UnsafeLayoutNode}; use wrapper::{PreorderDomTraversal, PostorderDomTraversal}; @@ -171,7 +171,7 @@ impl<'a> PreorderDomTraversal for RecalcStyleForNode<'a> { &mut applicable_declarations, &mut shareable); } else { - ThreadSafeLayoutNode::new(&node).set_restyle_damage(incremental::all()) + ThreadSafeLayoutNode::new(&node).set_restyle_damage(RestyleDamage::all()) } // Perform the CSS cascade. @@ -376,4 +376,3 @@ 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 e43603d0dc1..54f341d1d09 100644 --- a/components/layout/wrapper.rs +++ b/components/layout/wrapper.rs @@ -67,11 +67,9 @@ use std::marker::ContravariantLifetime; use std::mem; use std::sync::mpsc::Sender; use string_cache::{Atom, Namespace}; -use selectors::parser::{NamespaceConstraint, AttrSelector}; -use style::computed_values::content::ContentItem; use style::computed_values::{content, display, white_space}; -use style::legacy::{IntegerAttribute, LengthAttribute, SimpleColorAttribute}; -use style::legacy::{UnsignedIntegerAttribute}; +use selectors::parser::{NamespaceConstraint, AttrSelector}; +use style::legacy::{LengthAttribute, SimpleColorAttribute, UnsignedIntegerAttribute, IntegerAttribute}; use style::node::{TElement, TElementAttributes, TNode}; use style::properties::PropertyDeclarationBlock; use url::Url; @@ -156,11 +154,10 @@ pub trait TLayoutNode { } } - /// If this is a text node or generated content, copies out its content. If this is not a text - /// node, fails. + /// If this is a text node, copies out the text. If this is not a text node, fails. /// - /// FIXME(pcwalton): This might have too much copying and/or allocation. Profile this. - fn text_content(&self) -> Vec; + /// FIXME(pcwalton): Don't copy text. Atomically reference count instead. + fn text(&self) -> String; /// Returns the first child of this node. fn first_child(&self) -> Option; @@ -217,25 +214,19 @@ impl<'ln> TLayoutNode for LayoutNode<'ln> { } } - fn text_content(&self) -> Vec { + fn text(&self) -> String { unsafe { let text: Option> = TextCast::to_layout_js(self.get_jsmanaged()); if let Some(text) = text { - return vec![ - ContentItem::String((*text.unsafe_get()).characterdata() - .data_for_layout() - .to_owned()) - ]; + return (*text.unsafe_get()).characterdata().data_for_layout().to_owned(); } - let input: Option> = - HTMLInputElementCast::to_layout_js(self.get_jsmanaged()); + let input: Option> = HTMLInputElementCast::to_layout_js(self.get_jsmanaged()); if let Some(input) = input { - return vec![ContentItem::String(input.get_value_for_layout())]; + return input.get_value_for_layout(); } - let area: Option> = - HTMLTextAreaElementCast::to_layout_js(self.get_jsmanaged()); + let area: Option> = HTMLTextAreaElementCast::to_layout_js(self.get_jsmanaged()); if let Some(area) = area { - return vec![ContentItem::String(area.get_value_for_layout())]; + return area.get_value_for_layout(); } panic!("not text!") @@ -670,10 +661,16 @@ impl<'le> TElementAttributes for LayoutElement<'le> { } } -fn get_content(content_list: &content::T) -> Vec { +fn get_content(content_list: &content::T) -> String { match *content_list { - content::T::Content(ref value) if !value.is_empty() => (*value).clone(), - _ => vec![ContentItem::String("".to_owned())], + 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(), } } @@ -765,7 +762,7 @@ impl<'ln> TLayoutNode for ThreadSafeLayoutNode<'ln> { } } - fn text_content(&self) -> Vec { + fn text(&self) -> String { if self.pseudo != PseudoElementType::Normal { let layout_data_ref = self.borrow_layout_data(); let node_layout_data_wrapper = layout_data_ref.as_ref().unwrap(); @@ -778,7 +775,7 @@ impl<'ln> TLayoutNode for ThreadSafeLayoutNode<'ln> { return get_content(&after_style.get_box().content) } } - self.node.text_content() + self.node.text() } } diff --git a/components/script/dom/webidls/CSSStyleDeclaration.webidl b/components/script/dom/webidls/CSSStyleDeclaration.webidl index bde48aaaff3..46ca8424b70 100644 --- a/components/script/dom/webidls/CSSStyleDeclaration.webidl +++ b/components/script/dom/webidls/CSSStyleDeclaration.webidl @@ -103,11 +103,6 @@ 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/style/properties.mako.rs b/components/style/properties.mako.rs index b3ccef6f95a..5330de59a93 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -758,241 +758,86 @@ pub mod longhands { ${switch_to_style_struct("Box")} <%self:longhand name="content"> - use cssparser::{ToCss, Token}; - use std::ascii::AsciiExt; - use std::borrow::ToOwned; - use values::computed::ComputedValueAsSpecified; + pub use self::computed_value::T as SpecifiedValue; + pub use self::computed_value::ContentItem; + use cssparser::Token; + use values::computed::ComputedValueAsSpecified; - use super::list_style_type; + impl ComputedValueAsSpecified for SpecifiedValue {} - pub use self::computed_value::T as SpecifiedValue; - pub use self::computed_value::ContentItem; + pub mod computed_value { + use std::borrow::IntoCow; + use cssparser::{ToCss, Token}; + use text_writer::{self, TextWriter}; - impl ComputedValueAsSpecified for SpecifiedValue {} - - pub mod computed_value { - use super::super::list_style_type; - - use cssparser::{ToCss, Token}; - use std::borrow::IntoCow; - 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(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { - match self { - &ContentItem::String(ref s) => { - Token::QuotedString((&**s).into_cow()).to_css(dest) - } - &ContentItem::Counter(ref s, _) => { - // FIXME(pcwalton) - Token::QuotedString((&**s).into_cow()).to_css(dest) - } - &ContentItem::Counters(ref s, _, _) => { - // FIXME(pcwalton) - Token::QuotedString((&**s).into_cow()).to_css(dest) - } - &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"), - } + #[derive(PartialEq, Eq, Clone)] + pub enum ContentItem { + StringContent(String), } - } - #[allow(non_camel_case_types)] - #[derive(PartialEq, Eq, Clone)] - pub enum T { - normal, - none, - Content(Vec), - } - - impl ToCss for T { - fn to_css(&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)); + impl ToCss for ContentItem { + fn to_css(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { + match self { + &ContentItem::StringContent(ref s) => { + Token::QuotedString((&**s).into_cow()).to_css(dest) } - Ok(()) } } } - } - } - #[inline] - pub fn get_initial_value() -> computed_value::T { - computed_value::T::normal - } - 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") - } + #[allow(non_camel_case_types)] + #[derive(PartialEq, Eq, Clone)] + pub enum T { + normal, + none, + Content(Vec), + } - // normal | none | [ | | open-quote | close-quote | no-open-quote | - // no-close-quote ]+ - // TODO: , attr() - pub fn parse(context: &ParserContext, input: &mut Parser) - -> Result { - 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)) => { - if name.eq_ignore_ascii_case("counter") { - let (mut counter_name, mut counter_style) = (None, None); - match input.parse_nested_block(|input| { - input.parse_comma_separated(|input| { - if counter_name.is_none() { - match input.next() { - Ok(Token::Ident(value)) => { - counter_name = Some((*value).to_owned()); - Ok(()) - } - _ => Err(()) - } - } else if counter_style.is_none() { - match list_style_type::parse(context, input) { - Ok(style) => { - counter_style = Some(style); - Ok(()) - } - _ => Err(()) - } - } else { - Err(()) - } - }) - }) { - Ok(_) => { - match (counter_name, counter_style) { - (Some(name), Some(style)) => { - content.push(ContentItem::Counter(name, style)) - } - (Some(name), None) => { - content.push(ContentItem::Counter( - name, - list_style_type::computed_value::T::decimal)) - } - _ => return Err(()), - } + impl ToCss for T { + fn to_css(&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)); } - Err(_) => return Err(()), + Ok(()) } - } else if name.eq_ignore_ascii_case("counters") { - let mut counter_name = None; - let mut counter_separator = None; - let mut counter_style = None; - match input.parse_nested_block(|input| { - input.parse_comma_separated(|input| { - if counter_name.is_none() { - match input.next() { - Ok(Token::Ident(value)) => { - counter_name = Some((*value).to_owned()); - Ok(()) - } - _ => Err(()) - } - } else if counter_separator.is_none() { - match input.next() { - Ok(Token::QuotedString(value)) => { - counter_separator = Some((*value).to_owned()); - Ok(()) - } - _ => Err(()) - } - } else if counter_style.is_none() { - match input.try(|input| { - list_style_type::parse(context, input) - }) { - Ok(style) => { - counter_style = Some(style); - Ok(()) - } - _ => Err(()), - } - } else { - Err(()) - } - }) - }) { - Ok(_) => { - match (counter_name, counter_separator, counter_style) { - (Some(name), Some(separator), Some(style)) => { - content.push(ContentItem::Counters(name, - separator, - style)) - } - (Some(name), Some(separator), None) => { - content.push(ContentItem::Counters( - name, - separator, - list_style_type::computed_value::T::decimal)) - } - _ => return Err(()), - } - } - Err(_) => return Err(()), - } - } else { - return Err(()) } } - Ok(Token::Ident(ident)) => { - if ident.eq_ignore_ascii_case("open-quote") { - content.push(ContentItem::OpenQuote) - } else if ident.eq_ignore_ascii_case("close-quote") { - content.push(ContentItem::CloseQuote) - } else if ident.eq_ignore_ascii_case("no-open-quote") { - content.push(ContentItem::NoOpenQuote) - } else if ident.eq_ignore_ascii_case("no-close-quote") { - content.push(ContentItem::NoCloseQuote) - } else { - return Err(()) - } - } - Err(()) if !content.is_empty() => { - let mut result = String::new(); - for content in content.iter() { - content.to_css(&mut result).unwrap() - } - return Ok(SpecifiedValue::Content(content)) - } - _ => return Err(()) } } - } + #[inline] + pub fn get_initial_value() -> computed_value::T { + computed_value::T::normal + } + + // normal | none | [ ]+ + // TODO: , , attr(), open-quote, close-quote, no-open-quote, no-close-quote + pub fn parse(_context: &ParserContext, input: &mut Parser) + -> Result { + 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)) + } + _ => return Err(()) + } + } + } ${new_style_struct("List", is_inherited=True)} @@ -1001,12 +846,14 @@ pub mod longhands { // TODO(pcwalton): Implement the full set of counter styles per CSS-COUNTER-STYLES [1] 6.1: // - // decimal-leading-zero, armenian, upper-armenian, lower-armenian, georgian, lower-roman, - // upper-roman + // 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 // // [1]: http://dev.w3.org/csswg/css-counter-styles/ ${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")} + "disc none circle square disclosure-open disclosure-closed")} <%self:longhand name="list-style-image"> use std::borrow::IntoCow; @@ -1062,144 +909,6 @@ pub mod longhands { } - <%self:longhand name="quotes"> - use cssparser::{ToCss, Token}; - use text_writer::{self, TextWriter}; - use values::computed::{ToComputedValue, Context}; - - pub use self::computed_value::T as SpecifiedValue; - - pub mod computed_value { - #[derive(Clone, PartialEq)] - pub struct T(pub Vec<(String,String)>); - } - - impl ToComputedValue for SpecifiedValue { - type ComputedValue = computed_value::T; - - fn to_computed_value(&self, _: &Context) -> computed_value::T { - (*self).clone() - } - } - - impl ToCss for SpecifiedValue { - fn to_css(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { - // TODO(pcwalton) - dest.write_str("") - } - } - - #[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 { - 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(()) - } - } - - - ${new_style_struct("Counters", is_inherited=False)} - - <%self:longhand name="counter-increment"> - use cssparser::{NumericValue, ToCss, Token}; - use super::content; - use text_writer::{self, TextWriter}; - use values::computed::{ToComputedValue, Context}; - - use std::borrow::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 ToComputedValue for SpecifiedValue { - type ComputedValue = computed_value::T; - - fn to_computed_value(&self, _: &Context) -> computed_value::T { - (*self).clone() - } - } - - impl ToCss for SpecifiedValue { - fn to_css(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { - // TODO(pcwalton) - dest.write_str("") - } - } - - pub fn parse(_: &ParserContext, input: &mut Parser) -> Result { - 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| { - match input.next() { - Ok(Token::Number(NumericValue { - int_value: Some(int_value), - .. - })) => Ok(int_value as i32), - _ => Err(()), - } - }).unwrap_or(1); - counters.push((counter_name, counter_delta)) - } - - if !counters.is_empty() { - Ok(SpecifiedValue(counters)) - } else { - Err(()) - } - } - - - <%self:longhand name="counter-reset"> - pub use super::counter_increment::{SpecifiedValue, computed_value, get_initial_value}; - pub use super::counter_increment::{parse}; - - // 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 c9cf32c0617..46de5abbceb 100644 --- a/components/util/time.rs +++ b/components/util/time.rs @@ -81,7 +81,6 @@ pub enum TimeProfilerCategory { LayoutSelectorMatch, LayoutTreeBuilder, LayoutDamagePropagate, - LayoutGeneratedContent, LayoutMain, LayoutParallelWarmup, LayoutShaping, @@ -100,7 +99,6 @@ impl Formatable for TimeProfilerCategory { TimeProfilerCategory::LayoutStyleRecalc | TimeProfilerCategory::LayoutRestyleDamagePropagation | TimeProfilerCategory::LayoutNonIncrementalReset | - TimeProfilerCategory::LayoutGeneratedContent | TimeProfilerCategory::LayoutMain | TimeProfilerCategory::LayoutDispListBuild | TimeProfilerCategory::LayoutShaping | @@ -121,7 +119,6 @@ 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 15370ead80b..c67a240d535 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -902,7 +902,6 @@ dependencies = [ "regex 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "selectors 0.1.0 (git+https://github.com/servo/rust-selectors)", - "plugins 0.0.1", "string_cache 0.0.0 (git+https://github.com/servo/string-cache)", "string_cache_macros 0.0.0 (git+https://github.com/servo/string-cache)", "task_info 0.0.1", diff --git a/tests/ref/basic.list b/tests/ref/basic.list index a53460612d0..2aec5daa48d 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -172,11 +172,6 @@ fragment=top != ../html/acid2.html acid2_ref.html == block_formatting_context_containing_floats_a.html block_formatting_context_containing_floats_ref.html == clear_generated_content_table_a.html clear_generated_content_table_ref.html == inline_block_border_a.html inline_block_border_ref.html -== counters_simple_a.html counters_simple_ref.html -== counters_nested_a.html counters_nested_ref.html -== quotes_simple_a.html quotes_simple_ref.html -== ol_simple_a.html ol_simple_ref.html -== ol_japanese_iroha_a.html ol_japanese_iroha_ref.html == vertical_align_top_a.html vertical_align_top_ref.html == vertical_align_bottom_a.html vertical_align_bottom_ref.html == vertical_align_top_span_a.html vertical_align_top_span_ref.html diff --git a/tests/ref/counters_nested_a.html b/tests/ref/counters_nested_a.html deleted file mode 100644 index 39baa0ac16b..00000000000 --- a/tests/ref/counters_nested_a.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - -
-

Foo

-
-

Boo

-

Quux

-
-

Blah

-
-
-

Bar

-
-

Boo

-

Quux

-

Baz

-
- - - diff --git a/tests/ref/counters_nested_ref.html b/tests/ref/counters_nested_ref.html deleted file mode 100644 index b42f6509c83..00000000000 --- a/tests/ref/counters_nested_ref.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - -
-

1. Foo

-
-

1.1. Boo

-

1.2. Quux

-
-

1.2.1. Blah

-
-
-

1.3. Bar

-
-

1.1. Boo

-

1.2. Quux

-

1.3. Baz

-
- - - diff --git a/tests/ref/counters_simple_a.html b/tests/ref/counters_simple_a.html deleted file mode 100644 index e8534e3da17..00000000000 --- a/tests/ref/counters_simple_a.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - -

Foo

-

Boo

-

Quux

-

Blah

-

Bar

-

Boo

-

Quux

-

Baz

- - - diff --git a/tests/ref/counters_simple_ref.html b/tests/ref/counters_simple_ref.html deleted file mode 100644 index b1adc96fb26..00000000000 --- a/tests/ref/counters_simple_ref.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - -

1. Foo

-

1.1. Boo

-

1.2. Quux

-

1.2.1. Blah

-

2. Bar

-

2.1. Boo

-

2.2. Quux

-

3. Baz

- - - diff --git a/tests/ref/ol_japanese_iroha_a.html b/tests/ref/ol_japanese_iroha_a.html deleted file mode 100644 index 92cbc34e006..00000000000 --- a/tests/ref/ol_japanese_iroha_a.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - -
    -
  1. Gryffindor
  2. -
  3. Hufflepuff
  4. -
  5. Ravenclaw
  6. -
  7. Slytherin
  8. -
- - - - diff --git a/tests/ref/ol_japanese_iroha_ref.html b/tests/ref/ol_japanese_iroha_ref.html deleted file mode 100644 index 6accdac4b63..00000000000 --- a/tests/ref/ol_japanese_iroha_ref.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - -
    -
  1. い. Gryffindor
  2. -
  3. ろ. Hufflepuff
  4. -
  5. は. Ravenclaw
  6. -
  7. に. Slytherin
  8. -
- - - - diff --git a/tests/ref/ol_simple_a.html b/tests/ref/ol_simple_a.html deleted file mode 100644 index e63c00c4ceb..00000000000 --- a/tests/ref/ol_simple_a.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - -
    -
  1. Foo
  2. -
  3. Bar
  4. -
  5. Baz
  6. -
- - - diff --git a/tests/ref/ol_simple_ref.html b/tests/ref/ol_simple_ref.html deleted file mode 100644 index d3411bb2e6d..00000000000 --- a/tests/ref/ol_simple_ref.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - -
    -
  1. 1. Foo
  2. -
  3. 2. Bar
  4. -
  5. 3. Baz
  6. -
- - - diff --git a/tests/ref/quotes_simple_a.html b/tests/ref/quotes_simple_a.html deleted file mode 100644 index 257be1276e0..00000000000 --- a/tests/ref/quotes_simple_a.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - -I remember when I first read Hagrid said, You're a wizard, Harry! - - - diff --git a/tests/ref/quotes_simple_ref.html b/tests/ref/quotes_simple_ref.html deleted file mode 100644 index b378efaa4ab..00000000000 --- a/tests/ref/quotes_simple_ref.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - -I remember when I first read “Hagrid said, ‘You're a wizard, Harry!’” - - - - From 522b0ea3b97a67af3eef782274c14f58c6b4399a Mon Sep 17 00:00:00 2001 From: Dan Fox Date: Tue, 3 Mar 2015 20:27:19 +0000 Subject: [PATCH 14/29] uint -> usize --- tests/reftest.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/reftest.rs b/tests/reftest.rs index c718e827969..3908217c556 100644 --- a/tests/reftest.rs +++ b/tests/reftest.rs @@ -130,7 +130,7 @@ struct Reftest { name: String, kind: ReftestKind, files: [Path; 2], - id: uint, + id: usize, servo_args: Vec, render_mode: RenderMode, is_flaky: bool, @@ -145,7 +145,7 @@ struct TestLine<'a> { file_right: &'a str, } -fn parse_lists(file: &Path, servo_args: &[String], render_mode: RenderMode, id_offset: uint) -> Vec { +fn parse_lists(file: &Path, servo_args: &[String], render_mode: RenderMode, id_offset: usize) -> Vec { let mut tests = Vec::new(); let contents = File::open_mode(file, io::Open, io::Read) .and_then(|mut f| f.read_to_string()) @@ -240,7 +240,7 @@ fn make_test(reftest: Reftest) -> TestDescAndFn { } } -fn capture(reftest: &Reftest, side: uint) -> (u32, u32, Vec) { +fn capture(reftest: &Reftest, side: usize) -> (u32, u32, Vec) { let png_filename = format!("/tmp/servo-reftest-{:06}-{}.png", reftest.id, side); let mut command = Command::new(os::self_exe_path().unwrap().join("servo")); command From d9f04180a5d9146f4486ede6fabb9da638cccd41 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Thu, 19 Feb 2015 10:00:02 -0500 Subject: [PATCH 15/29] Split page load into separate network and parsing stages. Delay Page creation until the load is finished. Make session history traversal simply activate existing pipelines, rather than potentially loading them from the network. --- components/compositing/constellation.rs | 5 +- components/compositing/pipeline.rs | 10 +- components/script/page.rs | 15 +- components/script/script_task.rs | 418 ++++++++++++++---------- components/script_traits/lib.rs | 8 +- 5 files changed, 267 insertions(+), 189 deletions(-) diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index 2d370eca4e5..5ef72e185ec 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -407,8 +407,7 @@ impl Constellation { self.time_profiler_chan.clone(), self.window_size, script_pipeline, - load_data.clone()); - pipe.load(); + load_data); Rc::new(pipe) } @@ -876,7 +875,7 @@ impl Constellation { }; for frame in destination_frame.iter() { - frame.pipeline.borrow().load(); + frame.pipeline.borrow().activate(); frame.pipeline.borrow().thaw(); } self.send_frame_tree_and_grant_paint_permission(destination_frame); diff --git a/components/compositing/pipeline.rs b/components/compositing/pipeline.rs index 27ee5eba150..eabf93752b3 100644 --- a/components/compositing/pipeline.rs +++ b/components/compositing/pipeline.rs @@ -88,7 +88,8 @@ impl Pipeline { storage_task.clone(), image_cache_task.clone(), devtools_chan, - window_size); + window_size, + load_data.clone()); ScriptControlChan(script_chan) } Some(spipe) => { @@ -97,6 +98,7 @@ impl Pipeline { new_pipeline_id: id, subpage_id: parent.expect("script_pipeline != None but subpage_id == None").1, layout_chan: ScriptTaskFactory::clone_layout_channel(None::<&mut STF>, &layout_pair), + load_data: load_data.clone(), }; let ScriptControlChan(ref chan) = spipe.script_chan; @@ -160,11 +162,9 @@ impl Pipeline { } } - pub fn load(&self) { + pub fn activate(&self) { let ScriptControlChan(ref chan) = self.script_chan; - chan.send(ConstellationControlMsg::Load(self.id, - self.parent, - self.load_data.clone())).unwrap(); + chan.send(ConstellationControlMsg::Activate(self.id)).unwrap(); } pub fn grant_paint_permission(&self) { diff --git a/components/script/page.rs b/components/script/page.rs index c7ea2e44bec..3800b5b3950 100644 --- a/components/script/page.rs +++ b/components/script/page.rs @@ -68,11 +68,11 @@ pub struct Page { js_info: DOMRefCell>, - /// Cached copy of the most recent url loaded by the script + /// Cached copy of the most recent url loaded by the script, after all redirections. /// TODO(tkuehn): this currently does not follow any particular caching policy /// and simply caches pages forever (!). The bool indicates if reflow is required /// when reloading. - url: DOMRefCell>, + url: DOMRefCell<(Url, bool)>, next_subpage_id: Cell, @@ -140,7 +140,8 @@ impl Page { storage_task: StorageTask, constellation_chan: ConstellationChan, js_context: Rc, - devtools_chan: Option) -> Page { + devtools_chan: Option, + url: Url) -> Page { let js_info = JSPageInfo { dom_static: GlobalStaticData::new(), js_context: js_context, @@ -160,7 +161,7 @@ impl Page { layout_join_port: DOMRefCell::new(None), window_size: Cell::new(window_size), js_info: DOMRefCell::new(Some(js_info)), - url: DOMRefCell::new(None), + url: DOMRefCell::new((url, true)), next_subpage_id: Cell::new(SubpageId(0)), resize_event: Cell::new(None), fragment_name: DOMRefCell::new(None), @@ -292,11 +293,11 @@ impl Page { self.js_info.borrow() } - pub fn url<'a>(&'a self) -> Ref<'a, Option<(Url, bool)>> { + pub fn url<'a>(&'a self) -> Ref<'a, (Url, bool)> { self.url.borrow() } - pub fn mut_url<'a>(&'a self) -> RefMut<'a, Option<(Url, bool)>> { + pub fn mut_url<'a>(&'a self) -> RefMut<'a, (Url, bool)> { self.url.borrow_mut() } @@ -316,7 +317,7 @@ impl Page { } pub fn get_url(&self) -> Url { - self.url().as_ref().unwrap().0.clone() + self.url().0.clone() } // FIXME(cgaebel): join_layout is racey. What if the compositor triggers a diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 51361bd1436..28a2e6437c0 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -49,16 +49,17 @@ use msg::constellation_msg::{LoadData, PipelineId, SubpageId}; use msg::constellation_msg::{Failure, Msg, WindowSizeData, PipelineExitType}; use msg::constellation_msg::Msg as ConstellationMsg; use net::image_cache_task::ImageCacheTask; -use net::resource_task::{ResourceTask, ControlMsg}; +use net::resource_task::{ResourceTask, ControlMsg, LoadResponse}; use net::resource_task::LoadData as NetLoadData; use net::storage_task::StorageTask; use string_cache::Atom; use util::geometry::to_frac_px; use util::smallvec::SmallVec; use util::str::DOMString; -use util::task::spawn_named_with_send_on_failure; +use util::task::{spawn_named, spawn_named_with_send_on_failure}; use util::task_state; +use geom::Rect; use geom::point::Point2D; use hyper::header::{LastModified, Headers}; use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ, JS_GC}; @@ -83,6 +84,32 @@ use time::Tm; thread_local!(pub static STACK_ROOTS: Cell> = Cell::new(None)); +struct InProgressLoad { + pipeline_id: PipelineId, + subpage_id: Option<(PipelineId, SubpageId)>, + window_size: WindowSizeData, + layout_chan: LayoutChan, + clip_rect: Option>, + url: Url, +} + +impl InProgressLoad { + fn new(id: PipelineId, + subpage_id: Option<(PipelineId, SubpageId)>, + layout_chan: LayoutChan, + window_size: WindowSizeData, + url: Url) -> InProgressLoad { + InProgressLoad { + pipeline_id: id, + subpage_id: subpage_id, + layout_chan: layout_chan, + window_size: window_size, + clip_rect: None, + url: url, + } + } +} + #[derive(Copy)] pub enum TimerSource { FromWindow(PipelineId), @@ -118,6 +145,8 @@ pub enum ScriptMsg { RunnableMsg(Box), /// A DOM object's last pinned reference was removed (dispatched to all tasks). RefcountCleanup(TrustedReference), + /// The final network response for a page has arrived. + PageFetchComplete(PipelineId, Option, LoadResponse), } /// A cloneable interface for communicating with an event loop. @@ -175,11 +204,15 @@ impl Drop for StackRootTLS { /// FIXME: Rename to `Page`, following WebKit? pub struct ScriptTask { /// A handle to the information pertaining to page layout - page: DOMRefCell>, + page: DOMRefCell>>, + /// A list of data pertaining to loads that have not yet received a network response + incomplete_loads: DOMRefCell>, /// A handle to the image cache task. image_cache_task: ImageCacheTask, /// A handle to the resource task. resource_task: ResourceTask, + /// A handle to the storage task. + storage_task: StorageTask, /// The port on which the script task receives messages (load URL, exit, etc.) port: Receiver, @@ -274,15 +307,14 @@ impl ScriptTaskFactory for ScriptTask { storage_task: StorageTask, image_cache_task: ImageCacheTask, devtools_chan: Option, - window_size: WindowSizeData) + window_size: WindowSizeData, + load_data: LoadData) where C: ScriptListener + Send + 'static { let ConstellationChan(const_chan) = constellation_chan.clone(); let (script_chan, script_port) = channel(); let layout_chan = LayoutChan(layout_chan.sender()); spawn_named_with_send_on_failure("ScriptTask", task_state::SCRIPT, move || { - let script_task = ScriptTask::new(id, - box compositor as Box, - layout_chan, + let script_task = ScriptTask::new(box compositor as Box, script_port, NonWorkerScriptChan(script_chan), control_chan, @@ -291,9 +323,13 @@ impl ScriptTaskFactory for ScriptTask { resource_task, storage_task, image_cache_task, - devtools_chan, - window_size); + devtools_chan); let mut failsafe = ScriptMemoryFailsafe::new(&script_task); + + let new_load = InProgressLoad::new(id, None, layout_chan, window_size, + load_data.url.clone()); + script_task.start_page_load(new_load, load_data); + script_task.start(); // This must always be the very last operation performed before the task completes @@ -312,9 +348,7 @@ unsafe extern "C" fn debug_gc_callback(_rt: *mut JSRuntime, status: JSGCStatus) impl ScriptTask { /// Creates a new script task. - pub fn new(id: PipelineId, - compositor: Box, - layout_chan: LayoutChan, + pub fn new(compositor: Box, port: Receiver, chan: NonWorkerScriptChan, control_chan: ScriptControlChan, @@ -323,8 +357,7 @@ impl ScriptTask { resource_task: ResourceTask, storage_task: StorageTask, img_cache_task: ImageCacheTask, - devtools_chan: Option, - window_size: WindowSizeData) + devtools_chan: Option) -> ScriptTask { let (js_runtime, js_context) = ScriptTask::new_rt_and_cx(); let wrap_for_same_compartment = wrap_for_same_compartment as @@ -348,19 +381,14 @@ impl ScriptTask { Some(pre_wrap)); } - let page = Page::new(id, None, layout_chan, window_size, - resource_task.clone(), - storage_task, - constellation_chan.clone(), - js_context.clone(), - devtools_chan.clone()); - let (devtools_sender, devtools_receiver) = channel(); ScriptTask { - page: DOMRefCell::new(Rc::new(page)), + page: DOMRefCell::new(None), + incomplete_loads: DOMRefCell::new(vec!()), image_cache_task: img_cache_task, resource_task: resource_task, + storage_task: storage_task, port: port, chan: chan, @@ -417,6 +445,10 @@ impl ScriptTask { (js_runtime, js_context) } + fn root_page(&self) -> Rc { + self.page.borrow().as_ref().unwrap().clone() + } + pub fn get_cx(&self) -> *mut JSContext { (**self.js_context.borrow().as_ref().unwrap()).ptr } @@ -439,17 +471,19 @@ impl ScriptTask { let mut resizes = vec!(); { - let page = self.page.borrow_mut(); - for page in page.iter() { - // Only process a resize if layout is idle. - let layout_join_port = page.layout_join_port.borrow(); - if layout_join_port.is_none() { - let mut resize_event = page.resize_event.get(); - match resize_event.take() { - Some(size) => resizes.push((page.id, size)), - None => () + let page = self.page.borrow(); + if let Some(ref page) = page.as_ref() { + for page in page.iter() { + // Only process a resize if layout is idle. + let layout_join_port = page.layout_join_port.borrow(); + if layout_join_port.is_none() { + let mut resize_event = page.resize_event.get(); + match resize_event.take() { + Some(size) => resizes.push((page.id, size)), + None => () + } + page.resize_event.set(None); } - page.resize_event.set(None); } } } @@ -502,17 +536,10 @@ impl ScriptTask { self.handle_new_layout(new_layout_info); } MixedMessage::FromConstellation(ConstellationControlMsg::Resize(id, size)) => { - let page = self.page.borrow_mut(); - let page = page.find(id).expect("resize sent to nonexistent pipeline"); - page.resize_event.set(Some(size)); + self.handle_resize(id, size); } MixedMessage::FromConstellation(ConstellationControlMsg::Viewport(id, rect)) => { - let page = self.page.borrow_mut(); - let inner_page = page.find(id).expect("Page rect message sent to nonexistent pipeline"); - if inner_page.set_page_clip_rect_with_new_viewport(rect) { - let page = get_page(&*page, id); - self.force_reflow(&*page); - } + self.handle_viewport(id, rect); } _ => { sequential.push(event); @@ -555,8 +582,8 @@ impl ScriptTask { match msg { ConstellationControlMsg::AttachLayout(_) => panic!("should have handled AttachLayout already"), - ConstellationControlMsg::Load(id, parent, load_data) => - self.load(id, parent, load_data), + ConstellationControlMsg::Activate(id) => + self.handle_activate(id), ConstellationControlMsg::SendEvent(id, event) => self.handle_event(id, event), ConstellationControlMsg::ReflowComplete(id, reflow_id) => @@ -598,57 +625,91 @@ impl ScriptTask { runnable.handler(), ScriptMsg::RefcountCleanup(addr) => LiveDOMReferences::cleanup(self.get_cx(), addr), + ScriptMsg::PageFetchComplete(id, subpage, response) => + self.handle_page_fetch_complete(id, subpage, response), } } fn handle_msg_from_devtools(&self, msg: DevtoolScriptControlMsg) { + let page = self.root_page(); match msg { DevtoolScriptControlMsg::EvaluateJS(id, s, reply) => - devtools::handle_evaluate_js(&*self.page.borrow(), id, s, reply), + devtools::handle_evaluate_js(&page, id, s, reply), DevtoolScriptControlMsg::GetRootNode(id, reply) => - devtools::handle_get_root_node(&*self.page.borrow(), id, reply), + devtools::handle_get_root_node(&page, id, reply), DevtoolScriptControlMsg::GetDocumentElement(id, reply) => - devtools::handle_get_document_element(&*self.page.borrow(), id, reply), + devtools::handle_get_document_element(&page, id, reply), DevtoolScriptControlMsg::GetChildren(id, node_id, reply) => - devtools::handle_get_children(&*self.page.borrow(), id, node_id, reply), + devtools::handle_get_children(&page, id, node_id, reply), DevtoolScriptControlMsg::GetLayout(id, node_id, reply) => - devtools::handle_get_layout(&*self.page.borrow(), id, node_id, reply), + devtools::handle_get_layout(&page, id, node_id, reply), DevtoolScriptControlMsg::ModifyAttribute(id, node_id, modifications) => - devtools::handle_modify_attribute(&*self.page.borrow(), id, node_id, modifications), + devtools::handle_modify_attribute(&page, id, node_id, modifications), DevtoolScriptControlMsg::WantsLiveNotifications(pipeline_id, to_send) => - devtools::handle_wants_live_notifications(&*self.page.borrow(), pipeline_id, to_send), + devtools::handle_wants_live_notifications(&page, pipeline_id, to_send), } } + fn handle_resize(&self, id: PipelineId, size: WindowSizeData) { + let page = self.page.borrow(); + if let Some(ref page) = page.as_ref() { + if let Some(ref page) = page.find(id) { + page.resize_event.set(Some(size)); + return; + } + } + let mut loads = self.incomplete_loads.borrow_mut(); + if let Some(ref mut load) = loads.iter_mut().find(|load| load.pipeline_id == id) { + load.window_size = size; + return; + } + panic!("resize sent to nonexistent pipeline"); + } + + fn handle_viewport(&self, id: PipelineId, rect: Rect) { + let page = self.page.borrow(); + if let Some(page) = page.as_ref() { + if let Some(ref inner_page) = page.find(id) { + if inner_page.set_page_clip_rect_with_new_viewport(rect) { + let page = get_page(page, id); + self.force_reflow(&*page); + } + return; + } + } + let mut loads = self.incomplete_loads.borrow_mut(); + if let Some(ref mut load) = loads.iter_mut().find(|load| load.pipeline_id == id) { + load.clip_rect = Some(rect); + return; + } + panic!("Page rect message sent to nonexistent pipeline"); + } + fn handle_new_layout(&self, new_layout_info: NewLayoutInfo) { let NewLayoutInfo { old_pipeline_id, new_pipeline_id, subpage_id, - layout_chan + layout_chan, + load_data, } = new_layout_info; - let page = self.page.borrow_mut(); + let page = self.root_page(); let parent_page = page.find(old_pipeline_id).expect("ScriptTask: received a layout whose parent has a PipelineId which does not correspond to a pipeline in the script task's page tree. This is a bug."); - let new_page = { - let window_size = parent_page.window_size.get(); - Page::new(new_pipeline_id, Some(subpage_id), - LayoutChan(layout_chan.downcast_ref::>().unwrap().clone()), - window_size, - parent_page.resource_task.clone(), - parent_page.storage_task.clone(), - self.constellation_chan.clone(), - self.js_context.borrow().as_ref().unwrap().clone(), - self.devtools_chan.clone()) - }; - parent_page.children.borrow_mut().push(Rc::new(new_page)); + + let chan = layout_chan.downcast_ref::>().unwrap(); + let layout_chan = LayoutChan(chan.clone()); + let new_load = InProgressLoad::new(new_pipeline_id, Some((old_pipeline_id, subpage_id)), + layout_chan, parent_page.window_size.get(), + load_data.url.clone()); + self.start_page_load(new_load, load_data); } /// Handles a timer that fired. fn handle_fire_timer_msg(&self, id: PipelineId, timer_id: TimerId) { - let page = self.page.borrow_mut(); + let page = self.root_page(); let page = page.find(id).expect("ScriptTask: received fire timer msg for a pipeline ID not associated with this script task. This is a bug."); let frame = page.frame(); @@ -658,7 +719,7 @@ impl ScriptTask { /// Handles freeze message fn handle_freeze_msg(&self, id: PipelineId) { - let page = self.page.borrow_mut(); + let page = self.root_page(); let page = page.find(id).expect("ScriptTask: received freeze msg for a pipeline ID not associated with this script task. This is a bug."); let frame = page.frame(); @@ -668,7 +729,7 @@ impl ScriptTask { /// Handles thaw message fn handle_thaw_msg(&self, id: PipelineId) { - let page = self.page.borrow_mut(); + let page = self.root_page(); let page = page.find(id).expect("ScriptTask: received thaw msg for a pipeline ID not associated with this script task. This is a bug."); let frame = page.frame(); @@ -676,10 +737,26 @@ impl ScriptTask { window.r().thaw(); } + fn handle_activate(&self, pipeline_id: PipelineId) { + // We should only get this message when moving in history, so all pages requested + // should exist. + let page = self.root_page().find(pipeline_id).unwrap(); + + // Pull out the `needs_reflow` flag explicitly because `reflow` can ask for the + // page's URL, and we can't be holding a borrow on that URL (#4402). + let needed_reflow = { + let &mut (_, ref mut needs_reflow) = &mut *page.mut_url(); + replace(needs_reflow, false) + }; + if needed_reflow { + self.force_reflow(&*page); + } + } + /// Handles a notification that reflow completed. fn handle_reflow_complete_msg(&self, pipeline_id: PipelineId, reflow_id: uint) { debug!("Script: Reflow {:?} complete for {:?}", reflow_id, pipeline_id); - let page = self.page.borrow_mut(); + let page = self.root_page(); let page = page.find(pipeline_id).expect( "ScriptTask: received a load message for a layout channel that is not associated \ with this script task. This is a bug."); @@ -700,13 +777,12 @@ impl ScriptTask { /// Window was resized, but this script was not active, so don't reflow yet fn handle_resize_inactive_msg(&self, id: PipelineId, new_size: WindowSizeData) { - let page = self.page.borrow_mut(); + let page = self.root_page(); let page = page.find(id).expect("Received resize message for PipelineId not associated with a page in the page tree. This is a bug."); page.window_size.set(new_size); match &mut *page.mut_url() { - &mut Some((_, ref mut needs_reflow)) => *needs_reflow = true, - &mut None => (), + &mut (_, ref mut needs_reflow) => *needs_reflow = true, } } @@ -724,20 +800,29 @@ impl ScriptTask { self.compositor.borrow_mut().close(); } + fn handle_page_fetch_complete(&self, id: PipelineId, subpage: Option, + response: LoadResponse) { + let idx = self.incomplete_loads.borrow().iter().position(|&:load| { + load.pipeline_id == id && load.subpage_id.map(|sub| sub.1) == subpage + }).unwrap(); + let load = self.incomplete_loads.borrow_mut().remove(idx); + self.load(response, load); + } + /// Handles a request for the window title. fn handle_get_title_msg(&self, pipeline_id: PipelineId) { - get_page(&*self.page.borrow(), pipeline_id).send_title_to_compositor(); + get_page(&self.root_page(), pipeline_id).send_title_to_compositor(); } /// Handles a request to exit the script task and shut down layout. /// Returns true if the script task should shut down and false otherwise. fn handle_exit_pipeline_msg(&self, id: PipelineId, exit_type: PipelineExitType) -> bool { // If root is being exited, shut down all pages - let page = self.page.borrow_mut(); + let page = self.root_page(); if page.id == id { debug!("shutting down layout for root page {:?}", id); *self.js_context.borrow_mut() = None; - shut_down_layout(&*page, (*self.js_runtime).ptr, exit_type); + shut_down_layout(&page, (*self.js_runtime).ptr, exit_type); return true } @@ -758,98 +843,56 @@ impl ScriptTask { /// The entry point to document loading. Defines bindings, sets up the window and document /// objects, parses HTML and CSS, and kicks off initial layout. - fn load(&self, pipeline_id: PipelineId, - parent: Option<(PipelineId, SubpageId)>, load_data: LoadData) { - let url = load_data.url.clone(); - debug!("ScriptTask: loading {} on page {:?}", url.serialize(), pipeline_id); + fn load(&self, response: LoadResponse, incomplete: InProgressLoad) { + let final_url = response.metadata.final_url.clone(); + debug!("ScriptTask: loading {} on page {:?}", incomplete.url.serialize(), incomplete.pipeline_id); - let borrowed_page = self.page.borrow_mut(); + let root_page_exists = self.page.borrow().is_none(); + assert!(incomplete.subpage_id.is_none() || root_page_exists); - let frame_element = parent.and_then(|(parent_id, subpage_id)| { - // In the case a parent id exists but the matching page - // cannot be found, this means the page exists in a different - // script task (due to origin) so it shouldn't be returned. - // TODO: window.parent will continue to return self in that - // case, which is wrong. We should be returning an object that - // denies access to most properties (per - // https://github.com/servo/servo/issues/3939#issuecomment-62287025). - borrowed_page.find(parent_id).and_then(|page| { - let doc = page.frame().as_ref().unwrap().document.root(); - let doc: JSRef = NodeCast::from_ref(doc.r()); + let frame_element = incomplete.subpage_id.and_then(|(parent_id, subpage_id)| { + let borrowed_page = self.root_page(); + // In the case a parent id exists but the matching page + // cannot be found, this means the page exists in a different + // script task (due to origin) so it shouldn't be returned. + // TODO: window.parent will continue to return self in that + // case, which is wrong. We should be returning an object that + // denies access to most properties (per + // https://github.com/servo/servo/issues/3939#issuecomment-62287025). + borrowed_page.find(parent_id).and_then(|page| { + let doc = page.frame().as_ref().unwrap().document.root(); + let doc: JSRef = NodeCast::from_ref(doc.r()); - doc.traverse_preorder() - .filter_map(HTMLIFrameElementCast::to_ref) - .find(|node| node.subpage_id() == Some(subpage_id)) - .map(ElementCast::from_ref) - .map(Temporary::from_rooted) - }) + doc.traverse_preorder() + .filter_map(HTMLIFrameElementCast::to_ref) + .find(|node| node.subpage_id() == Some(subpage_id)) + .map(ElementCast::from_ref) + .map(Temporary::from_rooted) + }) }).root(); - let page = borrowed_page.find(pipeline_id).expect("ScriptTask: received a load - message for a layout channel that is not associated with this script task. This - is a bug."); + self.compositor.borrow_mut().set_ready_state(incomplete.pipeline_id, Loading); - // Are we reloading? - let reloading = match *page.url() { - Some((ref loaded, _)) => *loaded == url, - _ => false, - }; - if reloading { - // Pull out the `needs_reflow` flag explicitly because `reflow` can ask for the page's - // URL, and we can't be holding a borrow on that URL (#4402). - let needed_reflow = match &mut *page.mut_url() { - &mut Some((_, ref mut needs_reflow)) => replace(needs_reflow, false), - _ => panic!("can't reload a page with no URL!") - }; - if needed_reflow { - self.force_reflow(&*page); - } - return - } - let last_url = replace(&mut *page.mut_url(), None).map(|(last_url, _)| last_url); - - let is_javascript = url.scheme.as_slice() == "javascript"; - - self.compositor.borrow_mut().set_ready_state(pipeline_id, Loading); - - let (mut parser_input, final_url, last_modified) = if !is_javascript { - // Wait for the LoadResponse so that the parser knows the final URL. - let (input_chan, input_port) = channel(); - self.resource_task.send(ControlMsg::Load(NetLoadData { - url: url, - method: load_data.method, - headers: Headers::new(), - preserved_headers: load_data.headers, - data: load_data.data, - cors: None, - consumer: input_chan, - })).unwrap(); - - let load_response = input_port.recv().unwrap(); - - let last_modified = load_response.metadata.headers.as_ref().and_then(|headers| { - headers.get().map(|&LastModified(ref tm)| tm.clone()) - }); - - let final_url = load_response.metadata.final_url.clone(); - - (Some(HTMLInput::InputUrl(load_response)), final_url, last_modified) - } else { - let doc_url = last_url.unwrap_or_else(|| { - Url::parse("about:blank").unwrap() - }); - (None, doc_url, None) - }; - - // Store the final URL before we start parsing, so that DOM routines - // (e.g. HTMLImageElement::update_image) can resolve relative URLs - // correctly. - { - *page.mut_url() = Some((final_url.clone(), true)); - } + let last_modified = response.metadata.headers.as_ref().and_then(|headers| { + headers.get().map(|&LastModified(ref tm)| tm.clone()) + }); let cx = self.js_context.borrow(); let cx = cx.as_ref().unwrap(); + + let page = Rc::new(Page::new(incomplete.pipeline_id, incomplete.subpage_id.map(|p| p.1), + incomplete.layout_chan, incomplete.window_size, + self.resource_task.clone(), self.storage_task.clone(), + self.constellation_chan.clone(), cx.clone(), + self.devtools_chan.clone(), final_url.clone())); + if root_page_exists { + *self.page.borrow_mut() = Some(page.clone()); + } else if let Some((parent, _)) = incomplete.subpage_id { + let parent_page = self.root_page(); + parent_page.find(parent).expect("received load for subpage with missing parent"); + parent_page.children.borrow_mut().push(page.clone()); + } + // Create the window and document objects. let window = Window::new(cx.ptr, page.clone(), @@ -876,18 +919,21 @@ impl ScriptTask { }); } - if is_javascript { - let evalstr = load_data.url.non_relative_scheme_data().unwrap(); + let is_javascript = incomplete.url.scheme.as_slice() == "javascript"; + let parse_input = if is_javascript { + let evalstr = incomplete.url.non_relative_scheme_data().unwrap(); let jsval = window.r().evaluate_js_on_global_with_result(evalstr); let strval = FromJSValConvertible::from_jsval(self.get_cx(), jsval, StringificationBehavior::Empty); - parser_input = Some(HTMLInput::InputString(strval.unwrap_or("".to_owned()))); + HTMLInput::InputString(strval.unwrap_or("".to_owned())) + } else { + HTMLInput::InputUrl(response) }; - parse_html(document.r(), parser_input.unwrap(), &final_url); + parse_html(document.r(), parse_input, &final_url); document.r().set_ready_state(DocumentReadyState::Interactive); - self.compositor.borrow_mut().set_ready_state(pipeline_id, PerformingLayout); + self.compositor.borrow_mut().set_ready_state(incomplete.pipeline_id, PerformingLayout); // Kick off the initial reflow of the page. debug!("kicking off initial reflow of {:?}", final_url); @@ -898,7 +944,7 @@ impl ScriptTask { { // No more reflow required let mut page_url = page.mut_url(); - *page_url = Some((final_url.clone(), false)); + (*page_url).1 = false; } // https://html.spec.whatwg.org/multipage/#the-end step 4 @@ -927,7 +973,7 @@ impl ScriptTask { title: document.r().Title(), url: final_url }; - chan.send(DevtoolsControlMsg::NewGlobal(pipeline_id, + chan.send(DevtoolsControlMsg::NewGlobal(incomplete.pipeline_id, self.devtools_sender.clone(), page_info)).unwrap(); } @@ -978,7 +1024,7 @@ impl ScriptTask { for node in nodes.iter() { let node_to_dirty = node::from_untrusted_node_address(self.js_runtime.ptr, *node).root(); - let page = get_page(&*self.page.borrow(), pipeline_id); + let page = get_page(&self.root_page(), pipeline_id); let frame = page.frame(); let document = frame.as_ref().unwrap().document.root(); document.r().content_changed(node_to_dirty.r(), @@ -989,7 +1035,7 @@ impl ScriptTask { } ClickEvent(_button, point) => { - let page = get_page(&*self.page.borrow(), pipeline_id); + let page = get_page(&self.root_page(), pipeline_id); let frame = page.frame(); let document = frame.as_ref().unwrap().document.root(); document.r().handle_click_event(self.js_runtime.ptr, _button, point); @@ -998,7 +1044,7 @@ impl ScriptTask { MouseDownEvent(..) => {} MouseUpEvent(..) => {} MouseMoveEvent(point) => { - let page = get_page(&*self.page.borrow(), pipeline_id); + let page = get_page(&self.root_page(), pipeline_id); let frame = page.frame(); let document = frame.as_ref().unwrap().document.root(); let mouse_over_targets = &mut *self.mouse_over_targets.borrow_mut(); @@ -1009,7 +1055,7 @@ impl ScriptTask { } KeyEvent(key, state, modifiers) => { - let page = get_page(&*self.page.borrow(), pipeline_id); + let page = get_page(&self.root_page(), pipeline_id); let frame = page.frame(); let document = frame.as_ref().unwrap().document.root(); document.r().dispatch_key_event( @@ -1028,7 +1074,7 @@ impl ScriptTask { /// The entry point for content to notify that a fragment url has been requested /// for the given pipeline. fn trigger_fragment(&self, pipeline_id: PipelineId, fragment: String) { - let page = get_page(&*self.page.borrow(), pipeline_id); + let page = get_page(&self.root_page(), pipeline_id); match page.find_fragment_node(fragment).root() { Some(node) => { self.scroll_fragment_point(pipeline_id, node.r()); @@ -1040,7 +1086,7 @@ impl ScriptTask { fn handle_resize_event(&self, pipeline_id: PipelineId, new_size: WindowSizeData) { let window = { - let page = get_page(&*self.page.borrow(), pipeline_id); + let page = get_page(&self.root_page(), pipeline_id); page.window_size.set(new_size); let frame = page.frame(); @@ -1081,12 +1127,42 @@ impl ScriptTask { fn handle_reflow_event(&self, pipeline_id: PipelineId) { debug!("script got reflow event"); - let page = get_page(&*self.page.borrow(), pipeline_id); + let page = get_page(&self.root_page(), pipeline_id); let frame = page.frame(); if frame.is_some() { self.force_reflow(&*page); } } + + fn start_page_load(&self, incomplete: InProgressLoad, mut load_data: LoadData) { + let id = incomplete.pipeline_id.clone(); + let subpage = incomplete.subpage_id.clone().map(|p| p.1); + + let script_chan = self.chan.clone(); + let resource_task = self.resource_task.clone(); + + spawn_named(format!("fetch for {:?}", load_data.url), move || { + if load_data.url.scheme.as_slice() == "javascript" { + load_data.url = Url::parse("about:blank").unwrap(); + } + + let (input_chan, input_port) = channel(); + resource_task.send(ControlMsg::Load(NetLoadData { + url: load_data.url, + method: load_data.method, + headers: Headers::new(), + preserved_headers: load_data.headers, + data: load_data.data, + cors: None, + consumer: input_chan, + })).unwrap(); + + let load_response = input_port.recv().unwrap(); + script_chan.send(ScriptMsg::PageFetchComplete(id, subpage, load_response)).unwrap(); + }); + + self.incomplete_loads.borrow_mut().push(incomplete); + } } /// Shuts down layout for the given page tree. diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 7df28d29108..deb5ba30770 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -48,12 +48,13 @@ pub struct NewLayoutInfo { pub new_pipeline_id: PipelineId, pub subpage_id: SubpageId, pub layout_chan: Box, // opaque reference to a LayoutChannel + pub load_data: LoadData, } /// Messages sent from the constellation to the script task pub enum ConstellationControlMsg { - /// Loads a new URL on the specified pipeline. - Load(PipelineId, Option<(PipelineId, SubpageId)>, LoadData), + /// Reactivate an existing pipeline. + Activate(PipelineId), /// Gives a channel and ID to a layout task, as well as the ID of that layout's parent AttachLayout(NewLayoutInfo), /// Window resized. Sends a DOM event eventually, but first we combine events. @@ -111,7 +112,8 @@ pub trait ScriptTaskFactory { storage_task: StorageTask, image_cache_task: ImageCacheTask, devtools_chan: Option, - window_size: WindowSizeData) + window_size: WindowSizeData, + load_data: LoadData) where C: ScriptListener + Send; fn create_layout_channel(_phantom: Option<&mut Self>) -> OpaqueScriptLayoutChannel; fn clone_layout_channel(_phantom: Option<&mut Self>, pair: &OpaqueScriptLayoutChannel) From e2c4f5ed6726ed7434197180b301f74a967d3ffc Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Thu, 19 Feb 2015 13:08:50 -0500 Subject: [PATCH 16/29] Move everything unrelated to the frame tree out of Page and into Document or Window. Reduce the API surface of Page to a bare minimum to allow for easier future removal. --- components/script/devtools.rs | 17 +- .../dom/bindings/codegen/CodegenRust.py | 1 - components/script/dom/bindings/global.rs | 4 +- components/script/dom/bindings/utils.rs | 2 + components/script/dom/browsercontext.rs | 6 +- components/script/dom/console.rs | 5 +- components/script/dom/cssstyledeclaration.rs | 8 +- components/script/dom/document.rs | 153 ++++--- components/script/dom/domparser.rs | 2 +- components/script/dom/htmlbodyelement.rs | 1 + components/script/dom/htmlelement.rs | 1 + components/script/dom/htmlformelement.rs | 2 +- components/script/dom/htmliframeelement.rs | 36 +- components/script/dom/htmlimageelement.rs | 1 + components/script/dom/htmllinkelement.rs | 5 +- components/script/dom/htmlscriptelement.rs | 7 +- components/script/dom/htmlstyleelement.rs | 5 +- components/script/dom/location.rs | 34 +- components/script/dom/macros.rs | 1 + components/script/dom/node.rs | 6 +- components/script/dom/window.rs | 421 ++++++++++++++++-- components/script/page.rs | 389 +--------------- components/script/script_task.rs | 229 ++++------ 23 files changed, 664 insertions(+), 672 deletions(-) diff --git a/components/script/devtools.rs b/components/script/devtools.rs index 5333b9a4cee..051002c7478 100644 --- a/components/script/devtools.rs +++ b/components/script/devtools.rs @@ -11,7 +11,7 @@ use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::DOMRectBinding::{DOMRectMethods}; use dom::bindings::codegen::Bindings::ElementBinding::{ElementMethods}; use dom::node::{Node, NodeHelpers}; -use dom::window::{ScriptHelpers}; +use dom::window::{WindowHelpers, ScriptHelpers}; use dom::element::Element; use dom::document::DocumentHelpers; use page::Page; @@ -24,8 +24,7 @@ use std::rc::Rc; pub fn handle_evaluate_js(page: &Rc, pipeline: PipelineId, eval: String, reply: Sender){ let page = get_page(&*page, pipeline); - let frame = page.frame(); - let window = frame.as_ref().unwrap().window.root(); + let window = page.window().root(); let cx = window.r().get_cx(); let rval = window.r().evaluate_js_on_global_with_result(eval.as_slice()); @@ -49,8 +48,7 @@ pub fn handle_evaluate_js(page: &Rc, pipeline: PipelineId, eval: String, r pub fn handle_get_root_node(page: &Rc, pipeline: PipelineId, reply: Sender) { let page = get_page(&*page, pipeline); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let document = page.document().root(); let node: JSRef = NodeCast::from_ref(document.r()); reply.send(node.summarize()).unwrap(); @@ -58,8 +56,7 @@ pub fn handle_get_root_node(page: &Rc, pipeline: PipelineId, reply: Sender pub fn handle_get_document_element(page: &Rc, pipeline: PipelineId, reply: Sender) { let page = get_page(&*page, pipeline); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let document = page.document().root(); let document_element = document.r().GetDocumentElement().root().unwrap(); let node: JSRef = NodeCast::from_ref(document_element.r()); @@ -68,8 +65,7 @@ pub fn handle_get_document_element(page: &Rc, pipeline: PipelineId, reply: fn find_node_by_unique_id(page: &Rc, pipeline: PipelineId, node_id: String) -> Temporary { let page = get_page(&*page, pipeline); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let document = page.document().root(); let node: JSRef = NodeCast::from_ref(document.r()); for candidate in node.traverse_preorder() { @@ -110,5 +106,6 @@ pub fn handle_modify_attribute(page: &Rc, pipeline: PipelineId, node_id: S pub fn handle_wants_live_notifications(page: &Rc, pipeline_id: PipelineId, send_notifications: bool) { let page = get_page(&*page, pipeline_id); - page.devtools_wants_updates.set(send_notifications); + let window = page.window().root(); + window.r().set_devtools_wants_updates(send_notifications); } diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 203bf60f340..9274c6cafb7 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -4643,7 +4643,6 @@ class CGBindingRoot(CGThing): 'dom::bindings::proxyhandler::{fill_property_descriptor, get_expando_object}', 'dom::bindings::proxyhandler::{get_property_descriptor}', 'dom::bindings::str::ByteString', - 'page::JSPageInfo', 'libc', 'util::str::DOMString', 'std::borrow::ToOwned', diff --git a/components/script/dom/bindings/global.rs b/components/script/dom/bindings/global.rs index dc42d0da6e0..3d17778c6b1 100644 --- a/components/script/dom/bindings/global.rs +++ b/components/script/dom/bindings/global.rs @@ -11,7 +11,7 @@ use dom::bindings::conversions::FromJSValConvertible; use dom::bindings::js::{JS, JSRef, Root, Unrooted}; use dom::bindings::utils::{Reflectable, Reflector}; use dom::workerglobalscope::{WorkerGlobalScope, WorkerGlobalScopeHelpers}; -use dom::window; +use dom::window::{self, WindowHelpers}; use script_task::ScriptChan; use net::resource_task::ResourceTask; @@ -84,7 +84,7 @@ impl<'a> GlobalRef<'a> { /// Get the `ResourceTask` for this global scope. pub fn resource_task(&self) -> ResourceTask { match *self { - GlobalRef::Window(ref window) => window.page().resource_task.clone(), + GlobalRef::Window(ref window) => window.resource_task().clone(), GlobalRef::Worker(ref worker) => worker.resource_task().clone(), } } diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index 56c9b58288d..706c228c0b2 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -45,6 +45,8 @@ use js::JSFUN_CONSTRUCTOR; use js; /// Proxy handler for a WindowProxy. +#[allow(raw_pointer_derive)] +#[derive(Copy)] pub struct WindowProxyHandler(pub *const libc::c_void); #[allow(raw_pointer_derive)] diff --git a/components/script/dom/browsercontext.rs b/components/script/dom/browsercontext.rs index e21533bbfe0..a3af0a8b02a 100644 --- a/components/script/dom/browsercontext.rs +++ b/components/script/dom/browsercontext.rs @@ -71,14 +71,12 @@ impl BrowserContext { fn create_window_proxy(&mut self) { let win = self.active_window().root(); let win = win.r(); - let page = win.page(); - let js_info = page.js_info(); - let WindowProxyHandler(handler) = js_info.as_ref().unwrap().dom_static.windowproxy_handler; + let WindowProxyHandler(handler) = win.windowproxy_handler(); assert!(!handler.is_null()); let parent = win.reflector().get_jsobject(); - let cx = js_info.as_ref().unwrap().js_context.ptr; + let cx = win.get_cx(); let wrapper = with_compartment(cx, parent, || unsafe { WrapperNew(cx, parent, handler) }); diff --git a/components/script/dom/console.rs b/components/script/dom/console.rs index 4dec1c3bd45..6308556ef2e 100644 --- a/components/script/dom/console.rs +++ b/components/script/dom/console.rs @@ -7,6 +7,7 @@ use dom::bindings::codegen::Bindings::ConsoleBinding::ConsoleMethods; use dom::bindings::global::{GlobalRef, GlobalField}; use dom::bindings::js::{JSRef, Temporary}; use dom::bindings::utils::{Reflector, reflect_dom_object}; +use dom::window::WindowHelpers; use devtools_traits::{DevtoolsControlMsg, ConsoleMessage}; use util::str::DOMString; @@ -66,8 +67,8 @@ fn propagate_console_msg(console: &JSRef, console_message: ConsoleMessa let global = console.global.root(); match global.r() { GlobalRef::Window(window_ref) => { - let pipelineId = window_ref.page().id; - console.global.root().r().as_window().page().devtools_chan.as_ref().map(|chan| { + let pipelineId = window_ref.pipeline(); + console.global.root().r().as_window().devtools_chan().as_ref().map(|chan| { chan.send(DevtoolsControlMsg::SendConsoleMessage( pipelineId, console_message.clone())).unwrap(); }); diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs index 2337e6c061a..e1be98f99af 100644 --- a/components/script/dom/cssstyledeclaration.rs +++ b/components/script/dom/cssstyledeclaration.rs @@ -14,7 +14,7 @@ use dom::document::DocumentHelpers; use dom::element::{Element, ElementHelpers, StylePriority}; use dom::htmlelement::HTMLElement; use dom::node::{window_from_node, document_from_node, NodeDamage, Node}; -use dom::window::Window; +use dom::window::{Window, WindowHelpers}; use util::str::DOMString; use string_cache::Atom; use style::properties::{is_supported_property, longhands_from_shorthand, parse_style_attribute}; @@ -222,9 +222,8 @@ impl<'a> CSSStyleDeclarationMethods for JSRef<'a, CSSStyleDeclaration> { let owner = self.owner.root(); let window = window_from_node(owner.r()).root(); let window = window.r(); - let page = window.page(); let decl_block = parse_style_attribute(synthesized_declaration.as_slice(), - &page.get_url()); + &window.get_url()); // Step 7 if decl_block.normal.len() == 0 { @@ -271,9 +270,8 @@ impl<'a> CSSStyleDeclarationMethods for JSRef<'a, CSSStyleDeclaration> { let owner = self.owner.root(); let window = window_from_node(owner.r()).root(); let window = window.r(); - let page = window.page(); let decl_block = parse_style_attribute(property.as_slice(), - &page.get_url()); + &window.get_url()); let element: JSRef = ElementCast::from_ref(owner.r()); // Step 5 diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 6e5413ebdc8..1d611a6a643 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -11,7 +11,6 @@ use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter; -use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::codegen::InheritTypes::{DocumentDerived, EventCast, HTMLElementCast}; use dom::bindings::codegen::InheritTypes::{HTMLHeadElementCast, TextCast, ElementCast}; use dom::bindings::codegen::InheritTypes::{DocumentTypeCast, HTMLHtmlElementCast, NodeCast}; @@ -59,12 +58,14 @@ use dom::treewalker::TreeWalker; use dom::uievent::UIEvent; use dom::window::{Window, WindowHelpers}; +use layout_interface::{HitTestResponse, MouseOverResponse}; use msg::compositor_msg::ScriptListener; use msg::constellation_msg::{Key, KeyState, KeyModifiers}; use msg::constellation_msg::{SUPER, ALT, SHIFT, CONTROL}; use net::resource_task::ControlMsg::{SetCookiesForUrl, GetCookiesForUrl}; use net::cookie_storage::CookieSource::NonHTTP; use script_task::Runnable; +use script_traits::UntrustedNodeAddress; use util::namespace; use util::str::{DOMString, split_html_space_chars}; use layout_interface::{ReflowGoal, ReflowQueryType}; @@ -201,6 +202,8 @@ pub trait DocumentHelpers<'a> { fn register_named_element(self, element: JSRef, id: Atom); fn load_anchor_href(self, href: DOMString); fn find_fragment_node(self, fragid: DOMString) -> Option>; + fn hit_test(self, point: &Point2D) -> Option; + fn get_nodes_under_mouse(self, point: &Point2D) -> Vec; fn set_ready_state(self, state: DocumentReadyState); fn get_focused_element(self) -> Option>; fn begin_focus_transaction(self); @@ -264,7 +267,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { Quirks => { let window = self.window.root(); let window = window.r(); - let LayoutChan(ref layout_chan) = window.page().layout_chan; + let LayoutChan(ref layout_chan) = window.layout_chan(); layout_chan.send(Msg::SetQuirksMode).unwrap(); } NoQuirks | LimitedQuirks => {} @@ -377,6 +380,44 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { }) } + fn hit_test(self, point: &Point2D) -> Option { + let root = self.GetDocumentElement().root(); + let root = match root.r() { + Some(root) => root, + None => return None, + }; + let root: JSRef = NodeCast::from_ref(root); + let win = self.window.root(); + let address = match win.r().layout().hit_test(root.to_trusted_node_address(), *point) { + Ok(HitTestResponse(node_address)) => { + Some(node_address) + } + Err(()) => { + debug!("layout query error"); + None + } + }; + address + } + + fn get_nodes_under_mouse(self, point: &Point2D) -> Vec { + let root = self.GetDocumentElement().root(); + let root = match root.r() { + Some(root) => root, + None => return vec!(), + }; + let root: JSRef = NodeCast::from_ref(root); + let win = self.window.root(); + match win.r().layout().mouse_over(root.to_trusted_node_address(), *point) { + Ok(MouseOverResponse(node_address)) => { + node_address + } + Err(()) => { + vec!() + } + } + } + // https://html.spec.whatwg.org/multipage/dom.html#current-document-readiness fn set_ready_state(self, state: DocumentReadyState) { self.ready_state.set(state); @@ -416,7 +457,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { /// Sends this document's title to the compositor. fn send_title_to_compositor(self) { let window = self.window().root(); - window.r().page().send_title_to_compositor(); + window.r().compositor().set_title(window.r().pipeline(), Some(self.Title())); } fn dirty_all_nodes(self) { @@ -428,10 +469,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { fn handle_click_event(self, js_runtime: *mut JSRuntime, _button: uint, point: Point2D) { debug!("ClickEvent: clicked at {:?}", point); - let window = self.window.root(); - let window = window.r(); - let page = window.page(); - let node = match page.hit_test(&point) { + let node = match self.hit_test(&point) { Some(node_address) => { debug!("node address is {:?}", node_address.0); node::from_untrusted_node_address(js_runtime, node_address) @@ -460,48 +498,40 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { return; } - match *page.frame() { - Some(ref frame) => { - let window = frame.window.root(); - let doc = window.r().Document().root(); - doc.r().begin_focus_transaction(); + self.begin_focus_transaction(); - // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-click - let x = point.x as i32; - let y = point.y as i32; - let event = MouseEvent::new(window.r(), - "click".to_owned(), - true, - true, - Some(window.r()), - 0i32, - x, y, x, y, - false, false, false, false, - 0i16, - None).root(); - let event: JSRef = EventCast::from_ref(event.r()); + let window = self.window.root(); - // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#trusted-events - event.set_trusted(true); - // https://html.spec.whatwg.org/multipage/interaction.html#run-authentic-click-activation-steps - el.authentic_click_activation(event); + // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-click + let x = point.x as i32; + let y = point.y as i32; + let event = MouseEvent::new(window.r(), + "click".to_owned(), + true, + true, + Some(window.r()), + 0i32, + x, y, x, y, + false, false, false, false, + 0i16, + None).root(); + let event: JSRef = EventCast::from_ref(event.r()); - doc.r().commit_focus_transaction(); - window.r().flush_layout(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); - } - None => {} - } + // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#trusted-events + event.set_trusted(true); + // https://html.spec.whatwg.org/multipage/interaction.html#run-authentic-click-activation-steps + el.authentic_click_activation(event); + + self.commit_focus_transaction(); + window.r().flush_layout(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); } /// Return need force reflow or not fn handle_mouse_move_event(self, js_runtime: *mut JSRuntime, point: Point2D, prev_mouse_over_targets: &mut Vec>) -> bool { - let window = self.window.root(); - let window = window.r(); - let page = window.page(); let mut needs_reflow = false; // Build a list of elements that are currently under the mouse. - let mouse_over_addresses = page.get_nodes_under_mouse(&point); + let mouse_over_addresses = self.get_nodes_under_mouse(&point); let mouse_over_targets: Vec> = mouse_over_addresses.iter() .filter_map(|node_address| { let node = node::from_untrusted_node_address(js_runtime, *node_address); @@ -534,27 +564,24 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { let top_most_node = node::from_untrusted_node_address(js_runtime, mouse_over_addresses[0]).root(); - if let Some(ref frame) = *page.frame() { - let window = frame.window.root(); + let x = point.x.to_i32().unwrap_or(0); + let y = point.y.to_i32().unwrap_or(0); - let x = point.x.to_i32().unwrap_or(0); - let y = point.y.to_i32().unwrap_or(0); + let window = self.window.root(); + let mouse_event = MouseEvent::new(window.r(), + "mousemove".to_owned(), + true, + true, + Some(window.r()), + 0i32, + x, y, x, y, + false, false, false, false, + 0i16, + None).root(); - let mouse_event = MouseEvent::new(window.r(), - "mousemove".to_owned(), - true, - true, - Some(window.r()), - 0i32, - x, y, x, y, - false, false, false, false, - 0i16, - None).root(); - - let event: JSRef = EventCast::from_ref(mouse_event.r()); - let target: JSRef = EventTargetCast::from_ref(top_most_node.r()); - event.fire(target); - } + let event: JSRef = EventCast::from_ref(mouse_event.r()); + let target: JSRef = EventTargetCast::from_ref(top_most_node.r()); + event.fire(target); } // Store the current mouse over targets for next frame @@ -1259,7 +1286,7 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { fn Location(self) -> Temporary { let window = self.window.root(); let window = window.r(); - self.location.or_init(|| Location::new(window, window.page_clone())) + self.location.or_init(|| Location::new(window)) } // http://dom.spec.whatwg.org/#dom-parentnode-children @@ -1298,10 +1325,8 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { return Err(Security); } let window = self.window.root(); - let window = window.r(); - let page = window.page(); let (tx, rx) = channel(); - let _ = page.resource_task.send(GetCookiesForUrl(url, tx, NonHTTP)); + let _ = window.r().resource_task().send(GetCookiesForUrl(url, tx, NonHTTP)); let cookies = rx.recv().unwrap(); Ok(cookies.unwrap_or("".to_owned())) } @@ -1314,9 +1339,7 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { return Err(Security); } let window = self.window.root(); - let window = window.r(); - let page = window.page(); - let _ = page.resource_task.send(SetCookiesForUrl(url, cookie, NonHTTP)); + let _ = window.r().resource_task().send(SetCookiesForUrl(url, cookie, NonHTTP)); Ok(()) } diff --git a/components/script/dom/domparser.rs b/components/script/dom/domparser.rs index 91d5d96a615..88429277f19 100644 --- a/components/script/dom/domparser.rs +++ b/components/script/dom/domparser.rs @@ -12,7 +12,7 @@ use dom::bindings::js::{JS, JSRef, Temporary}; use dom::bindings::utils::{Reflector, reflect_dom_object}; use dom::document::{Document, DocumentHelpers, IsHTMLDocument}; use dom::document::DocumentSource; -use dom::window::Window; +use dom::window::{Window, WindowHelpers}; use parse::html::{HTMLInput, parse_html}; use util::str::DOMString; diff --git a/components/script/dom/htmlbodyelement.rs b/components/script/dom/htmlbodyelement.rs index e609169ff05..eaf683ce3bd 100644 --- a/components/script/dom/htmlbodyelement.rs +++ b/components/script/dom/htmlbodyelement.rs @@ -16,6 +16,7 @@ use dom::eventtarget::{EventTarget, EventTargetTypeId, EventTargetHelpers}; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeTypeId, window_from_node}; use dom::virtualmethods::VirtualMethods; +use dom::window::WindowHelpers; use cssparser::RGBA; use util::str::{self, DOMString}; diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs index dd5cd1cb7e0..f3528185211 100644 --- a/components/script/dom/htmlelement.rs +++ b/components/script/dom/htmlelement.rs @@ -26,6 +26,7 @@ use dom::htmlmediaelement::HTMLMediaElementTypeId; use dom::htmltablecellelement::HTMLTableCellElementTypeId; use dom::node::{Node, NodeTypeId, window_from_node}; use dom::virtualmethods::VirtualMethods; +use dom::window::WindowHelpers; use util::str::DOMString; diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index ddc8d42b661..9c2148334e2 100644 --- a/components/script/dom/htmlformelement.rs +++ b/components/script/dom/htmlformelement.rs @@ -215,7 +215,7 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> { } // This is wrong. https://html.spec.whatwg.org/multipage/forms.html#planned-navigation - win.r().script_chan().send(ScriptMsg::TriggerLoad(win.r().page().id, load_data)).unwrap(); + win.r().script_chan().send(ScriptMsg::TriggerLoad(win.r().pipeline(), load_data)).unwrap(); } fn get_form_dataset<'b>(self, submitter: Option>) -> Vec { diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index b4f9b577b7b..3afcb2f4af2 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -20,8 +20,8 @@ use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeHelpers, NodeTypeId, window_from_node}; use dom::urlhelper::UrlHelper; use dom::virtualmethods::VirtualMethods; -use dom::window::Window; -use page::{IterablePage, Page}; +use dom::window::{Window, WindowHelpers}; +use page::IterablePage; use msg::constellation_msg::{PipelineId, SubpageId, ConstellationChan}; use msg::constellation_msg::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed}; @@ -62,7 +62,7 @@ pub trait HTMLIFrameElementHelpers { fn get_url(self) -> Option; /// http://www.whatwg.org/html/#process-the-iframe-attributes fn process_the_iframe_attributes(self); - fn generate_new_subpage_id(self, page: &Page) -> (SubpageId, Option); + fn generate_new_subpage_id(self) -> (SubpageId, Option); } impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> { @@ -78,15 +78,16 @@ impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> { None } else { let window = window_from_node(self).root(); - UrlParser::new().base_url(&window.r().page().get_url()) + UrlParser::new().base_url(&window.r().get_url()) .parse(url.as_slice()).ok() } }) } - fn generate_new_subpage_id(self, page: &Page) -> (SubpageId, Option) { + fn generate_new_subpage_id(self) -> (SubpageId, Option) { let old_subpage_id = self.subpage_id.get(); - let subpage_id = page.get_next_subpage_id(); + let win = window_from_node(self).root(); + let subpage_id = win.r().get_next_subpage_id(); self.subpage_id.set(Some(subpage_id)); (subpage_id, old_subpage_id) } @@ -105,14 +106,13 @@ impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> { let window = window_from_node(self).root(); let window = window.r(); - let page = window.page(); - let (new_subpage_id, old_subpage_id) = self.generate_new_subpage_id(page); + let (new_subpage_id, old_subpage_id) = self.generate_new_subpage_id(); - self.containing_page_pipeline_id.set(Some(page.id)); + self.containing_page_pipeline_id.set(Some(window.pipeline())); - let ConstellationChan(ref chan) = page.constellation_chan; + let ConstellationChan(ref chan) = window.constellation_chan(); chan.send(ConstellationMsg::ScriptLoadedURLInIFrame(url, - page.id, + window.pipeline(), new_subpage_id, old_subpage_id, sandboxed)).unwrap(); @@ -172,14 +172,10 @@ impl<'a> HTMLIFrameElementMethods for JSRef<'a, HTMLIFrameElement> { let window = window_from_node(self).root(); let window = window.r(); let children = window.page().children.borrow(); - let child = children.iter().find(|child| { - child.subpage_id.unwrap() == subpage_id - }); - child.and_then(|page| { - page.frame.borrow().as_ref().map(|frame| { - Temporary::new(frame.window.clone()) - }) - }) + children.iter().find(|child| { + let window = child.window().root(); + window.r().subpage() == Some(subpage_id) + }).map(|page| page.window()) }) } @@ -189,7 +185,7 @@ impl<'a> HTMLIFrameElementMethods for JSRef<'a, HTMLIFrameElement> { Some(self_url) => self_url, None => return None, }; - let win_url = window_from_node(self).root().r().page().get_url(); + let win_url = window_from_node(self).root().r().get_url(); if UrlHelper::SameOrigin(&self_url, &win_url) { Some(window.r().Document()) diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 2f8d09bb759..6b2f0b3dc2f 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -17,6 +17,7 @@ use dom::element::ElementTypeId; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeTypeId, NodeHelpers, NodeDamage, window_from_node}; use dom::virtualmethods::VirtualMethods; +use dom::window::WindowHelpers; use net::image_cache_task; use util::geometry::to_px; use util::str::DOMString; diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index 4f8f2aa14b8..ab0c45a22d5 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -17,6 +17,7 @@ use dom::element::ElementTypeId; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeHelpers, NodeTypeId, window_from_node}; use dom::virtualmethods::VirtualMethods; +use dom::window::WindowHelpers; use layout_interface::{LayoutChan, Msg}; use util::str::{DOMString, HTML_SPACE_CHARACTERS}; @@ -130,9 +131,9 @@ impl<'a> PrivateHTMLLinkElementHelpers for JSRef<'a, HTMLLinkElement> { fn handle_stylesheet_url(self, href: &str) { let window = window_from_node(self).root(); let window = window.r(); - match UrlParser::new().base_url(&window.page().get_url()).parse(href) { + match UrlParser::new().base_url(&window.get_url()).parse(href) { Ok(url) => { - let LayoutChan(ref layout_chan) = window.page().layout_chan; + let LayoutChan(ref layout_chan) = window.layout_chan(); layout_chan.send(Msg::LoadStylesheet(url)).unwrap(); } Err(e) => debug!("Parsing url {} failed: {}", href, e) diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 853d6709fcb..e9809a6e2cb 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -25,7 +25,7 @@ use dom::element::ElementTypeId; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeHelpers, NodeTypeId, document_from_node, window_from_node, CloneChildrenFlag}; use dom::virtualmethods::VirtualMethods; -use dom::window::ScriptHelpers; +use dom::window::{WindowHelpers, ScriptHelpers}; use script_task::{ScriptMsg, Runnable}; use encoding::all::UTF_8; @@ -214,8 +214,7 @@ impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> { // Step 14. let window = window_from_node(self).root(); let window = window.r(); - let page = window.page(); - let base_url = page.get_url(); + let base_url = window.get_url(); let load = match element.get_attribute(ns!(""), &atom!("src")).root() { // Step 14. @@ -243,7 +242,7 @@ impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> { // state of the element's `crossorigin` content attribute, the origin being // the origin of the script element's node document, and the default origin // behaviour set to taint. - ScriptOrigin::External(load_whole_resource(&page.resource_task, url)) + ScriptOrigin::External(load_whole_resource(&window.resource_task(), url)) } } }, diff --git a/components/script/dom/htmlstyleelement.rs b/components/script/dom/htmlstyleelement.rs index 6fc3e1a2039..945831a35b1 100644 --- a/components/script/dom/htmlstyleelement.rs +++ b/components/script/dom/htmlstyleelement.rs @@ -12,6 +12,7 @@ use dom::element::ElementTypeId; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeHelpers, NodeTypeId, window_from_node}; use dom::virtualmethods::VirtualMethods; +use dom::window::WindowHelpers; use layout_interface::{LayoutChan, Msg}; use util::str::DOMString; use style::stylesheets::{Origin, Stylesheet}; @@ -52,11 +53,11 @@ impl<'a> StyleElementHelpers for JSRef<'a, HTMLStyleElement> { let win = window_from_node(node).root(); let win = win.r(); - let url = win.page().get_url(); + let url = win.get_url(); let data = node.GetTextContent().expect("Element.textContent must be a string"); let sheet = Stylesheet::from_str(data.as_slice(), url, Origin::Author); - let LayoutChan(ref layout_chan) = win.page().layout_chan; + let LayoutChan(ref layout_chan) = win.layout_chan(); layout_chan.send(Msg::AddStylesheet(sheet)).unwrap(); } } diff --git a/components/script/dom/location.rs b/components/script/dom/location.rs index 614b623e5b9..13cc1a05c65 100644 --- a/components/script/dom/location.rs +++ b/components/script/dom/location.rs @@ -5,33 +5,31 @@ use dom::bindings::codegen::Bindings::LocationBinding; use dom::bindings::codegen::Bindings::LocationBinding::LocationMethods; use dom::bindings::global::GlobalRef; -use dom::bindings::js::{JSRef, Temporary}; +use dom::bindings::js::{JS, JSRef, Temporary}; use dom::bindings::utils::{Reflector, reflect_dom_object}; use dom::urlhelper::UrlHelper; use dom::window::Window; use dom::window::WindowHelpers; -use page::Page; use util::str::DOMString; - -use std::rc::Rc; +use url::Url; #[dom_struct] pub struct Location { reflector_: Reflector, - page: Rc, + window: JS, } impl Location { - fn new_inherited(page: Rc) -> Location { + fn new_inherited(window: JSRef) -> Location { Location { reflector_: Reflector::new(), - page: page + window: JS::from_rooted(window) } } - pub fn new(window: JSRef, page: Rc) -> Temporary { - reflect_dom_object(box Location::new_inherited(page), + pub fn new(window: JSRef) -> Temporary { + reflect_dom_object(box Location::new_inherited(window), GlobalRef::Window(window), LocationBinding::Wrap) } @@ -40,11 +38,11 @@ impl Location { impl<'a> LocationMethods for JSRef<'a, Location> { // https://html.spec.whatwg.org/multipage/browsers.html#dom-location-assign fn Assign(self, url: DOMString) { - self.page.frame().as_ref().unwrap().window.root().r().load_url(url); + self.window.root().r().load_url(url); } fn Href(self) -> DOMString { - UrlHelper::Href(&self.page.get_url()) + UrlHelper::Href(&self.get_url()) } fn Stringify(self) -> DOMString { @@ -52,11 +50,21 @@ impl<'a> LocationMethods for JSRef<'a, Location> { } fn Search(self) -> DOMString { - UrlHelper::Search(&self.page.get_url()) + UrlHelper::Search(&self.get_url()) } fn Hash(self) -> DOMString { - UrlHelper::Hash(&self.page.get_url()) + UrlHelper::Hash(&self.get_url()) } } +trait PrivateLocationHelpers { + fn get_url(self) -> Url; +} + +impl<'a> PrivateLocationHelpers for JSRef<'a, Location> { + fn get_url(self) -> Url { + let window = self.window.root(); + window.r().get_url() + } +} diff --git a/components/script/dom/macros.rs b/components/script/dom/macros.rs index d00aa9961b1..b1cba9ccd3e 100644 --- a/components/script/dom/macros.rs +++ b/components/script/dom/macros.rs @@ -80,6 +80,7 @@ macro_rules! make_url_or_base_getter( fn $attr(self) -> DOMString { use dom::element::{Element, AttributeHandlers}; use dom::bindings::codegen::InheritTypes::ElementCast; + use dom::window::WindowHelpers; #[allow(unused_imports)] use std::ascii::AsciiExt; let element: JSRef = ElementCast::from_ref(self); diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index a7b5e5484d6..7526c2490d2 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -41,7 +41,7 @@ use dom::nodelist::NodeList; use dom::processinginstruction::ProcessingInstruction; use dom::text::Text; use dom::virtualmethods::{VirtualMethods, vtable_for}; -use dom::window::Window; +use dom::window::{Window, WindowHelpers}; use geom::rect::Rect; use layout_interface::{LayoutChan, Msg}; use devtools_traits::NodeInfo; @@ -737,11 +737,11 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { } fn get_bounding_content_box(self) -> Rect { - window_from_node(self).root().r().page().content_box_query(self.to_trusted_node_address()) + window_from_node(self).root().r().content_box_query(self.to_trusted_node_address()) } fn get_content_boxes(self) -> Vec> { - window_from_node(self).root().r().page().content_boxes_query(self.to_trusted_node_address()) + window_from_node(self).root().r().content_boxes_query(self.to_trusted_node_address()) } // http://dom.spec.whatwg.org/#dom-parentnode-queryselector diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 27b297e21f2..30b1e6baaa0 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -8,50 +8,59 @@ use dom::bindings::codegen::Bindings::FunctionBinding::Function; use dom::bindings::codegen::Bindings::WindowBinding; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; -use dom::bindings::codegen::InheritTypes::EventTargetCast; +use dom::bindings::codegen::InheritTypes::{NodeCast, EventTargetCast}; use dom::bindings::global::global_object_for_js_object; use dom::bindings::error::{report_pending_exception, Fallible}; use dom::bindings::error::Error::InvalidCharacter; use dom::bindings::global::GlobalRef; -use dom::bindings::js::{MutNullableJS, JSRef, Temporary}; -use dom::bindings::utils::Reflectable; +use dom::bindings::js::{MutNullableJS, JSRef, Temporary, OptionalRootable, RootedReference}; +use dom::bindings::utils::{GlobalStaticData, Reflectable, WindowProxyHandler}; use dom::browsercontext::BrowserContext; use dom::console::Console; -use dom::document::Document; +use dom::document::{Document, DocumentHelpers}; use dom::element::Element; use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId}; use dom::location::Location; use dom::navigator::Navigator; -use dom::node::window_from_node; +use dom::node::{window_from_node, Node, TrustedNodeAddress, NodeHelpers}; use dom::performance::Performance; use dom::screen::Screen; use dom::storage::Storage; -use layout_interface::{ReflowGoal, ReflowQueryType}; +use layout_interface::{ReflowGoal, ReflowQueryType, LayoutRPC, LayoutChan, Reflow, Msg}; +use layout_interface::{ContentBoxResponse, ContentBoxesResponse}; use page::Page; use script_task::{TimerSource, ScriptChan}; use script_task::ScriptMsg; use script_traits::ScriptControlChan; use timers::{IsInterval, TimerId, TimerManager, TimerCallback}; +use devtools_traits::DevtoolsControlChan; use msg::compositor_msg::ScriptListener; -use msg::constellation_msg::LoadData; +use msg::constellation_msg::{LoadData, PipelineId, SubpageId, ConstellationChan, WindowSizeData}; use net::image_cache_task::ImageCacheTask; +use net::resource_task::ResourceTask; use net::storage_task::StorageTask; +use util::geometry::{self, Au, MAX_RECT}; use util::str::{DOMString,HTML_SPACE_CHARACTERS}; +use geom::{Point2D, Rect, Size2D}; use js::jsapi::JS_EvaluateUCScript; use js::jsapi::JSContext; use js::jsapi::{JS_GC, JS_GetRuntime}; use js::jsval::{JSVal, UndefinedValue}; -use js::rust::with_compartment; +use js::rust::{Cx, with_compartment}; use url::{Url, UrlParser}; use libc; use rustc_serialize::base64::{FromBase64, ToBase64, STANDARD}; -use std::cell::{Ref, RefMut}; +use std::cell::{Cell, Ref, RefMut}; use std::default::Default; use std::ffi::CString; +use std::mem::replace; +use std::num::Float; use std::rc::Rc; +use std::sync::mpsc::{channel, Receiver}; +use std::sync::mpsc::TryRecvError::{Empty, Disconnected}; use time; #[dom_struct] @@ -71,18 +80,80 @@ pub struct Window { screen: MutNullableJS, session_storage: MutNullableJS, timers: TimerManager, + + /// For providing instructions to an optional devtools server. + devtools_chan: Option, + + /// A flag to indicate whether the developer tools have requested live updates of + /// page changes. + devtools_wants_updates: Cell, + + next_subpage_id: Cell, + + /// Pending resize event, if any. + resize_event: Cell>, + + /// Pipeline id associated with this page. + id: PipelineId, + + /// Subpage id associated with this page, if any. + subpage_id: Option, + + /// Unique id for last reflow request; used for confirming completion reply. + last_reflow_id: Cell, + + /// Global static data related to the DOM. + dom_static: GlobalStaticData, + + /// The JavaScript context. + js_context: DOMRefCell>>, + + /// A handle for communicating messages to the layout task. + layout_chan: LayoutChan, + + /// A handle to perform RPC calls into the layout, quickly. + layout_rpc: Box, + + /// The port that we will use to join layout. If this is `None`, then layout is not running. + layout_join_port: DOMRefCell>>, + + /// The current size of the window, in pixels. + window_size: Cell, + + /// Associated resource task for use by DOM objects like XMLHttpRequest + resource_task: ResourceTask, + + /// A handle for communicating messages to the storage task. + storage_task: StorageTask, + + /// A handle for communicating messages to the constellation task. + constellation_chan: ConstellationChan, + + /// Pending scroll to fragment event, if any + fragment_name: DOMRefCell>, + + /// An enlarged rectangle around the page contents visible in the viewport, used + /// to prevent creating display list items for content that is far away from the viewport. + page_clip_rect: Cell>, } impl Window { pub fn get_cx(&self) -> *mut JSContext { - let js_info = self.page().js_info(); - (*js_info.as_ref().unwrap().js_context).ptr + self.js_context.borrow().as_ref().unwrap().ptr } pub fn script_chan(&self) -> Box { self.script_chan.clone() } + pub fn pipeline(&self) -> PipelineId { + self.id.clone() + } + + pub fn subpage(&self) -> Option { + self.subpage_id.clone() + } + pub fn control_chan<'a>(&'a self) -> &'a ScriptControlChan { &self.control_chan } @@ -103,16 +174,8 @@ impl Window { &*self.page } - pub fn page_clone(&self) -> Rc { - self.page.clone() - } - - pub fn get_url(&self) -> Url { - self.page().get_url() - } - pub fn storage_task(&self) -> StorageTask { - self.page().storage_task.clone() + self.storage_task.clone() } } @@ -197,12 +260,11 @@ impl<'a> WindowMethods for JSRef<'a, Window> { } fn Close(self) { - self.script_chan.send(ScriptMsg::ExitWindow(self.page.id.clone())).unwrap(); + self.script_chan.send(ScriptMsg::ExitWindow(self.id.clone())).unwrap(); } fn Document(self) -> Temporary { - let frame = self.page().frame(); - Temporary::new(frame.as_ref().unwrap().document.clone()) + self.browser_context().as_ref().unwrap().active_document() } fn Location(self) -> Temporary { @@ -230,7 +292,7 @@ impl<'a> WindowMethods for JSRef<'a, Window> { args, timeout, IsInterval::NonInterval, - TimerSource::FromWindow(self.page.id.clone()), + TimerSource::FromWindow(self.id.clone()), self.script_chan.clone()) } @@ -239,7 +301,7 @@ impl<'a> WindowMethods for JSRef<'a, Window> { args, timeout, IsInterval::NonInterval, - TimerSource::FromWindow(self.page.id.clone()), + TimerSource::FromWindow(self.id.clone()), self.script_chan.clone()) } @@ -252,7 +314,7 @@ impl<'a> WindowMethods for JSRef<'a, Window> { args, timeout, IsInterval::Interval, - TimerSource::FromWindow(self.page.id.clone()), + TimerSource::FromWindow(self.id.clone()), self.script_chan.clone()) } @@ -261,7 +323,7 @@ impl<'a> WindowMethods for JSRef<'a, Window> { args, timeout, IsInterval::Interval, - TimerSource::FromWindow(self.page.id.clone()), + TimerSource::FromWindow(self.id.clone()), self.script_chan.clone()) } @@ -330,10 +392,35 @@ impl<'a> WindowMethods for JSRef<'a, Window> { } pub trait WindowHelpers { + fn clear_js_context(self); + fn clear_js_context_for_script_deallocation(self); fn flush_layout(self, goal: ReflowGoal, query: ReflowQueryType); fn init_browser_context(self, doc: JSRef, frame_element: Option>); fn load_url(self, href: DOMString); fn handle_fire_timer(self, timer_id: TimerId); + fn reflow(self, goal: ReflowGoal, query_type: ReflowQueryType); + fn join_layout(self); + fn layout(&self) -> &LayoutRPC; + fn content_box_query(self, content_box_request: TrustedNodeAddress) -> Rect; + fn content_boxes_query(self, content_boxes_request: TrustedNodeAddress) -> Vec>; + fn handle_reflow_complete_msg(self, reflow_id: uint); + fn handle_resize_inactive_msg(self, new_size: WindowSizeData); + fn set_fragment_name(self, fragment: Option); + fn steal_fragment_name(self) -> Option; + fn set_window_size(self, size: WindowSizeData); + fn window_size(self) -> WindowSizeData; + fn get_url(self) -> Url; + fn resource_task(self) -> ResourceTask; + fn devtools_chan(self) -> Option; + fn layout_chan(self) -> LayoutChan; + fn constellation_chan(self) -> ConstellationChan; + fn windowproxy_handler(self) -> WindowProxyHandler; + fn get_next_subpage_id(self) -> SubpageId; + fn layout_is_idle(self) -> bool; + fn set_resize_event(self, event: WindowSizeData); + fn steal_resize_event(self) -> Option; + fn set_page_clip_rect_with_new_viewport(self, viewport: Rect) -> bool; + fn set_devtools_wants_updates(self, value: bool); fn IndexedGetter(self, _index: u32, _found: &mut bool) -> Option>; fn thaw(self); fn freeze(self); @@ -373,8 +460,132 @@ impl<'a, T: Reflectable> ScriptHelpers for JSRef<'a, T> { } impl<'a> WindowHelpers for JSRef<'a, Window> { + fn clear_js_context(self) { + *self.js_context.borrow_mut() = None; + *self.browser_context.borrow_mut() = None; + } + + #[allow(unsafe_blocks)] + fn clear_js_context_for_script_deallocation(self) { + unsafe { + *self.js_context.borrow_for_script_deallocation() = None; + *self.browser_context.borrow_for_script_deallocation() = None; + } + } + fn flush_layout(self, goal: ReflowGoal, query: ReflowQueryType) { - self.page().flush_layout(goal, query); + self.reflow(goal, query); + } + + /// Reflows the page if it's possible to do so and the page is dirty. This method will wait + /// for the layout thread to complete (but see the `TODO` below). If there is no window size + /// yet, the page is presumed invisible and no reflow is performed. + /// + /// TODO(pcwalton): Only wait for style recalc, since we have off-main-thread layout. + fn reflow(self, goal: ReflowGoal, query_type: ReflowQueryType) { + let document = self.Document().root(); + let root = document.r().GetDocumentElement().root(); + let root = match root.r() { + Some(root) => root, + None => return, + }; + + debug!("script: performing reflow for goal {:?}", goal); + + let root: JSRef = NodeCast::from_ref(root); + if !root.get_has_dirty_descendants() { + debug!("root has no dirty descendants; avoiding reflow"); + return + } + + debug!("script: performing reflow for goal {:?}", goal); + + // Layout will let us know when it's done. + let (join_chan, join_port) = channel(); + + { + let mut layout_join_port = self.layout_join_port.borrow_mut(); + *layout_join_port = Some(join_port); + } + + let last_reflow_id = &self.last_reflow_id; + last_reflow_id.set(last_reflow_id.get() + 1); + + let window_size = self.window_size.get(); + + // Send new document and relevant styles to layout. + let reflow = box Reflow { + document_root: root.to_trusted_node_address(), + url: self.get_url(), + iframe: self.subpage_id.is_some(), + goal: goal, + window_size: window_size, + script_chan: self.control_chan.clone(), + script_join_chan: join_chan, + id: last_reflow_id.get(), + query_type: query_type, + page_clip_rect: self.page_clip_rect.get(), + }; + + let LayoutChan(ref chan) = self.layout_chan; + chan.send(Msg::Reflow(reflow)).unwrap(); + + debug!("script: layout forked"); + + self.join_layout(); + } + + // FIXME(cgaebel): join_layout is racey. What if the compositor triggers a + // reflow between the "join complete" message and returning from this + // function? + + /// Sends a ping to layout and waits for the response. The response will arrive when the + /// layout task has finished any pending request messages. + fn join_layout(self) { + let mut layout_join_port = self.layout_join_port.borrow_mut(); + if let Some(join_port) = replace(&mut *layout_join_port, None) { + match join_port.try_recv() { + Err(Empty) => { + info!("script: waiting on layout"); + join_port.recv().unwrap(); + } + Ok(_) => {} + Err(Disconnected) => { + panic!("Layout task failed while script was waiting for a result."); + } + } + + debug!("script: layout joined") + } + } + + fn layout(&self) -> &LayoutRPC { + &*self.layout_rpc + } + + fn content_box_query(self, content_box_request: TrustedNodeAddress) -> Rect { + self.flush_layout(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxQuery(content_box_request)); + self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? + let ContentBoxResponse(rect) = self.layout_rpc.content_box(); + rect + } + + fn content_boxes_query(self, content_boxes_request: TrustedNodeAddress) -> Vec> { + self.flush_layout(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxesQuery(content_boxes_request)); + self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? + let ContentBoxesResponse(rects) = self.layout_rpc.content_boxes(); + rects + } + + fn handle_reflow_complete_msg(self, reflow_id: uint) { + let last_reflow_id = self.last_reflow_id.get(); + if last_reflow_id == reflow_id { + *self.layout_join_port.borrow_mut() = None; + } + } + + fn handle_resize_inactive_msg(self, new_size: WindowSizeData) { + self.window_size.set(new_size); } fn init_browser_context(self, doc: JSRef, frame_element: Option>) { @@ -383,17 +594,17 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { /// Commence a new URL load which will either replace this window or scroll to a fragment. fn load_url(self, href: DOMString) { - let base_url = self.page().get_url(); + let base_url = self.get_url(); debug!("current page url is {}", base_url); let url = UrlParser::new().base_url(&base_url).parse(href.as_slice()); // FIXME: handle URL parse errors more gracefully. let url = url.unwrap(); match url.fragment { Some(fragment) => { - self.script_chan.send(ScriptMsg::TriggerFragment(self.page.id, fragment)).unwrap(); + self.script_chan.send(ScriptMsg::TriggerFragment(self.id, fragment)).unwrap(); }, None => { - self.script_chan.send(ScriptMsg::TriggerLoad(self.page.id, LoadData::new(url))).unwrap(); + self.script_chan.send(ScriptMsg::TriggerLoad(self.id, LoadData::new(url))).unwrap(); } } } @@ -403,6 +614,97 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { self.flush_layout(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); } + fn set_fragment_name(self, fragment: Option) { + *self.fragment_name.borrow_mut() = fragment; + } + + fn steal_fragment_name(self) -> Option { + self.fragment_name.borrow_mut().take() + } + + fn set_window_size(self, size: WindowSizeData) { + self.window_size.set(size); + } + + fn window_size(self) -> WindowSizeData { + self.window_size.get() + } + + fn get_url(self) -> Url { + let doc = self.Document().root(); + doc.r().url() + } + + fn resource_task(self) -> ResourceTask { + self.resource_task.clone() + } + + fn devtools_chan(self) -> Option { + self.devtools_chan.clone() + } + + fn layout_chan(self) -> LayoutChan { + self.layout_chan.clone() + } + + fn constellation_chan(self) -> ConstellationChan { + self.constellation_chan.clone() + } + + fn windowproxy_handler(self) -> WindowProxyHandler { + self.dom_static.windowproxy_handler + } + + fn get_next_subpage_id(self) -> SubpageId { + let subpage_id = self.next_subpage_id.get(); + let SubpageId(id_num) = subpage_id; + self.next_subpage_id.set(SubpageId(id_num + 1)); + subpage_id + } + + fn layout_is_idle(self) -> bool { + self.layout_join_port.borrow().is_none() + } + + fn set_resize_event(self, event: WindowSizeData) { + self.resize_event.set(Some(event)); + } + + fn steal_resize_event(self) -> Option { + let event = self.resize_event.get(); + self.resize_event.set(None); + event + } + + fn set_page_clip_rect_with_new_viewport(self, viewport: Rect) -> bool { + // We use a clipping rectangle that is five times the size of the of the viewport, + // so that we don't collect display list items for areas too far outside the viewport, + // but also don't trigger reflows every time the viewport changes. + static VIEWPORT_EXPANSION: f32 = 2.0; // 2 lengths on each side plus original length is 5 total. + let proposed_clip_rect = geometry::f32_rect_to_au_rect( + viewport.inflate(viewport.size.width * VIEWPORT_EXPANSION, + viewport.size.height * VIEWPORT_EXPANSION)); + let clip_rect = self.page_clip_rect.get(); + if proposed_clip_rect == clip_rect { + return false; + } + + let had_clip_rect = clip_rect != MAX_RECT; + if had_clip_rect && !should_move_clip_rect(clip_rect, viewport) { + return false; + } + + self.page_clip_rect.set(proposed_clip_rect); + + // If we didn't have a clip rect, the previous display doesn't need rebuilding + // because it was built for infinite clip (MAX_RECT). + had_clip_rect + } + + fn set_devtools_wants_updates(self, value: bool) { + self.devtools_wants_updates.set(value); + } + // https://html.spec.whatwg.org/multipage/browsers.html#accessing-other-browsing-contexts fn IndexedGetter(self, _index: u32, _found: &mut bool) -> Option> { None @@ -419,13 +721,28 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { } impl Window { - pub fn new(cx: *mut JSContext, + pub fn new(js_context: Rc, page: Rc, script_chan: Box, control_chan: ScriptControlChan, compositor: Box, - image_cache_task: ImageCacheTask) + image_cache_task: ImageCacheTask, + resource_task: ResourceTask, + storage_task: StorageTask, + devtools_chan: Option, + constellation_chan: ConstellationChan, + layout_chan: LayoutChan, + id: PipelineId, + subpage_id: Option, + window_size: WindowSizeData) -> Temporary { + let layout_rpc: Box = { + let (rpc_send, rpc_recv) = channel(); + let LayoutChan(ref lchan) = layout_chan; + lchan.send(Msg::GetRPC(rpc_send)).unwrap(); + rpc_recv.recv().unwrap() + }; + let win = box Window { eventtarget: EventTarget::new_inherited(EventTargetTypeId::Window), script_chan: script_chan, @@ -435,6 +752,7 @@ impl Window { page: page, navigator: Default::default(), image_cache_task: image_cache_task, + devtools_chan: devtools_chan, browser_context: DOMRefCell::new(None), performance: Default::default(), navigation_start: time::get_time().sec as u64, @@ -442,8 +760,43 @@ impl Window { screen: Default::default(), session_storage: Default::default(), timers: TimerManager::new(), + id: id, + subpage_id: subpage_id, + dom_static: GlobalStaticData::new(), + js_context: DOMRefCell::new(Some(js_context.clone())), + resource_task: resource_task, + storage_task: storage_task, + constellation_chan: constellation_chan, + page_clip_rect: Cell::new(MAX_RECT), + fragment_name: DOMRefCell::new(None), + last_reflow_id: Cell::new(0), + resize_event: Cell::new(None), + next_subpage_id: Cell::new(SubpageId(0)), + layout_chan: layout_chan, + layout_rpc: layout_rpc, + layout_join_port: DOMRefCell::new(None), + window_size: Cell::new(window_size), + devtools_wants_updates: Cell::new(false), }; - WindowBinding::Wrap(cx, win) + WindowBinding::Wrap(js_context.ptr, win) } } + +fn should_move_clip_rect(clip_rect: Rect, new_viewport: Rect) -> bool{ + let clip_rect = Rect(Point2D(geometry::to_frac_px(clip_rect.origin.x) as f32, + geometry::to_frac_px(clip_rect.origin.y) as f32), + Size2D(geometry::to_frac_px(clip_rect.size.width) as f32, + geometry::to_frac_px(clip_rect.size.height) as f32)); + + // We only need to move the clip rect if the viewport is getting near the edge of + // our preexisting clip rect. We use half of the size of the viewport as a heuristic + // for "close." + static VIEWPORT_SCROLL_MARGIN_SIZE: f32 = 0.5; + let viewport_scroll_margin = new_viewport.size * VIEWPORT_SCROLL_MARGIN_SIZE; + + (clip_rect.origin.x - new_viewport.origin.x).abs() <= viewport_scroll_margin.width || + (clip_rect.max_x() - new_viewport.max_x()).abs() <= viewport_scroll_margin.width || + (clip_rect.origin.y - new_viewport.origin.y).abs() <= viewport_scroll_margin.height || + (clip_rect.max_y() - new_viewport.max_y()).abs() <= viewport_scroll_margin.height +} diff --git a/components/script/page.rs b/components/script/page.rs index 3800b5b3950..c080be4d16c 100644 --- a/components/script/page.rs +++ b/components/script/page.rs @@ -3,39 +3,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::cell::DOMRefCell; -use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; -use dom::bindings::codegen::InheritTypes::NodeCast; -use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable}; -use dom::bindings::utils::GlobalStaticData; +use dom::bindings::js::{JS, Temporary, Unrooted}; use dom::document::{Document, DocumentHelpers}; -use dom::element::Element; -use dom::node::{Node, NodeHelpers}; +use dom::node::NodeHelpers; use dom::window::Window; -use devtools_traits::DevtoolsControlChan; -use layout_interface::{ - ContentBoxResponse, ContentBoxesResponse, - HitTestResponse, LayoutChan, LayoutRPC, MouseOverResponse, Msg, Reflow, - ReflowGoal, ReflowQueryType, - TrustedNodeAddress -}; -use script_traits::{UntrustedNodeAddress, ScriptControlChan}; -use geom::{Point2D, Rect, Size2D}; -use js::rust::Cx; -use msg::compositor_msg::ScriptListener; -use msg::constellation_msg::{ConstellationChan, WindowSizeData}; use msg::constellation_msg::{PipelineId, SubpageId}; -use net::resource_task::ResourceTask; -use net::storage_task::StorageTask; -use util::geometry::{Au, MAX_RECT}; -use util::geometry; -use util::str::DOMString; use util::smallvec::SmallVec; -use std::cell::{Cell, Ref, RefMut}; -use std::sync::mpsc::{channel, Receiver}; -use std::sync::mpsc::TryRecvError::{Empty, Disconnected}; -use std::mem::replace; -use std::num::Float; use std::rc::Rc; use url::Url; @@ -43,30 +17,13 @@ use url::Url; #[jstraceable] pub struct Page { /// Pipeline id associated with this page. - pub id: PipelineId, + id: PipelineId, /// Subpage id associated with this page, if any. - pub subpage_id: Option, + subpage_id: Option, - /// Unique id for last reflow request; used for confirming completion reply. - pub last_reflow_id: Cell, - - /// The outermost frame containing the document, window, and page URL. - pub frame: DOMRefCell>, - - /// A handle for communicating messages to the layout task. - pub layout_chan: LayoutChan, - - /// A handle to perform RPC calls into the layout, quickly. - layout_rpc: Box, - - /// The port that we will use to join layout. If this is `None`, then layout is not running. - pub layout_join_port: DOMRefCell>>, - - /// The current size of the window, in pixels. - pub window_size: Cell, - - js_info: DOMRefCell>, + /// The outermost frame containing the document and window. + frame: DOMRefCell>, /// Cached copy of the most recent url loaded by the script, after all redirections. /// TODO(tkuehn): this currently does not follow any particular caching policy @@ -74,36 +31,8 @@ pub struct Page { /// when reloading. url: DOMRefCell<(Url, bool)>, - next_subpage_id: Cell, - - /// Pending resize event, if any. - pub resize_event: Cell>, - - /// Pending scroll to fragment event, if any - pub fragment_name: DOMRefCell>, - - /// Associated resource task for use by DOM objects like XMLHttpRequest - pub resource_task: ResourceTask, - - /// A handle for communicating messages to the storage task. - pub storage_task: StorageTask, - - /// A handle for communicating messages to the constellation task. - pub constellation_chan: ConstellationChan, - // Child Pages. pub children: DOMRefCell>>, - - /// An enlarged rectangle around the page contents visible in the viewport, used - /// to prevent creating display list items for content that is far away from the viewport. - pub page_clip_rect: Cell>, - - /// A flag to indicate whether the developer tools have requested live updates of - /// page changes. - pub devtools_wants_updates: Cell, - - /// For providing instructions to an optional devtools server. - pub devtools_chan: Option, } pub struct PageIterator { @@ -133,71 +62,26 @@ impl IterablePage for Rc { } impl Page { - pub fn new(id: PipelineId, subpage_id: Option, - layout_chan: LayoutChan, - window_size: WindowSizeData, - resource_task: ResourceTask, - storage_task: StorageTask, - constellation_chan: ConstellationChan, - js_context: Rc, - devtools_chan: Option, - url: Url) -> Page { - let js_info = JSPageInfo { - dom_static: GlobalStaticData::new(), - js_context: js_context, - }; - let layout_rpc: Box = { - let (rpc_send, rpc_recv) = channel(); - let LayoutChan(ref lchan) = layout_chan; - lchan.send(Msg::GetRPC(rpc_send)).unwrap(); - rpc_recv.recv().unwrap() - }; + pub fn new(id: PipelineId, subpage_id: Option, url: Url) -> Page { Page { id: id, subpage_id: subpage_id, frame: DOMRefCell::new(None), - layout_chan: layout_chan, - layout_rpc: layout_rpc, - layout_join_port: DOMRefCell::new(None), - window_size: Cell::new(window_size), - js_info: DOMRefCell::new(Some(js_info)), url: DOMRefCell::new((url, true)), - next_subpage_id: Cell::new(SubpageId(0)), - resize_event: Cell::new(None), - fragment_name: DOMRefCell::new(None), - last_reflow_id: Cell::new(0), - resource_task: resource_task, - storage_task: storage_task, - constellation_chan: constellation_chan, children: DOMRefCell::new(vec!()), - page_clip_rect: Cell::new(MAX_RECT), - devtools_wants_updates: Cell::new(false), - devtools_chan: devtools_chan, } } - pub fn flush_layout(&self, goal: ReflowGoal, query: ReflowQueryType) { - let frame = self.frame(); - let window = frame.as_ref().unwrap().window.root(); - self.reflow(goal, window.r().control_chan().clone(), &mut **window.r().compositor(), query); + pub fn window(&self) -> Temporary { + Temporary::new(self.frame.borrow().as_ref().unwrap().window.clone()) } - pub fn layout(&self) -> &LayoutRPC { - &*self.layout_rpc + pub fn window_for_script_dealloation(&self) -> Unrooted { + Unrooted::from_js(self.frame.borrow().as_ref().unwrap().window) } - pub fn content_box_query(&self, content_box_request: TrustedNodeAddress) -> Rect { - self.flush_layout(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxQuery(content_box_request)); - self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? - let ContentBoxResponse(rect) = self.layout_rpc.content_box(); - rect - } - - pub fn content_boxes_query(&self, content_boxes_request: TrustedNodeAddress) -> Vec> { - self.flush_layout(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxesQuery(content_boxes_request)); - self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? - let ContentBoxesResponse(rects) = self.layout_rpc.content_boxes(); - rects + pub fn document(&self) -> Temporary { + Temporary::new(self.frame.borrow().as_ref().unwrap().document.clone()) } // must handle root case separately @@ -219,49 +103,6 @@ impl Page { } } } - - pub fn set_page_clip_rect_with_new_viewport(&self, viewport: Rect) -> bool { - // We use a clipping rectangle that is five times the size of the of the viewport, - // so that we don't collect display list items for areas too far outside the viewport, - // but also don't trigger reflows every time the viewport changes. - static VIEWPORT_EXPANSION: f32 = 2.0; // 2 lengths on each side plus original length is 5 total. - let proposed_clip_rect = geometry::f32_rect_to_au_rect( - viewport.inflate(viewport.size.width * VIEWPORT_EXPANSION, - viewport.size.height * VIEWPORT_EXPANSION)); - let clip_rect = self.page_clip_rect.get(); - if proposed_clip_rect == clip_rect { - return false; - } - - let had_clip_rect = clip_rect != MAX_RECT; - if had_clip_rect && !should_move_clip_rect(clip_rect, viewport) { - return false; - } - - self.page_clip_rect.set(proposed_clip_rect); - - // If we didn't have a clip rect, the previous display doesn't need rebuilding - // because it was built for infinite clip (MAX_RECT). - had_clip_rect - } - - pub fn send_title_to_compositor(&self) { - match *self.frame() { - None => {} - Some(ref frame) => { - let window = frame.window.root(); - let document = frame.document.root(); - window.r().compositor().set_title(self.id, Some(document.r().Title())); - } - } - } - - pub fn dirty_all_nodes(&self) { - match *self.frame.borrow() { - None => {} - Some(ref frame) => frame.document.root().r().dirty_all_nodes(), - } - } } impl Iterator for PageIterator { @@ -281,198 +122,15 @@ impl Iterator for PageIterator { } impl Page { - pub fn mut_js_info<'a>(&'a self) -> RefMut<'a, Option> { - self.js_info.borrow_mut() + pub fn set_reflow_status(&self, status: bool) -> bool { + let old = (*self.url.borrow()).1; + (*self.url.borrow_mut()).1 = status; + old } - pub unsafe fn unsafe_mut_js_info<'a>(&'a self) -> &'a mut Option { - self.js_info.borrow_for_script_deallocation() + pub fn set_frame(&self, frame: Option) { + *self.frame.borrow_mut() = frame; } - - pub fn js_info<'a>(&'a self) -> Ref<'a, Option> { - self.js_info.borrow() - } - - pub fn url<'a>(&'a self) -> Ref<'a, (Url, bool)> { - self.url.borrow() - } - - pub fn mut_url<'a>(&'a self) -> RefMut<'a, (Url, bool)> { - self.url.borrow_mut() - } - - pub fn frame<'a>(&'a self) -> Ref<'a, Option> { - self.frame.borrow() - } - - pub fn mut_frame<'a>(&'a self) -> RefMut<'a, Option> { - self.frame.borrow_mut() - } - - pub fn get_next_subpage_id(&self) -> SubpageId { - let subpage_id = self.next_subpage_id.get(); - let SubpageId(id_num) = subpage_id; - self.next_subpage_id.set(SubpageId(id_num + 1)); - subpage_id - } - - pub fn get_url(&self) -> Url { - self.url().0.clone() - } - - // FIXME(cgaebel): join_layout is racey. What if the compositor triggers a - // reflow between the "join complete" message and returning from this - // function? - - /// Sends a ping to layout and waits for the response. The response will arrive when the - /// layout task has finished any pending request messages. - fn join_layout(&self) { - let mut layout_join_port = self.layout_join_port.borrow_mut(); - if let Some(join_port) = replace(&mut *layout_join_port, None) { - match join_port.try_recv() { - Err(Empty) => { - info!("script: waiting on layout"); - join_port.recv().unwrap(); - } - Ok(_) => {} - Err(Disconnected) => { - panic!("Layout task failed while script was waiting for a result."); - } - } - - debug!("script: layout joined") - } - } - - /// Reflows the page if it's possible to do so and the page is dirty. This method will wait - /// for the layout thread to complete (but see the `TODO` below). If there is no window size - /// yet, the page is presumed invisible and no reflow is performed. - /// - /// TODO(pcwalton): Only wait for style recalc, since we have off-main-thread layout. - pub fn reflow(&self, - goal: ReflowGoal, - script_chan: ScriptControlChan, - _: &mut ScriptListener, - query_type: ReflowQueryType) { - let root = match *self.frame() { - None => return, - Some(ref frame) => { - frame.document.root().r().GetDocumentElement() - } - }; - - let root = match root.root() { - None => return, - Some(root) => root, - }; - - debug!("script: performing reflow for goal {:?}", goal); - - let root: JSRef = NodeCast::from_ref(root.r()); - if !root.get_has_dirty_descendants() { - debug!("root has no dirty descendants; avoiding reflow"); - return - } - - debug!("script: performing reflow for goal {:?}", goal); - - // Layout will let us know when it's done. - let (join_chan, join_port) = channel(); - - { - let mut layout_join_port = self.layout_join_port.borrow_mut(); - *layout_join_port = Some(join_port); - } - - let last_reflow_id = &self.last_reflow_id; - last_reflow_id.set(last_reflow_id.get() + 1); - - let window_size = self.window_size.get(); - - // Send new document and relevant styles to layout. - let reflow = box Reflow { - document_root: root.to_trusted_node_address(), - url: self.get_url(), - iframe: self.subpage_id.is_some(), - goal: goal, - window_size: window_size, - script_chan: script_chan, - script_join_chan: join_chan, - id: last_reflow_id.get(), - query_type: query_type, - page_clip_rect: self.page_clip_rect.get(), - }; - - let LayoutChan(ref chan) = self.layout_chan; - chan.send(Msg::Reflow(reflow)).unwrap(); - - debug!("script: layout forked"); - - self.join_layout(); - } - - /// Attempt to find a named element in this page's document. - pub fn find_fragment_node(&self, fragid: DOMString) -> Option> { - let document = self.frame().as_ref().unwrap().document.root(); - document.r().find_fragment_node(fragid) - } - - pub fn hit_test(&self, point: &Point2D) -> Option { - let frame = self.frame(); - let document = frame.as_ref().unwrap().document.root(); - let root = match document.r().GetDocumentElement().root() { - None => return None, - Some(root) => root, - }; - let root: JSRef = NodeCast::from_ref(root.r()); - let address = match self.layout().hit_test(root.to_trusted_node_address(), *point) { - Ok(HitTestResponse(node_address)) => { - Some(node_address) - } - Err(()) => { - debug!("layout query error"); - None - } - }; - address - } - - pub fn get_nodes_under_mouse(&self, point: &Point2D) -> Vec { - let mut results = vec!(); - let frame = self.frame(); - let document = frame.as_ref().unwrap().document.root(); - match document.r().GetDocumentElement().root() { - Some(root) => { - let root: JSRef = NodeCast::from_ref(root.r()); - match self.layout().mouse_over(root.to_trusted_node_address(), *point) { - Ok(MouseOverResponse(node_addresses)) => { - results = node_addresses; - } - Err(()) => {} - }; - } - None => {} - } - results - } -} - -fn should_move_clip_rect(clip_rect: Rect, new_viewport: Rect) -> bool{ - let clip_rect = Rect(Point2D(geometry::to_frac_px(clip_rect.origin.x) as f32, - geometry::to_frac_px(clip_rect.origin.y) as f32), - Size2D(geometry::to_frac_px(clip_rect.size.width) as f32, - geometry::to_frac_px(clip_rect.size.height) as f32)); - - // We only need to move the clip rect if the viewport is getting near the edge of - // our preexisting clip rect. We use half of the size of the viewport as a heuristic - // for "close." - static VIEWPORT_SCROLL_MARGIN_SIZE: f32 = 0.5; - let viewport_scroll_margin = new_viewport.size * VIEWPORT_SCROLL_MARGIN_SIZE; - - (clip_rect.origin.x - new_viewport.origin.x).abs() <= viewport_scroll_margin.width || - (clip_rect.max_x() - new_viewport.max_x()).abs() <= viewport_scroll_margin.width || - (clip_rect.origin.y - new_viewport.origin.y).abs() <= viewport_scroll_margin.height || - (clip_rect.max_y() - new_viewport.max_y()).abs() <= viewport_scroll_margin.height } /// Information for one frame in the browsing context. @@ -484,12 +142,3 @@ pub struct Frame { /// The window object for this frame. pub window: JS, } - -/// Encapsulation of the javascript information associated with each frame. -#[jstraceable] -pub struct JSPageInfo { - /// Global static data related to the DOM. - pub dom_static: GlobalStaticData, - /// The JavaScript context. - pub js_context: Rc, -} diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 28a2e6437c0..de9f36fab24 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -23,7 +23,7 @@ use dom::element::{Element, AttributeHandlers}; use dom::event::{Event, EventHelpers}; use dom::uievent::UIEvent; use dom::eventtarget::EventTarget; -use dom::node::{self, Node, NodeHelpers, NodeDamage}; +use dom::node::{self, Node, NodeHelpers, NodeDamage, window_from_node}; use dom::window::{Window, WindowHelpers, ScriptHelpers}; use dom::worker::{Worker, TrustedWorkerAddress}; use parse::html::{HTMLInput, parse_html}; @@ -74,7 +74,6 @@ use libc; use std::any::Any; use std::borrow::ToOwned; use std::cell::Cell; -use std::mem::replace; use std::num::ToPrimitive; use std::rc::Rc; use std::result::Result; @@ -275,7 +274,8 @@ impl<'a> Drop for ScriptMemoryFailsafe<'a> { unsafe { let page = owner.page.borrow_for_script_deallocation(); for page in page.iter() { - *page.unsafe_mut_js_info() = None; + let window = page.window().root(); + window.r().clear_js_context_for_script_deallocation(); } *owner.js_context.borrow_for_script_deallocation() = None; } @@ -475,14 +475,13 @@ impl ScriptTask { if let Some(ref page) = page.as_ref() { for page in page.iter() { // Only process a resize if layout is idle. - let layout_join_port = page.layout_join_port.borrow(); - if layout_join_port.is_none() { - let mut resize_event = page.resize_event.get(); - match resize_event.take() { - Some(size) => resizes.push((page.id, size)), + let window = page.window().root(); + if window.r().layout_is_idle() { + let resize_event = window.r().steal_resize_event(); + match resize_event { + Some(size) => resizes.push((window.r().pipeline(), size)), None => () } - page.resize_event.set(None); } } } @@ -654,7 +653,8 @@ impl ScriptTask { let page = self.page.borrow(); if let Some(ref page) = page.as_ref() { if let Some(ref page) = page.find(id) { - page.resize_event.set(Some(size)); + let window = page.window().root(); + window.r().set_resize_event(size); return; } } @@ -670,7 +670,8 @@ impl ScriptTask { let page = self.page.borrow(); if let Some(page) = page.as_ref() { if let Some(ref inner_page) = page.find(id) { - if inner_page.set_page_clip_rect_with_new_viewport(rect) { + let window = inner_page.window().root(); + if window.r().set_page_clip_rect_with_new_viewport(rect) { let page = get_page(page, id); self.force_reflow(&*page); } @@ -699,10 +700,11 @@ impl ScriptTask { whose parent has a PipelineId which does not correspond to a pipeline in the script task's page tree. This is a bug."); + let parent_window = parent_page.window().root(); let chan = layout_chan.downcast_ref::>().unwrap(); let layout_chan = LayoutChan(chan.clone()); let new_load = InProgressLoad::new(new_pipeline_id, Some((old_pipeline_id, subpage_id)), - layout_chan, parent_page.window_size.get(), + layout_chan, parent_window.r().window_size(), load_data.url.clone()); self.start_page_load(new_load, load_data); } @@ -712,8 +714,7 @@ impl ScriptTask { let page = self.root_page(); let page = page.find(id).expect("ScriptTask: received fire timer msg for a pipeline ID not associated with this script task. This is a bug."); - let frame = page.frame(); - let window = frame.as_ref().unwrap().window.root(); + let window = page.window().root(); window.r().handle_fire_timer(timer_id); } @@ -722,8 +723,7 @@ impl ScriptTask { let page = self.root_page(); let page = page.find(id).expect("ScriptTask: received freeze msg for a pipeline ID not associated with this script task. This is a bug."); - let frame = page.frame(); - let window = frame.as_ref().unwrap().window.root(); + let window = page.window().root(); window.r().freeze(); } @@ -732,8 +732,7 @@ impl ScriptTask { let page = self.root_page(); let page = page.find(id).expect("ScriptTask: received thaw msg for a pipeline ID not associated with this script task. This is a bug."); - let frame = page.frame(); - let window = frame.as_ref().unwrap().window.root(); + let window = page.window().root(); window.r().thaw(); } @@ -742,12 +741,7 @@ impl ScriptTask { // should exist. let page = self.root_page().find(pipeline_id).unwrap(); - // Pull out the `needs_reflow` flag explicitly because `reflow` can ask for the - // page's URL, and we can't be holding a borrow on that URL (#4402). - let needed_reflow = { - let &mut (_, ref mut needs_reflow) = &mut *page.mut_url(); - replace(needs_reflow, false) - }; + let needed_reflow = page.set_reflow_status(false); if needed_reflow { self.force_reflow(&*page); } @@ -760,13 +754,10 @@ impl ScriptTask { let page = page.find(pipeline_id).expect( "ScriptTask: received a load message for a layout channel that is not associated \ with this script task. This is a bug."); - let last_reflow_id = page.last_reflow_id.get(); - if last_reflow_id == reflow_id { - let mut layout_join_port = page.layout_join_port.borrow_mut(); - *layout_join_port = None; - } + let window = page.window().root(); + window.r().handle_reflow_complete_msg(reflow_id); - let doc = page.frame().as_ref().unwrap().document.root(); + let doc = page.document().root(); let html_element = doc.r().GetDocumentElement().root(); let reftest_wait = html_element.r().map_or(false, |elem| elem.has_class(&Atom::from_slice("reftest-wait"))); @@ -780,10 +771,9 @@ impl ScriptTask { let page = self.root_page(); let page = page.find(id).expect("Received resize message for PipelineId not associated with a page in the page tree. This is a bug."); - page.window_size.set(new_size); - match &mut *page.mut_url() { - &mut (_, ref mut needs_reflow) => *needs_reflow = true, - } + let window = page.window().root(); + window.r().set_window_size(new_size); + page.set_reflow_status(true); } /// We have gotten a window.close from script, which we pass on to the compositor. @@ -811,7 +801,9 @@ impl ScriptTask { /// Handles a request for the window title. fn handle_get_title_msg(&self, pipeline_id: PipelineId) { - get_page(&self.root_page(), pipeline_id).send_title_to_compositor(); + let page = get_page(&self.root_page(), pipeline_id); + let document = page.document().root(); + document.r().send_title_to_compositor(); } /// Handles a request to exit the script task and shut down layout. @@ -819,7 +811,8 @@ impl ScriptTask { fn handle_exit_pipeline_msg(&self, id: PipelineId, exit_type: PipelineExitType) -> bool { // If root is being exited, shut down all pages let page = self.root_page(); - if page.id == id { + let window = page.window().root(); + if window.r().pipeline() == id { debug!("shutting down layout for root page {:?}", id); *self.js_context.borrow_mut() = None; shut_down_layout(&page, (*self.js_runtime).ptr, exit_type); @@ -827,18 +820,10 @@ impl ScriptTask { } // otherwise find just the matching page and exit all sub-pages - match page.find(id) { - Some(ref mut page) => { - shut_down_layout(&*page, (*self.js_runtime).ptr, exit_type); - page.remove(id); - false - } - // TODO(tkuehn): pipeline closing is currently duplicated across - // script and constellation, which can cause this to happen. Constellation - // needs to be smarter about exiting pipelines. - None => false, + if let Some(ref mut child_page) = page.remove(id) { + shut_down_layout(&*child_page, (*self.js_runtime).ptr, exit_type); } - + return false; } /// The entry point to document loading. Defines bindings, sets up the window and document @@ -860,7 +845,7 @@ impl ScriptTask { // denies access to most properties (per // https://github.com/servo/servo/issues/3939#issuecomment-62287025). borrowed_page.find(parent_id).and_then(|page| { - let doc = page.frame().as_ref().unwrap().document.root(); + let doc = page.document().root(); let doc: JSRef = NodeCast::from_ref(doc.r()); doc.traverse_preorder() @@ -881,10 +866,7 @@ impl ScriptTask { let cx = cx.as_ref().unwrap(); let page = Rc::new(Page::new(incomplete.pipeline_id, incomplete.subpage_id.map(|p| p.1), - incomplete.layout_chan, incomplete.window_size, - self.resource_task.clone(), self.storage_task.clone(), - self.constellation_chan.clone(), cx.clone(), - self.devtools_chan.clone(), final_url.clone())); + final_url.clone())); if root_page_exists { *self.page.borrow_mut() = Some(page.clone()); } else if let Some((parent, _)) = incomplete.subpage_id { @@ -894,12 +876,20 @@ impl ScriptTask { } // Create the window and document objects. - let window = Window::new(cx.ptr, + let window = Window::new(cx.clone(), page.clone(), self.chan.clone(), self.control_chan.clone(), self.compositor.borrow_mut().dup(), - self.image_cache_task.clone()).root(); + self.image_cache_task.clone(), + self.resource_task.clone(), + self.storage_task.clone(), + self.devtools_chan.clone(), + self.constellation_chan.clone(), + incomplete.layout_chan, + incomplete.pipeline_id, + incomplete.subpage_id.map(|s| s.1), + incomplete.window_size).root(); let document = Document::new(window.r(), Some(final_url.clone()), IsHTMLDocument::HTMLDocument, None, @@ -910,14 +900,11 @@ impl ScriptTask { window.r().init_browser_context(document.r(), frame_element.r()); - { - // Create the root frame. - let mut frame = page.mut_frame(); - *frame = Some(Frame { - document: JS::from_rooted(document.r()), - window: JS::from_rooted(window.r()), - }); - } + // Create the root frame + page.set_frame(Some(Frame { + document: JS::from_rooted(document.r()), + window: JS::from_rooted(window.r()), + })); let is_javascript = incomplete.url.scheme.as_slice() == "javascript"; let parse_input = if is_javascript { @@ -941,11 +928,8 @@ impl ScriptTask { NodeDamage::OtherNodeDamage); window.r().flush_layout(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); - { - // No more reflow required - let mut page_url = page.mut_url(); - (*page_url).1 = false; - } + // No more reflow required + page.set_reflow_status(false); // https://html.spec.whatwg.org/multipage/#the-end step 4 let addr: Trusted = Trusted::new(self.get_cx(), document.r(), self.chan.clone()); @@ -960,7 +944,7 @@ impl ScriptTask { let handler = Box::new(DocumentProgressHandler::new(addr, DocumentProgressTask::Load)); self.chan.send(ScriptMsg::RunnableMsg(handler)).unwrap(); - *page.fragment_name.borrow_mut() = final_url.fragment.clone(); + window.r().set_fragment_name(final_url.fragment.clone()); let ConstellationChan(ref chan) = self.constellation_chan; chan.send(ConstellationMsg::LoadComplete).unwrap(); @@ -994,11 +978,10 @@ impl ScriptTask { /// Reflows non-incrementally. fn force_reflow(&self, page: &Page) { - page.dirty_all_nodes(); - page.reflow(ReflowGoal::ForDisplay, - self.control_chan.clone(), - &mut **self.compositor.borrow_mut(), - ReflowQueryType::NoQuery); + let document = page.document().root(); + document.r().dirty_all_nodes(); + let window = window_from_node(document.r()).root(); + window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); } /// This is the main entry point for receiving and dispatching DOM events. @@ -1025,8 +1008,7 @@ impl ScriptTask { let node_to_dirty = node::from_untrusted_node_address(self.js_runtime.ptr, *node).root(); let page = get_page(&self.root_page(), pipeline_id); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let document = page.document().root(); document.r().content_changed(node_to_dirty.r(), NodeDamage::OtherNodeDamage); } @@ -1036,8 +1018,7 @@ impl ScriptTask { ClickEvent(_button, point) => { let page = get_page(&self.root_page(), pipeline_id); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let document = page.document().root(); document.r().handle_click_event(self.js_runtime.ptr, _button, point); } @@ -1045,8 +1026,7 @@ impl ScriptTask { MouseUpEvent(..) => {} MouseMoveEvent(point) => { let page = get_page(&self.root_page(), pipeline_id); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let document = page.document().root(); let mouse_over_targets = &mut *self.mouse_over_targets.borrow_mut(); if document.r().handle_mouse_move_event(self.js_runtime.ptr, point, mouse_over_targets) { @@ -1056,8 +1036,7 @@ impl ScriptTask { KeyEvent(key, state, modifiers) => { let page = get_page(&self.root_page(), pipeline_id); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let document = page.document().root(); document.r().dispatch_key_event( key, state, modifiers, &mut *self.compositor.borrow_mut()); } @@ -1075,7 +1054,8 @@ impl ScriptTask { /// for the given pipeline. fn trigger_fragment(&self, pipeline_id: PipelineId, fragment: String) { let page = get_page(&self.root_page(), pipeline_id); - match page.find_fragment_node(fragment).root() { + let document = page.document().root(); + match document.r().find_fragment_node(fragment).root() { Some(node) => { self.scroll_fragment_point(pipeline_id, node.r()); } @@ -1085,53 +1065,36 @@ impl ScriptTask { fn handle_resize_event(&self, pipeline_id: PipelineId, new_size: WindowSizeData) { - let window = { - let page = get_page(&self.root_page(), pipeline_id); - page.window_size.set(new_size); + let page = get_page(&self.root_page(), pipeline_id); + let window = page.window().root(); + window.r().set_window_size(new_size); + self.force_reflow(&*page); - let frame = page.frame(); - if frame.is_some() { - self.force_reflow(&*page); - } - - let fragment_node = - page.fragment_name - .borrow_mut() - .take() - .and_then(|name| page.find_fragment_node(name)) - .root(); - match fragment_node { - Some(node) => self.scroll_fragment_point(pipeline_id, node.r()), - None => {} - } - - frame.as_ref().map(|frame| Temporary::new(frame.window.clone())) - }; - - match window.root() { - Some(window) => { - // http://dev.w3.org/csswg/cssom-view/#resizing-viewports - // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-resize - let uievent = UIEvent::new(window.r(), - "resize".to_owned(), false, - false, Some(window.r()), - 0i32).root(); - let event: JSRef = EventCast::from_ref(uievent.r()); - - let wintarget: JSRef = EventTargetCast::from_ref(window.r()); - event.fire(wintarget); - } - None => () + let document = page.document().root(); + let fragment_node = window.r().steal_fragment_name() + .and_then(|name| document.r().find_fragment_node(name)) + .root(); + match fragment_node { + Some(node) => self.scroll_fragment_point(pipeline_id, node.r()), + None => {} } + + // http://dev.w3.org/csswg/cssom-view/#resizing-viewports + // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-resize + let uievent = UIEvent::new(window.r(), + "resize".to_owned(), false, + false, Some(window.r()), + 0i32).root(); + let event: JSRef = EventCast::from_ref(uievent.r()); + + let wintarget: JSRef = EventTargetCast::from_ref(window.r()); + event.fire(wintarget); } fn handle_reflow_event(&self, pipeline_id: PipelineId) { debug!("script got reflow event"); let page = get_page(&self.root_page(), pipeline_id); - let frame = page.frame(); - if frame.is_some() { - self.force_reflow(&*page); - } + self.force_reflow(&*page); } fn start_page_load(&self, incomplete: InProgressLoad, mut load_data: LoadData) { @@ -1167,24 +1130,25 @@ impl ScriptTask { /// Shuts down layout for the given page tree. fn shut_down_layout(page_tree: &Rc, rt: *mut JSRuntime, exit_type: PipelineExitType) { + let mut channels = vec!(); + for page in page_tree.iter() { // Tell the layout task to begin shutting down, and wait until it // processed this message. let (response_chan, response_port) = channel(); - let LayoutChan(ref chan) = page.layout_chan; + let window = page.window().root(); + let LayoutChan(chan) = window.r().layout_chan(); if chan.send(layout_interface::Msg::PrepareToExit(response_chan)).is_ok() { - response_port.recv().unwrap(); + channels.push(chan); + response_port.recv().unwrap(); } } - // Remove our references to the DOM objects in this page tree. + // Drop our references to the JSContext and DOM objects, potentially triggering a GC. for page in page_tree.iter() { - *page.mut_frame() = None; - } - - // Drop our references to the JSContext, potentially triggering a GC. - for page in page_tree.iter() { - *page.mut_js_info() = None; + let window = page.window().root(); + window.r().clear_js_context(); + page.set_frame(None); } // Force a GC to make sure that our DOM reflectors are released before we tell @@ -1194,8 +1158,7 @@ fn shut_down_layout(page_tree: &Rc, rt: *mut JSRuntime, exit_type: Pipelin } // Destroy the layout task. If there were node leaks, layout will now crash safely. - for page in page_tree.iter() { - let LayoutChan(ref chan) = page.layout_chan; + for chan in channels.into_iter() { chan.send(layout_interface::Msg::ExitNow(exit_type)).ok(); } } From 6351fc75fd3ce5d2d2136bfc18cde8370dce2646 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Thu, 19 Feb 2015 13:27:21 -0500 Subject: [PATCH 17/29] Only store the url inside a pipeline instead of the rest of the LoadData. --- components/compositing/compositor.rs | 8 ++++---- components/compositing/compositor_task.rs | 9 +++++---- components/compositing/constellation.rs | 6 +++--- components/compositing/headless.rs | 2 +- components/compositing/pipeline.rs | 11 ++++++----- components/compositing/windowing.rs | 5 +++-- components/servo/Cargo.lock | 1 + ports/cef/Cargo.lock | 1 + ports/cef/window.rs | 6 +++--- ports/glutin/Cargo.toml | 1 + ports/glutin/lib.rs | 1 + ports/glutin/window.rs | 6 +++--- ports/gonk/src/window.rs | 4 ++-- 13 files changed, 34 insertions(+), 27 deletions(-) diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index cf303102ba3..02461adb15d 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -274,8 +274,8 @@ impl IOCompositor { self.change_page_title(pipeline_id, title); } - (Msg::ChangePageLoadData(frame_id, load_data), ShutdownState::NotShuttingDown) => { - self.change_page_load_data(frame_id, load_data); + (Msg::ChangePageUrl(frame_id, url), ShutdownState::NotShuttingDown) => { + self.change_page_url(frame_id, url); } (Msg::PaintMsgDiscarded, ShutdownState::NotShuttingDown) => { @@ -441,8 +441,8 @@ impl IOCompositor { } } - fn change_page_load_data(&mut self, _: FrameId, load_data: LoadData) { - self.window.set_page_load_data(load_data); + fn change_page_url(&mut self, _: FrameId, url: Url) { + self.window.set_page_url(url); } fn all_pipelines_in_idle_paint_state(&self) -> bool { diff --git a/components/compositing/compositor_task.rs b/components/compositing/compositor_task.rs index e3c1a5102d1..9458c7fa150 100644 --- a/components/compositing/compositor_task.rs +++ b/components/compositing/compositor_task.rs @@ -20,8 +20,9 @@ use layers::layers::LayerBufferSet; use pipeline::CompositionPipeline; use msg::compositor_msg::{Epoch, LayerId, LayerMetadata, ReadyState}; use msg::compositor_msg::{PaintListener, PaintState, ScriptListener, ScrollPolicy}; -use msg::constellation_msg::{ConstellationChan, LoadData, PipelineId}; +use msg::constellation_msg::{ConstellationChan, PipelineId}; use msg::constellation_msg::{Key, KeyState, KeyModifiers}; +use url::Url; use util::cursor::Cursor; use util::geometry::PagePx; use util::memory::MemoryProfilerChan; @@ -200,8 +201,8 @@ pub enum Msg { ChangePaintState(PipelineId, PaintState), /// Alerts the compositor that the current page has changed its title. ChangePageTitle(PipelineId, Option), - /// Alerts the compositor that the current page has changed its load data (including URL). - ChangePageLoadData(FrameId, LoadData), + /// Alerts the compositor that the current page has changed its URL. + ChangePageUrl(FrameId, Url), /// Alerts the compositor that a `PaintMsg` has been discarded. PaintMsgDiscarded, /// Replaces the current frame tree, typically called during main frame navigation. @@ -237,7 +238,7 @@ impl Debug for Msg { Msg::ChangeReadyState(..) => write!(f, "ChangeReadyState"), Msg::ChangePaintState(..) => write!(f, "ChangePaintState"), Msg::ChangePageTitle(..) => write!(f, "ChangePageTitle"), - Msg::ChangePageLoadData(..) => write!(f, "ChangePageLoadData"), + Msg::ChangePageUrl(..) => write!(f, "ChangePageUrl"), Msg::PaintMsgDiscarded(..) => write!(f, "PaintMsgDiscarded"), Msg::SetFrameTree(..) => write!(f, "SetFrameTree"), Msg::CreateRootLayerForPipeline(..) => write!(f, "CreateRootLayerForPipeline"), diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index 5ef72e185ec..aff40db788d 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -334,9 +334,9 @@ impl NavigationContext { /// compositor of the new URLs. fn set_current(&mut self, new_frame: Rc, compositor_proxy: &mut CompositorProxy) { self.current = Some(new_frame.clone()); - compositor_proxy.send(CompositorMsg::ChangePageLoadData( + compositor_proxy.send(CompositorMsg::ChangePageUrl( new_frame.id, - new_frame.pipeline.borrow().load_data.clone())); + new_frame.pipeline.borrow().url.clone())); } } @@ -762,7 +762,7 @@ impl Constellation { source Id of ScriptLoadedURLInIFrameMsg does have an associated pipeline in constellation. This should be impossible.").clone(); - let source_url = source_pipeline.load_data.url.clone(); + let source_url = source_pipeline.url.clone(); let same_script = (source_url.host() == url.host() && source_url.port() == url.port()) && diff --git a/components/compositing/headless.rs b/components/compositing/headless.rs index 0c38846d1d0..9957cb067cc 100644 --- a/components/compositing/headless.rs +++ b/components/compositing/headless.rs @@ -113,7 +113,7 @@ impl CompositorEventListener for NullCompositor { Msg::PaintMsgDiscarded(..) | Msg::ScrollTimeout(..) | Msg::ChangePageTitle(..) | - Msg::ChangePageLoadData(..) | + Msg::ChangePageUrl(..) | Msg::KeyEvent(..) | Msg::SetCursor(..) => {} Msg::PaintTaskExited(..) => {} diff --git a/components/compositing/pipeline.rs b/components/compositing/pipeline.rs index eabf93752b3..c09f4ea06d5 100644 --- a/components/compositing/pipeline.rs +++ b/components/compositing/pipeline.rs @@ -16,6 +16,7 @@ use msg::constellation_msg::{LoadData, WindowSizeData, PipelineExitType}; use net::image_cache_task::ImageCacheTask; use net::resource_task::ResourceTask; use net::storage_task::StorageTask; +use url::Url; use util::time::TimeProfilerChan; use std::rc::Rc; use std::sync::mpsc::{Receiver, channel}; @@ -29,8 +30,8 @@ pub struct Pipeline { pub paint_chan: PaintChan, pub layout_shutdown_port: Receiver<()>, pub paint_shutdown_port: Receiver<()>, - /// Load data corresponding to the most recently-loaded page. - pub load_data: LoadData, + /// URL corresponding to the most recently-loaded page. + pub url: Url, /// The title of the most recently-loaded page. pub title: Option, } @@ -137,7 +138,7 @@ impl Pipeline { paint_chan, layout_shutdown_port, paint_shutdown_port, - load_data) + load_data.url) } pub fn new(id: PipelineId, @@ -147,7 +148,7 @@ impl Pipeline { paint_chan: PaintChan, layout_shutdown_port: Receiver<()>, paint_shutdown_port: Receiver<()>, - load_data: LoadData) + url: Url) -> Pipeline { Pipeline { id: id, @@ -157,7 +158,7 @@ impl Pipeline { paint_chan: paint_chan, layout_shutdown_port: layout_shutdown_port, paint_shutdown_port: paint_shutdown_port, - load_data: load_data, + url: url, title: None, } } diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index 8c20aedd8e5..1990523dd2f 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -12,7 +12,8 @@ use geom::size::TypedSize2D; use layers::geometry::DevicePixel; use layers::platform::surface::NativeGraphicsMetadata; use msg::compositor_msg::{PaintState, ReadyState}; -use msg::constellation_msg::{Key, KeyState, KeyModifiers, LoadData}; +use msg::constellation_msg::{Key, KeyState, KeyModifiers}; +use url::Url; use util::cursor::Cursor; use util::geometry::ScreenPx; use std::fmt::{Error, Formatter, Debug}; @@ -105,7 +106,7 @@ pub trait WindowMethods { /// Sets the page title for the current page. fn set_page_title(&self, title: Option); /// Sets the load data for the current page. - fn set_page_load_data(&self, load_data: LoadData); + fn set_page_url(&self, url: Url); /// Called when the browser is done loading a frame. fn load_end(&self); diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index a52a5ec3191..d94dfcade90 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -389,6 +389,7 @@ dependencies = [ "libc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", "time 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)", "util 0.0.1", ] diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index c67a240d535..423e2be2246 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -392,6 +392,7 @@ dependencies = [ "libc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", "time 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)", "util 0.0.1", ] diff --git a/ports/cef/window.rs b/ports/cef/window.rs index 1b133e14d27..c485792005b 100644 --- a/ports/cef/window.rs +++ b/ports/cef/window.rs @@ -23,7 +23,7 @@ use layers::platform::surface::NativeGraphicsMetadata; use libc::{c_char, c_void}; use msg::constellation_msg::{Key, KeyModifiers}; use msg::compositor_msg::{ReadyState, PaintState}; -use msg::constellation_msg::LoadData; +use std_url::Url; use util::cursor::Cursor; use util::geometry::ScreenPx; use std::cell::RefCell; @@ -286,7 +286,7 @@ impl WindowMethods for Window { } } - fn set_page_load_data(&self, load_data: LoadData) { + fn set_page_url(&self, url: Url) { let browser = self.cef_browser.borrow(); let browser = match *browser { None => return, @@ -294,7 +294,7 @@ impl WindowMethods for Window { }; let frame = browser.get_main_frame(); let frame = frame.downcast(); - *frame.url.borrow_mut() = load_data.url.to_string() + *frame.url.borrow_mut() = url.to_string() } fn handle_key(&self, _: Key, _: KeyModifiers) { diff --git a/ports/glutin/Cargo.toml b/ports/glutin/Cargo.toml index d58ed4a32bd..7c3cb6f13d5 100644 --- a/ports/glutin/Cargo.toml +++ b/ports/glutin/Cargo.toml @@ -43,3 +43,4 @@ git = "https://github.com/servo/rust-egl" time = "0.1.12" bitflags = "*" libc = "*" +url = "*" \ No newline at end of file diff --git a/ports/glutin/lib.rs b/ports/glutin/lib.rs index b644483adb2..476ed457dea 100644 --- a/ports/glutin/lib.rs +++ b/ports/glutin/lib.rs @@ -22,6 +22,7 @@ extern crate msg; extern crate time; extern crate util; extern crate egl; +extern crate url; use compositing::windowing::WindowEvent; use geom::scale_factor::ScaleFactor; diff --git a/ports/glutin/window.rs b/ports/glutin/window.rs index 2256d34b998..bf83b445e6a 100644 --- a/ports/glutin/window.rs +++ b/ports/glutin/window.rs @@ -15,10 +15,10 @@ use layers::platform::surface::NativeGraphicsMetadata; use msg::constellation_msg; use msg::constellation_msg::Key; use msg::compositor_msg::{PaintState, ReadyState}; -use msg::constellation_msg::LoadData; use NestedEventLoopListener; use std::rc::Rc; use std::sync::mpsc::{channel, Sender}; +use url::Url; use util::cursor::Cursor; use util::geometry::ScreenPx; @@ -462,7 +462,7 @@ impl WindowMethods for Window { self.window.set_title(&title); } - fn set_page_load_data(&self, _: LoadData) { + fn set_page_url(&self, _: Url) { } fn load_end(&self) { @@ -657,7 +657,7 @@ impl WindowMethods for Window { fn set_page_title(&self, _: Option) { } - fn set_page_load_data(&self, _: LoadData) { + fn set_page_url(&self, _: Url) { } fn load_end(&self) { diff --git a/ports/gonk/src/window.rs b/ports/gonk/src/window.rs index 818e62c6835..0e8bf8451a1 100644 --- a/ports/gonk/src/window.rs +++ b/ports/gonk/src/window.rs @@ -13,7 +13,6 @@ use layers::platform::surface::NativeGraphicsMetadata; use libc::c_int; use msg::compositor_msg::{ReadyState, PaintState}; use msg::constellation_msg::{Key, KeyModifiers}; -use msg::constellation_msg::LoadData; use std::cell::Cell; use std::sync::mpsc::{channel, Sender, Receiver}; use std::rc::Rc; @@ -22,6 +21,7 @@ use std::mem::size_of; use std::mem::zeroed; use std::ptr; use std::ffi::CString; +use url::Url; use util::cursor::Cursor; use util::geometry::ScreenPx; use gleam::gl; @@ -804,7 +804,7 @@ impl WindowMethods for Window { fn set_page_title(&self, _: Option) { } - fn set_page_load_data(&self, _: LoadData) { + fn set_page_url(&self, _: Url) { } fn load_end(&self) { From c816975750c44ea9217e10f1f13ed1e94068a9e8 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Fri, 20 Feb 2015 15:49:43 -0500 Subject: [PATCH 18/29] Documentation and cleanup. --- components/script/dom/bindings/utils.rs | 2 - components/script/dom/cssstyledeclaration.rs | 6 +- components/script/dom/document.rs | 2 +- components/script/dom/htmliframeelement.rs | 4 +- components/script/dom/window.rs | 6 +- components/script/page.rs | 18 +++--- components/script/script_task.rs | 58 ++++++++++++++++---- 7 files changed, 67 insertions(+), 29 deletions(-) diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index 706c228c0b2..56c9b58288d 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -45,8 +45,6 @@ use js::JSFUN_CONSTRUCTOR; use js; /// Proxy handler for a WindowProxy. -#[allow(raw_pointer_derive)] -#[derive(Copy)] pub struct WindowProxyHandler(pub *const libc::c_void); #[allow(raw_pointer_derive)] diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs index e1be98f99af..b41adcb8b06 100644 --- a/components/script/dom/cssstyledeclaration.rs +++ b/components/script/dom/cssstyledeclaration.rs @@ -221,9 +221,8 @@ impl<'a> CSSStyleDeclarationMethods for JSRef<'a, CSSStyleDeclaration> { let owner = self.owner.root(); let window = window_from_node(owner.r()).root(); - let window = window.r(); let decl_block = parse_style_attribute(synthesized_declaration.as_slice(), - &window.get_url()); + &window.r().get_url()); // Step 7 if decl_block.normal.len() == 0 { @@ -269,9 +268,8 @@ impl<'a> CSSStyleDeclarationMethods for JSRef<'a, CSSStyleDeclaration> { let owner = self.owner.root(); let window = window_from_node(owner.r()).root(); - let window = window.r(); let decl_block = parse_style_attribute(property.as_slice(), - &window.get_url()); + &window.r().get_url()); let element: JSRef = ElementCast::from_ref(owner.r()); // Step 5 diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 1d611a6a643..6d559483e1e 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -386,7 +386,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { Some(root) => root, None => return None, }; - let root: JSRef = NodeCast::from_ref(root); + let root = NodeCast::from_ref(root); let win = self.window.root(); let address = match win.r().layout().hit_test(root.to_trusted_node_address(), *point) { Ok(HitTestResponse(node_address)) => { diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 3afcb2f4af2..35cc68c3da5 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -172,8 +172,8 @@ impl<'a> HTMLIFrameElementMethods for JSRef<'a, HTMLIFrameElement> { let window = window_from_node(self).root(); let window = window.r(); let children = window.page().children.borrow(); - children.iter().find(|child| { - let window = child.window().root(); + children.iter().find(|page| { + let window = page.window().root(); window.r().subpage() == Some(subpage_id) }).map(|page| page.window()) }) diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 30b1e6baaa0..28d7ac8b92d 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -56,7 +56,7 @@ use rustc_serialize::base64::{FromBase64, ToBase64, STANDARD}; use std::cell::{Cell, Ref, RefMut}; use std::default::Default; use std::ffi::CString; -use std::mem::replace; +use std::mem; use std::num::Float; use std::rc::Rc; use std::sync::mpsc::{channel, Receiver}; @@ -543,7 +543,7 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { /// layout task has finished any pending request messages. fn join_layout(self) { let mut layout_join_port = self.layout_join_port.borrow_mut(); - if let Some(join_port) = replace(&mut *layout_join_port, None) { + if let Some(join_port) = mem::replace(&mut *layout_join_port, None) { match join_port.try_recv() { Err(Empty) => { info!("script: waiting on layout"); @@ -652,7 +652,7 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { } fn windowproxy_handler(self) -> WindowProxyHandler { - self.dom_static.windowproxy_handler + WindowProxyHandler(self.dom_static.windowproxy_handler.0) } fn get_next_subpage_id(self) -> SubpageId { diff --git a/components/script/page.rs b/components/script/page.rs index c080be4d16c..ffaa25c9e14 100644 --- a/components/script/page.rs +++ b/components/script/page.rs @@ -10,10 +10,11 @@ use dom::window::Window; use msg::constellation_msg::{PipelineId, SubpageId}; use util::smallvec::SmallVec; +use std::cell::Cell; use std::rc::Rc; use url::Url; -/// Encapsulates a handle to a frame and its associated layout information. +/// Encapsulates a handle to a frame in a frame tree. #[jstraceable] pub struct Page { /// Pipeline id associated with this page. @@ -27,9 +28,11 @@ pub struct Page { /// Cached copy of the most recent url loaded by the script, after all redirections. /// TODO(tkuehn): this currently does not follow any particular caching policy - /// and simply caches pages forever (!). The bool indicates if reflow is required - /// when reloading. - url: DOMRefCell<(Url, bool)>, + /// and simply caches pages forever (!). + url: Url, + + /// Indicates if reflow is required when reloading. + needs_reflow: Cell, // Child Pages. pub children: DOMRefCell>>, @@ -67,7 +70,8 @@ impl Page { id: id, subpage_id: subpage_id, frame: DOMRefCell::new(None), - url: DOMRefCell::new((url, true)), + url: url, + needs_reflow: Cell::new(true), children: DOMRefCell::new(vec!()), } } @@ -123,8 +127,8 @@ impl Iterator for PageIterator { impl Page { pub fn set_reflow_status(&self, status: bool) -> bool { - let old = (*self.url.borrow()).1; - (*self.url.borrow_mut()).1 = status; + let old = self.needs_reflow.get(); + self.needs_reflow.set(status); old } diff --git a/components/script/script_task.rs b/components/script/script_task.rs index de9f36fab24..61174b97e92 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -3,7 +3,19 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //! The script task is the task that owns the DOM in memory, runs JavaScript, and spawns parsing -//! and layout tasks. +//! and layout tasks. It's in charge of processing events for all same-origin pages in a frame +//! tree, and manages the entire lifetime of pages in the frame tree from initial request to +//! teardown. +//! +//! Page loads follow a two-step process. When a request for a new page load is received, the +//! network request is initiated and the relevant data pertaining to the new page is stashed. +//! While the non-blocking request is ongoing, the script task is free to process further events, +//! noting when they pertain to ongoing loads (such as resizes/viewport adjustments). When the +//! initial response is received for an ongoing load, the second phase starts - the frame tree +//! entry is created, along with the Window and Document objects, and the appropriate parser +//! takes over the response body. Once parsing is complete, the document lifecycle for loading +//! a page runs its course and the script task returns to processing events in the main event +//! loop. #![allow(unsafe_blocks)] @@ -83,16 +95,27 @@ use time::Tm; thread_local!(pub static STACK_ROOTS: Cell> = Cell::new(None)); +/// A document load that is in the process of fetching the requested resource. Contains +/// data that will need to be present when the document and frame tree entry are created, +/// but is only easily available at initiation of the load and on a push basis (so some +/// data will be updated according to future resize events, viewport changes, etc.) struct InProgressLoad { + /// The pipeline which requested this load. pipeline_id: PipelineId, + /// The parent pipeline and child subpage associated with this load, if any. subpage_id: Option<(PipelineId, SubpageId)>, + /// The current window size associated with this pipeline. window_size: WindowSizeData, + /// Channel to the layout task associated with this pipeline. layout_chan: LayoutChan, + /// The current viewport clipping rectangle applying to this pipelie, if any. clip_rect: Option>, + /// The requested URL of the load. url: Url, } impl InProgressLoad { + /// Create a new InProgressLoad object. fn new(id: PipelineId, subpage_id: Option<(PipelineId, SubpageId)>, layout_chan: LayoutChan, @@ -445,6 +468,7 @@ impl ScriptTask { (js_runtime, js_context) } + // Return the root page in the frame tree. Panics if it doesn't exist. fn root_page(&self) -> Rc { self.page.borrow().as_ref().unwrap().clone() } @@ -472,7 +496,7 @@ impl ScriptTask { { let page = self.page.borrow(); - if let Some(ref page) = page.as_ref() { + if let Some(page) = page.as_ref() { for page in page.iter() { // Only process a resize if layout is idle. let window = page.window().root(); @@ -686,6 +710,7 @@ impl ScriptTask { panic!("Page rect message sent to nonexistent pipeline"); } + /// Handle a request to load a page in a new child frame of an existing page. fn handle_new_layout(&self, new_layout_info: NewLayoutInfo) { let NewLayoutInfo { old_pipeline_id, @@ -703,6 +728,7 @@ impl ScriptTask { let parent_window = parent_page.window().root(); let chan = layout_chan.downcast_ref::>().unwrap(); let layout_chan = LayoutChan(chan.clone()); + // Kick off the fetch for the new resource. let new_load = InProgressLoad::new(new_pipeline_id, Some((old_pipeline_id, subpage_id)), layout_chan, parent_window.r().window_size(), load_data.url.clone()); @@ -736,6 +762,8 @@ impl ScriptTask { window.r().thaw(); } + /// Handle a request to make a previously-created pipeline active. + //TODO: unsuspend JS and timers, etc. when we support such things. fn handle_activate(&self, pipeline_id: PipelineId) { // We should only get this message when moving in history, so all pages requested // should exist. @@ -790,8 +818,11 @@ impl ScriptTask { self.compositor.borrow_mut().close(); } + /// We have received notification that the response associated with a load has completed. + /// Kick off the document and frame tree creation process using the result. fn handle_page_fetch_complete(&self, id: PipelineId, subpage: Option, response: LoadResponse) { + // Any notification received should refer to an existing, in-progress load that is tracked. let idx = self.incomplete_loads.borrow().iter().position(|&:load| { load.pipeline_id == id && load.subpage_id.map(|sub| sub.1) == subpage }).unwrap(); @@ -832,7 +863,9 @@ impl ScriptTask { let final_url = response.metadata.final_url.clone(); debug!("ScriptTask: loading {} on page {:?}", incomplete.url.serialize(), incomplete.pipeline_id); - let root_page_exists = self.page.borrow().is_none(); + // We should either be initializing a root page or loading a child page of an + // existing one. + let root_page_exists = self.page.borrow().is_some(); assert!(incomplete.subpage_id.is_none() || root_page_exists); let frame_element = incomplete.subpage_id.and_then(|(parent_id, subpage_id)| { @@ -858,18 +891,17 @@ impl ScriptTask { self.compositor.borrow_mut().set_ready_state(incomplete.pipeline_id, Loading); - let last_modified = response.metadata.headers.as_ref().and_then(|headers| { - headers.get().map(|&LastModified(ref tm)| tm.clone()) - }); - let cx = self.js_context.borrow(); let cx = cx.as_ref().unwrap(); + // Create a new frame tree entry. let page = Rc::new(Page::new(incomplete.pipeline_id, incomplete.subpage_id.map(|p| p.1), final_url.clone())); - if root_page_exists { + if !root_page_exists { + // We have a new root frame tree. *self.page.borrow_mut() = Some(page.clone()); } else if let Some((parent, _)) = incomplete.subpage_id { + // We have a new child frame. let parent_page = self.root_page(); parent_page.find(parent).expect("received load for subpage with missing parent"); parent_page.children.borrow_mut().push(page.clone()); @@ -894,11 +926,15 @@ impl ScriptTask { let document = Document::new(window.r(), Some(final_url.clone()), IsHTMLDocument::HTMLDocument, None, DocumentSource::FromParser).root(); + + window.r().init_browser_context(document.r(), frame_element.r()); + + let last_modified = response.metadata.headers.as_ref().and_then(|headers| { + headers.get().map(|&LastModified(ref tm)| tm.clone()) + }); if let Some(tm) = last_modified { document.r().set_last_modified(dom_last_modified(&tm)); } - window.r().init_browser_context(document.r(), frame_element.r()); - // Create the root frame page.set_frame(Some(Frame { @@ -1097,6 +1133,8 @@ impl ScriptTask { self.force_reflow(&*page); } + /// Initiate a non-blocking fetch for a specified resource. Stores the InProgressLoad + /// argument until a notification is received that the fetch is complete. fn start_page_load(&self, incomplete: InProgressLoad, mut load_data: LoadData) { let id = incomplete.pipeline_id.clone(); let subpage = incomplete.subpage_id.clone().map(|p| p.1); From 4972b623e18d2bdad62a8c617e6885eb9b8158b8 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Thu, 26 Feb 2015 00:19:27 -0500 Subject: [PATCH 19/29] Separate disposing of layout data from the GCing of the DOM object reflectors. Change the order of operations when shutting down the script task to ensure that Window globals aren't used after they've been GCed. --- components/script/dom/node.rs | 9 +++++++++ components/script/dom/window.rs | 3 +++ components/script/script_task.rs | 19 ++++++++----------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 7526c2490d2..a0486e6ee11 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -490,9 +490,18 @@ pub trait NodeHelpers<'a> { fn get_unique_id(self) -> String; fn summarize(self) -> NodeInfo; + + fn teardown(self); } impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { + fn teardown(self) { + self.layout_data.dispose(); + for kid in self.children() { + kid.teardown(); + } + } + /// Dumps the subtree rooted at this node, for debugging. fn dump(self) { self.dump_indent(0); diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 28d7ac8b92d..097d8ed4ea0 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -461,6 +461,9 @@ impl<'a, T: Reflectable> ScriptHelpers for JSRef<'a, T> { impl<'a> WindowHelpers for JSRef<'a, Window> { fn clear_js_context(self) { + let document = self.Document().root(); + NodeCast::from_ref(document.r()).teardown(); + *self.js_context.borrow_mut() = None; *self.browser_context.borrow_mut() = None; } diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 61174b97e92..0538b2c0b72 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -74,7 +74,7 @@ use util::task_state; use geom::Rect; use geom::point::Point2D; use hyper::header::{LastModified, Headers}; -use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ, JS_GC}; +use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ}; use js::jsapi::{JSContext, JSRuntime, JSObject}; use js::jsapi::{JS_SetGCParameter, JSGC_MAX_BYTES}; use js::jsapi::{JS_SetGCCallback, JSGCStatus, JSGC_BEGIN, JSGC_END}; @@ -845,14 +845,16 @@ impl ScriptTask { let window = page.window().root(); if window.r().pipeline() == id { debug!("shutting down layout for root page {:?}", id); + // To ensure the elements of the DOM tree remain usable (such as the window global), + // don't free the JS context until all interactions with it are finished. + shut_down_layout(&page, exit_type); *self.js_context.borrow_mut() = None; - shut_down_layout(&page, (*self.js_runtime).ptr, exit_type); return true } // otherwise find just the matching page and exit all sub-pages if let Some(ref mut child_page) = page.remove(id) { - shut_down_layout(&*child_page, (*self.js_runtime).ptr, exit_type); + shut_down_layout(&*child_page, exit_type); } return false; } @@ -1167,7 +1169,7 @@ impl ScriptTask { } /// Shuts down layout for the given page tree. -fn shut_down_layout(page_tree: &Rc, rt: *mut JSRuntime, exit_type: PipelineExitType) { +fn shut_down_layout(page_tree: &Rc, exit_type: PipelineExitType) { let mut channels = vec!(); for page in page_tree.iter() { @@ -1182,19 +1184,14 @@ fn shut_down_layout(page_tree: &Rc, rt: *mut JSRuntime, exit_type: Pipelin } } - // Drop our references to the JSContext and DOM objects, potentially triggering a GC. + // Drop our references to the JSContext and DOM objects. for page in page_tree.iter() { let window = page.window().root(); window.r().clear_js_context(); + // Sever the connection between the global and the DOM tree page.set_frame(None); } - // Force a GC to make sure that our DOM reflectors are released before we tell - // layout to exit. - unsafe { - JS_GC(rt); - } - // Destroy the layout task. If there were node leaks, layout will now crash safely. for chan in channels.into_iter() { chan.send(layout_interface::Msg::ExitNow(exit_type)).ok(); From 1fb12b11f4beb2a544ba9ba1a4624aff857ad649 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Fri, 27 Feb 2015 19:17:55 -0500 Subject: [PATCH 20/29] Remove the newly-created Page from the tree if loading fails. --- components/script/script_task.rs | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 0538b2c0b72..59b6278a67d 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -909,6 +909,49 @@ impl ScriptTask { parent_page.children.borrow_mut().push(page.clone()); } + enum PageToRemove { + Root, + Child(PipelineId), + } + struct AutoPageRemover<'a> { + page: PageToRemove, + script_task: &'a ScriptTask, + neutered: bool, + } + impl<'a> AutoPageRemover<'a> { + fn new(script_task: &'a ScriptTask, page: PageToRemove) -> AutoPageRemover<'a> { + AutoPageRemover { + page: page, + script_task: script_task, + neutered: false, + } + } + + fn neuter(&mut self) { + self.neutered = true; + } + } + #[unsafe_destructor] + impl<'a> Drop for AutoPageRemover<'a> { + fn drop(&mut self) { + if !self.neutered { + match self.page { + PageToRemove::Root => *self.script_task.page.borrow_mut() = None, + PageToRemove::Child(id) => { + let _ = self.script_task.root_page().remove(id); + } + } + } + } + } + + let page_to_remove = if !root_page_exists { + PageToRemove::Root + } else { + PageToRemove::Child(incomplete.pipeline_id) + }; + let mut page_remover = AutoPageRemover::new(self, page_to_remove); + // Create the window and document objects. let window = Window::new(cx.clone(), page.clone(), @@ -1000,6 +1043,8 @@ impl ScriptTask { page_info)).unwrap(); } } + + page_remover.neuter(); } fn scroll_fragment_point(&self, pipeline_id: PipelineId, node: JSRef) { From 9a3d58032d53dce1627b2e443c131d45e2d3687d Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 2 Mar 2015 13:18:00 -0500 Subject: [PATCH 21/29] Update WPT expectations. --- .../document-metadata/the-base-element/base_multiple.html.ini | 2 +- .../the-iframe-element/move_iframe_in_dom_03.html.ini | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_03.html.ini diff --git a/tests/wpt/metadata/html/semantics/document-metadata/the-base-element/base_multiple.html.ini b/tests/wpt/metadata/html/semantics/document-metadata/the-base-element/base_multiple.html.ini index 956ccc14150..df76225f1fe 100644 --- a/tests/wpt/metadata/html/semantics/document-metadata/the-base-element/base_multiple.html.ini +++ b/tests/wpt/metadata/html/semantics/document-metadata/the-base-element/base_multiple.html.ini @@ -1,5 +1,5 @@ [base_multiple.html] type: testharness + expected: ERROR [The attributes of the a element must be affected by the first base element] expected: FAIL - diff --git a/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_03.html.ini b/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_03.html.ini new file mode 100644 index 00000000000..0ec075cd574 --- /dev/null +++ b/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_03.html.ini @@ -0,0 +1,3 @@ +[move_iframe_in_dom_03.html] + type: testharness + expected: TIMEOUT From 86baef2cc0acfee669b5ffad7eca9ac10a59ad6f Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Tue, 3 Mar 2015 09:38:39 +1000 Subject: [PATCH 22/29] Remove compositor layers when iframes are removed from doc or display:none. --- components/layout/block.rs | 5 ++ components/layout/construct.rs | 67 +++++++++++++++------------ components/layout/flow.rs | 4 ++ components/layout/fragment.rs | 19 +++++++- components/layout/layout_task.rs | 12 +++-- components/layout/util.rs | 25 ++++++++-- tests/ref/basic.list | 2 + tests/ref/iframe/hide_layers1.html | 20 ++++++++ tests/ref/iframe/hide_layers2.html | 18 +++++++ tests/ref/iframe/hide_layers_ref.html | 10 ++++ 10 files changed, 145 insertions(+), 37 deletions(-) create mode 100644 tests/ref/iframe/hide_layers1.html create mode 100644 tests/ref/iframe/hide_layers2.html create mode 100644 tests/ref/iframe/hide_layers_ref.html diff --git a/components/layout/block.rs b/components/layout/block.rs index e26fb5f8b8e..95ae010be07 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -52,6 +52,7 @@ 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 servo_util::geometry::{Au, MAX_AU}; use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize}; use servo_util::opts; @@ -1915,6 +1916,10 @@ impl Flow for BlockFlow { CoordinateSystem::Parent) .translate(stacking_context_position)); } + + fn remove_compositor_layers(&self, constellation_chan: ConstellationChan) { + self.fragment.remove_compositor_layers(constellation_chan); + } } impl fmt::Debug for BlockFlow { diff --git a/components/layout/construct.rs b/components/layout/construct.rs index b2ea8090ea6..c7e56426adc 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -232,6 +232,20 @@ impl<'a> FlowConstructor<'a> { } } + #[inline] + fn set_flow_construction_result(&self, node: &ThreadSafeLayoutNode, result: ConstructionResult) { + match result { + ConstructionResult::None => { + let mut layout_data_ref = node.mutate_layout_data(); + let layout_data = layout_data_ref.as_mut().expect("no layout data"); + layout_data.remove_compositor_layers(self.layout_context.shared.constellation_chan.clone()); + } + _ => {} + } + + node.set_flow_construction_result(result); + } + /// Builds the `ImageFragmentInfo` for the given image. This is out of line to guide inlining. fn build_fragment_info_for_image(&mut self, node: &ThreadSafeLayoutNode, url: Option) -> SpecificFragmentInfo { @@ -380,7 +394,7 @@ impl<'a> FlowConstructor<'a> { // If kid_flow is TableCaptionFlow, kid_flow should be added under // TableWrapperFlow. if flow.is_table() && kid_flow.is_table_caption() { - kid.set_flow_construction_result(ConstructionResult::Flow(kid_flow, + self.set_flow_construction_result(&kid, ConstructionResult::Flow(kid_flow, Descendants::new())) } else if flow.need_anonymous_flow(&*kid_flow) { consecutive_siblings.push(kid_flow) @@ -561,7 +575,7 @@ impl<'a> FlowConstructor<'a> { // box, so don't construct them. if node.type_id() == Some(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement))) { for kid in node.children() { - kid.set_flow_construction_result(ConstructionResult::None) + self.set_flow_construction_result(&kid, ConstructionResult::None) } } Some(Fragment::new_from_specific_info( @@ -691,7 +705,7 @@ impl<'a> FlowConstructor<'a> { fn build_fragments_for_replaced_inline_content(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult { for kid in node.children() { - kid.set_flow_construction_result(ConstructionResult::None) + self.set_flow_construction_result(&kid, ConstructionResult::None) } // If this node is ignorable whitespace, bail out now. @@ -1045,7 +1059,7 @@ 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() { - kid.set_flow_construction_result(ConstructionResult::None) + self.set_flow_construction_result(&kid, ConstructionResult::None) } let specific = SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node)); @@ -1173,15 +1187,15 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { // results of children. (display::T::none, _, _) => { for child in node.children() { - child.set_flow_construction_result(ConstructionResult::None); + self.set_flow_construction_result(&child, ConstructionResult::None); } - node.set_flow_construction_result(ConstructionResult::None); + self.set_flow_construction_result(node, 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); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, construction_result) } // Absolutely positioned elements will have computed value of @@ -1192,13 +1206,14 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { // below. (display::T::block, _, position::T::absolute) | (_, _, position::T::fixed) => { - node.set_flow_construction_result(self.build_flow_for_nonfloated_block(node)) + let construction_result = self.build_flow_for_nonfloated_block(node); + self.set_flow_construction_result(node, construction_result) } // List items contribute their own special flows. (display::T::list_item, float_value, _) => { - node.set_flow_construction_result(self.build_flow_for_list_item(node, - float_value)) + let construction_result = self.build_flow_for_list_item(node, float_value); + self.set_flow_construction_result(node, construction_result) } // Inline items that are absolutely-positioned contribute inline fragment construction @@ -1206,7 +1221,7 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { (display::T::inline, _, position::T::absolute) => { let construction_result = self.build_fragment_for_absolutely_positioned_inline(node); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, construction_result) } // Inline items contribute inline fragment construction results. @@ -1214,31 +1229,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); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, 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); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, construction_result) } // Table items contribute table flow construction results. (display::T::table_caption, _, _) => { let construction_result = self.build_flow_for_table_caption(node); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, construction_result) } // Table items contribute table flow construction results. (display::T::table_column_group, _, _) => { let construction_result = self.build_flow_for_table_colgroup(node); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, construction_result) } // Table items contribute table flow construction results. (display::T::table_column, _, _) => { let construction_result = self.build_fragments_for_table_column(node); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, construction_result) } // Table items contribute table flow construction results. @@ -1246,19 +1261,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); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, construction_result) } // Table items contribute table flow construction results. (display::T::table_row, _, _) => { let construction_result = self.build_flow_for_table_row(node); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, construction_result) } // Table items contribute table flow construction results. (display::T::table_cell, _, _) => { let construction_result = self.build_flow_for_table_cell(node); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, construction_result) } // Block flows that are not floated contribute block flow construction results. @@ -1267,14 +1282,15 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { // properties separately. (_, float::T::none, _) => { - node.set_flow_construction_result(self.build_flow_for_nonfloated_block(node)) + let construction_result = self.build_flow_for_nonfloated_block(node); + self.set_flow_construction_result(node, construction_result) } // Floated flows contribute float flow construction results. (_, float_value, _) => { let float_kind = FloatKind::from_property(float_value); - node.set_flow_construction_result( - self.build_flow_for_floated_block(node, float_kind)) + let construction_result = self.build_flow_for_floated_block(node, float_kind); + self.set_flow_construction_result(node, construction_result) } } @@ -1329,13 +1345,6 @@ impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> { let mut layout_data_ref = self.mutate_layout_data(); let layout_data = layout_data_ref.as_mut().expect("no layout data"); - match result { - ConstructionResult::None => { - layout_data.clear(); - } - _ => {} - } - let dst = self.get_construction_result(layout_data); *dst = result; diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 19f1df86c3a..ec618e03847 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -49,6 +49,7 @@ use wrapper::ThreadSafeLayoutNode; use geom::{Point2D, Rect, Size2D}; use gfx::display_list::ClippingRegion; use rustc_serialize::{Encoder, Encodable}; +use msg::constellation_msg::ConstellationChan; use msg::compositor_msg::LayerId; use servo_util::geometry::{Au, ZERO_RECT}; use servo_util::logical_geometry::{LogicalRect, LogicalSize, WritingMode}; @@ -312,6 +313,9 @@ pub trait Flow: fmt::Debug + Sync { /// Attempts to perform incremental fixup of this flow by replacing its fragment's style with /// the new style. This can only succeed if the flow has exactly one fragment. fn repair_style(&mut self, new_style: &Arc); + + /// Remove any compositor layers associated with this flow + fn remove_compositor_layers(&self, _: ConstellationChan) {} } // Base access diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 26a91139b31..31642bb4cd9 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -30,7 +30,7 @@ use gfx::text::glyph::CharIndex; use gfx::text::text_run::{TextRun, TextRunSlice}; use script_traits::UntrustedNodeAddress; use rustc_serialize::{Encodable, Encoder}; -use msg::constellation_msg::{PipelineId, SubpageId}; +use msg::constellation_msg::{ConstellationChan, Msg, PipelineId, SubpageId}; use net::image::holder::ImageHolder; use net::local_image_cache::LocalImageCache; use servo_util::geometry::{self, Au, ZERO_POINT}; @@ -2058,6 +2058,23 @@ impl Fragment { // box too. overflow } + + /// Remove any compositor layers associated with this fragment - it is being + /// removed from the tree or had its display property set to none. + /// TODO(gw): This just hides the compositor layer for now. In the future + /// it probably makes sense to provide a hint to the compositor whether + /// the layers should be destroyed to free memory. + pub fn remove_compositor_layers(&self, constellation_chan: ConstellationChan) { + match self.specific { + SpecificFragmentInfo::Iframe(ref iframe_info) => { + let ConstellationChan(ref chan) = constellation_chan; + chan.send(Msg::FrameRect(iframe_info.pipeline_id, + iframe_info.subpage_id, + Rect::zero())).unwrap(); + } + _ => {} + } + } } impl fmt::Debug for Fragment { diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index ca321f23526..dedc682af4c 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -124,6 +124,9 @@ pub struct LayoutTask { //// The channel to send messages to ourself. pub chan: LayoutChan, + /// The channel on which messages can be sent to the constellation. + pub constellation_chan: ConstellationChan, + /// The channel on which messages can be sent to the script task. pub script_chan: ScriptControlChan, @@ -272,6 +275,7 @@ impl LayoutTask { pipeline_port: pipeline_port, chan: chan, script_chan: script_chan, + constellation_chan: constellation_chan.clone(), paint_chan: paint_chan, time_profiler_chan: time_profiler_chan, resource_task: resource_task, @@ -412,7 +416,7 @@ impl LayoutTask { }, Msg::ReapLayoutData(dead_layout_data) => { unsafe { - LayoutTask::handle_reap_layout_data(dead_layout_data) + self.handle_reap_layout_data(dead_layout_data) } }, Msg::PrepareToExit(response_chan) => { @@ -441,7 +445,7 @@ impl LayoutTask { match self.port.recv().unwrap() { Msg::ReapLayoutData(dead_layout_data) => { unsafe { - LayoutTask::handle_reap_layout_data(dead_layout_data) + self.handle_reap_layout_data(dead_layout_data) } } Msg::ExitNow(exit_type) => { @@ -938,9 +942,9 @@ impl LayoutTask { /// Handles a message to destroy layout data. Layout data must be destroyed on *this* task /// because the struct type is transmuted to a different type on the script side. - unsafe fn handle_reap_layout_data(layout_data: LayoutData) { + unsafe fn handle_reap_layout_data(&self, layout_data: LayoutData) { let layout_data_wrapper: LayoutDataWrapper = mem::transmute(layout_data); - layout_data_wrapper.clear(); + layout_data_wrapper.remove_compositor_layers(self.constellation_chan.clone()); } /// Returns profiling information which is passed to the time profiler. diff --git a/components/layout/util.rs b/components/layout/util.rs index 8d4d72e7466..5b3bccd5b07 100644 --- a/components/layout/util.rs +++ b/components/layout/util.rs @@ -4,7 +4,7 @@ #![allow(unsafe_blocks)] -use construct::ConstructionResult; +use construct::{ConstructionItem, ConstructionResult}; use incremental::RestyleDamage; use parallel::DomParallelInfo; use wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode}; @@ -13,6 +13,7 @@ use azure::azure_hl::Color; use gfx::display_list::OpaqueNode; use gfx; use libc::{c_void, uintptr_t}; +use msg::constellation_msg::ConstellationChan; use script::dom::bindings::js::LayoutJS; use script::dom::node::{Node, SharedLayoutData}; use script::layout_interface::{LayoutChan, TrustedNodeAddress}; @@ -79,8 +80,26 @@ pub struct LayoutDataWrapper { } impl LayoutDataWrapper { - pub fn clear(&self) { - // TODO: Clear items related to this node, e.g. compositor layers + pub fn remove_compositor_layers(&self, constellation_chan: ConstellationChan) { + match self.data.flow_construction_result { + ConstructionResult::None => {} + ConstructionResult::Flow(ref flow_ref, _) => { + flow_ref.remove_compositor_layers(constellation_chan); + } + ConstructionResult::ConstructionItem(ref construction_item) => { + match construction_item { + &ConstructionItem::InlineFragments(ref inline_fragments) => { + for fragment in inline_fragments.fragments.iter() { + fragment.remove_compositor_layers(constellation_chan.clone()); + } + } + &ConstructionItem::Whitespace(..) => {} + &ConstructionItem::TableColumnFragment(ref fragment) => { + fragment.remove_compositor_layers(constellation_chan.clone()); + } + } + } + } } } diff --git a/tests/ref/basic.list b/tests/ref/basic.list index 0d83cc16d1f..b4bcd5d5e81 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -139,6 +139,8 @@ fragment=top != ../html/acid2.html acid2_ref.html == iframe/overflow.html iframe/overflow_ref.html == iframe/positioning_margin.html iframe/positioning_margin_ref.html == iframe/hide_and_show.html iframe/hide_and_show_ref.html +== iframe/hide_layers1.html iframe/hide_layers_ref.html +== iframe/hide_layers2.html iframe/hide_layers_ref.html == floated_generated_content_a.html floated_generated_content_b.html == inline_block_margin_a.html inline_block_margin_ref.html diff --git a/tests/ref/iframe/hide_layers1.html b/tests/ref/iframe/hide_layers1.html new file mode 100644 index 00000000000..159723211de --- /dev/null +++ b/tests/ref/iframe/hide_layers1.html @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/tests/ref/iframe/hide_layers2.html b/tests/ref/iframe/hide_layers2.html new file mode 100644 index 00000000000..1bc79c654da --- /dev/null +++ b/tests/ref/iframe/hide_layers2.html @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/tests/ref/iframe/hide_layers_ref.html b/tests/ref/iframe/hide_layers_ref.html new file mode 100644 index 00000000000..cb61f63d447 --- /dev/null +++ b/tests/ref/iframe/hide_layers_ref.html @@ -0,0 +1,10 @@ + + + + + + From b89339d05584e3bc27ba3a2cc58078e6e58a08d3 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Tue, 3 Mar 2015 17:56:55 -0500 Subject: [PATCH 23/29] Add missing crate to gonk. --- ports/gonk/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/gonk/src/main.rs b/ports/gonk/src/main.rs index c074ce632cd..fc53e3b7900 100644 --- a/ports/gonk/src/main.rs +++ b/ports/gonk/src/main.rs @@ -22,6 +22,7 @@ extern crate msg; extern crate gleam; extern crate layers; extern crate egl; +extern crate url; use util::opts; use servo::Browser; From 463f03826fb2e5459f8ce4e438f10b0b553fc62d Mon Sep 17 00:00:00 2001 From: Adenilson Cavalcanti Date: Tue, 3 Mar 2015 14:39:02 -0800 Subject: [PATCH 24/29] Cleanup compilation warning in OSX (should still work on Linux). --- components/util/memory.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/components/util/memory.rs b/components/util/memory.rs index c309cdbb4ed..6327a6e01f9 100644 --- a/components/util/memory.rs +++ b/components/util/memory.rs @@ -7,6 +7,7 @@ use libc::{c_char,c_int,c_void,size_t}; use std::borrow::ToOwned; use std::ffi::CString; +#[cfg(target_os = "linux")] use std::iter::AdditiveIterator; use std::old_io::timer::sleep; #[cfg(target_os="linux")] From 3b321789620aa7ae96ddfd8165a38a3d5db5ea0f Mon Sep 17 00:00:00 2001 From: Bryan Bell Date: Mon, 2 Mar 2015 21:04:42 -0800 Subject: [PATCH 25/29] Add inline-block overflow ref test For https://github.com/servo/servo/issues/3725 --- tests/ref/basic.list | 1 + tests/ref/inline_block_overflow.html | 6 ++++++ tests/ref/inline_block_overflow_ref.html | 6 ++++++ 3 files changed, 13 insertions(+) create mode 100644 tests/ref/inline_block_overflow.html create mode 100644 tests/ref/inline_block_overflow_ref.html diff --git a/tests/ref/basic.list b/tests/ref/basic.list index 9f25fa7af1d..87385a52989 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -145,6 +145,7 @@ fragment=top != ../html/acid2.html acid2_ref.html == inline_block_baseline_a.html inline_block_baseline_ref.html == inline_block_parent_width.html inline_block_parent_width_ref.html == inline_block_parent_width_percentage.html inline_block_parent_width_ref.html +== inline_block_overflow.html inline_block_overflow_ref.html == float_table_a.html float_table_ref.html == table_containing_block_a.html table_containing_block_ref.html == link_style_order.html link_style_order_ref.html diff --git a/tests/ref/inline_block_overflow.html b/tests/ref/inline_block_overflow.html new file mode 100644 index 00000000000..5b5aa9d0130 --- /dev/null +++ b/tests/ref/inline_block_overflow.html @@ -0,0 +1,6 @@ + + + + ab + + diff --git a/tests/ref/inline_block_overflow_ref.html b/tests/ref/inline_block_overflow_ref.html new file mode 100644 index 00000000000..63d14407d59 --- /dev/null +++ b/tests/ref/inline_block_overflow_ref.html @@ -0,0 +1,6 @@ + + + + ab + + From a35ffe1c6eccb0822d83427bd52327dec02d0496 Mon Sep 17 00:00:00 2001 From: Bryan Bell Date: Mon, 2 Mar 2015 20:39:04 -0800 Subject: [PATCH 26/29] For inline-block correctly use min-width With inline-block elements set the width to max(min width, specified width) instead of only using the specified width. --- components/layout/fragment.rs | 12 ++++++----- tests/ref/basic.list | 1 + tests/ref/inline_block_min_width.html | 26 +++++++++++++++++++++++ tests/ref/inline_block_min_width_ref.html | 25 ++++++++++++++++++++++ 4 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 tests/ref/inline_block_min_width.html create mode 100644 tests/ref/inline_block_min_width_ref.html diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 89b6d743fd1..adc0d001607 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -982,10 +982,11 @@ impl Fragment { fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizesContribution { let flags = self.quantities_included_in_intrinsic_inline_size(); let style = self.style(); - let specified = if flags.contains(INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED) { - MaybeAuto::from_style(style.content_inline_size(), Au(0)).specified_or_zero() + let (min_inline_size, specified) = if flags.contains(INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED) { + (model::specified(style.min_inline_size(), Au(0)), + MaybeAuto::from_style(style.content_inline_size(), Au(0)).specified_or_zero()) } else { - Au(0) + (Au(0), Au(0)) }; // FIXME(#2261, pcwalton): This won't work well for inlines: is this OK? @@ -993,7 +994,7 @@ impl Fragment { IntrinsicISizesContribution { content_intrinsic_sizes: IntrinsicISizes { - minimum_inline_size: specified, + minimum_inline_size: min_inline_size, preferred_inline_size: specified, }, surrounding_size: surrounding_inline_size, @@ -1721,7 +1722,8 @@ impl Fragment { SpecificFragmentInfo::InlineBlock(ref mut info) => { let block_flow = info.flow_ref.as_block(); self.border_box.size.inline = - block_flow.base.intrinsic_inline_sizes.preferred_inline_size; + max(block_flow.base.intrinsic_inline_sizes.minimum_inline_size, + block_flow.base.intrinsic_inline_sizes.preferred_inline_size); block_flow.base.block_container_inline_size = self.border_box.size.inline; } SpecificFragmentInfo::ScannedText(ref info) => { diff --git a/tests/ref/basic.list b/tests/ref/basic.list index 9f25fa7af1d..85730788883 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -156,6 +156,7 @@ fragment=top != ../html/acid2.html acid2_ref.html == img_block_display_a.html img_block_display_ref.html == after_block_iteration.html after_block_iteration_ref.html == inline_block_percentage_height_a.html inline_block_percentage_height_ref.html +== inline_block_min_width.html inline_block_min_width_ref.html == percentage_height_float_a.html percentage_height_float_ref.html == img_block_maxwidth_a.html img_block_maxwidth_ref.html == img_block_maxwidth_b.html img_block_maxwidth_ref.html diff --git a/tests/ref/inline_block_min_width.html b/tests/ref/inline_block_min_width.html new file mode 100644 index 00000000000..c677fb21d94 --- /dev/null +++ b/tests/ref/inline_block_min_width.html @@ -0,0 +1,26 @@ + + + + + + +
+ + diff --git a/tests/ref/inline_block_min_width_ref.html b/tests/ref/inline_block_min_width_ref.html new file mode 100644 index 00000000000..d0577dd80df --- /dev/null +++ b/tests/ref/inline_block_min_width_ref.html @@ -0,0 +1,25 @@ + + + + + + +
+ + From b613519a52115adc1f5222c4799a66a637a12b76 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 4 Mar 2015 13:43:51 +0100 Subject: [PATCH 27/29] Make submodule sync silent (and recursive). This eliminates the Synchronizing submodule url for 'support/android-rs-glue' Synchronizing submodule url for 'tests/wpt/web-platform-tests' messages that appeared for every `mach build` command. --- python/servo/command_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/servo/command_base.py b/python/servo/command_base.py index 20ee3fb6527..4bcae97c11b 100644 --- a/python/servo/command_base.py +++ b/python/servo/command_base.py @@ -202,7 +202,7 @@ class CommandBase(object): if self.context.bootstrapped: return - subprocess.check_call(["git", "submodule", "sync"]) + subprocess.check_call(["git", "submodule", "--quiet", "sync", "--recursive"]) submodules = subprocess.check_output(["git", "submodule", "status"]) for line in submodules.split('\n'): components = line.strip().split(' ') From a31d3d7f701b0194d9d83d6b684ab7718b9a954a Mon Sep 17 00:00:00 2001 From: Adenilson Cavalcanti Date: Wed, 4 Mar 2015 11:39:29 -0800 Subject: [PATCH 28/29] Due to changes on Page/Window interfaces, we no longer have use for flush_layout(). --- components/script/dom/document.rs | 4 ++-- components/script/dom/window.rs | 11 +++-------- components/script/script_task.rs | 2 +- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 6d559483e1e..059f568f873 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -523,7 +523,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { el.authentic_click_activation(event); self.commit_focus_transaction(); - window.r().flush_layout(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); + window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); } /// Return need force reflow or not @@ -664,7 +664,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { _ => () } - window.r().flush_layout(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); + window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); } fn set_current_script(self, script: Option>) { diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 097d8ed4ea0..29551114315 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -394,7 +394,6 @@ impl<'a> WindowMethods for JSRef<'a, Window> { pub trait WindowHelpers { fn clear_js_context(self); fn clear_js_context_for_script_deallocation(self); - fn flush_layout(self, goal: ReflowGoal, query: ReflowQueryType); fn init_browser_context(self, doc: JSRef, frame_element: Option>); fn load_url(self, href: DOMString); fn handle_fire_timer(self, timer_id: TimerId); @@ -476,10 +475,6 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { } } - fn flush_layout(self, goal: ReflowGoal, query: ReflowQueryType) { - self.reflow(goal, query); - } - /// Reflows the page if it's possible to do so and the page is dirty. This method will wait /// for the layout thread to complete (but see the `TODO` below). If there is no window size /// yet, the page is presumed invisible and no reflow is performed. @@ -567,14 +562,14 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { } fn content_box_query(self, content_box_request: TrustedNodeAddress) -> Rect { - self.flush_layout(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxQuery(content_box_request)); + self.reflow(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxQuery(content_box_request)); self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? let ContentBoxResponse(rect) = self.layout_rpc.content_box(); rect } fn content_boxes_query(self, content_boxes_request: TrustedNodeAddress) -> Vec> { - self.flush_layout(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxesQuery(content_boxes_request)); + self.reflow(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxesQuery(content_boxes_request)); self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? let ContentBoxesResponse(rects) = self.layout_rpc.content_boxes(); rects @@ -614,7 +609,7 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { fn handle_fire_timer(self, timer_id: TimerId) { self.timers.fire_timer(timer_id, self); - self.flush_layout(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); + self.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); } fn set_fragment_name(self, fragment: Option) { diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 59b6278a67d..673dc3510ef 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -1007,7 +1007,7 @@ impl ScriptTask { debug!("kicking off initial reflow of {:?}", final_url); document.r().content_changed(NodeCast::from_ref(document.r()), NodeDamage::OtherNodeDamage); - window.r().flush_layout(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); + window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); // No more reflow required page.set_reflow_status(false); From ff2f7ea567924059e7dff791b6ce438c0debaa4e Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Thu, 5 Mar 2015 16:21:57 +0100 Subject: [PATCH 29/29] Remove an unused import from properties.rs. --- components/style/properties.mako.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs index 5330de59a93..11089b165ce 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -2687,8 +2687,6 @@ pub mod shorthands { use properties::longhands::{background_color, background_position, background_repeat}; use properties::longhands::{background_attachment, background_image, background_size}; - use cssparser::Token; - let mut color = None; let mut image = None; let mut position = None;