diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index acc1daca80b..f9389fc1ecd 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -1114,6 +1114,14 @@ impl LayoutThread { if node.needs_dirty_on_viewport_size_changed() { // NB: The dirty bit is propagated down the tree. unsafe { node.set_dirty(true); } + + let mut current = node.parent_node(); + while let Some(node) = current { + if node.has_dirty_descendants() { break; } + unsafe { node.set_dirty_descendants(true); } + current = node.parent_node(); + } + next = iter.next_skipping_children(); } else { next = iter.next(); diff --git a/components/style/animation.rs b/components/style/animation.rs index 1767cd49234..63ddc9e8c69 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -394,6 +394,7 @@ fn compute_style_for_animation_step(context: &SharedStyleContext, false, Some(previous_style), None, + None, context.error_reporter.clone()); computed } diff --git a/components/style/cascade_info.rs b/components/style/cascade_info.rs new file mode 100644 index 00000000000..d0c584e7e0f --- /dev/null +++ b/components/style/cascade_info.rs @@ -0,0 +1,78 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use dom::TNode; +use properties::{DeclaredValue, PropertyDeclaration}; +use values::HasViewportPercentage; + +/// A structure to collect information about the cascade. +/// +/// This is useful to gather information about what an element is affected by, +/// and can be used in the future to track optimisations like when a +/// non-inherited property is explicitly inherited, in order to cut-off the +/// traversal. +pub struct CascadeInfo { + pub saw_viewport_units: bool, + #[cfg(debug_assertions)] + finished: bool, +} + +impl CascadeInfo { + #[cfg(debug_assertions)] + pub fn new() -> Self { + CascadeInfo { + saw_viewport_units: false, + finished: false, + } + } + + #[cfg(not(debug_assertions))] + pub fn new() -> Self { + CascadeInfo { + saw_viewport_units: false, + } + } + + /// Called when a property is cascaded. + /// + /// NOTE: We can add a vast amount of information here. + #[inline] + pub fn on_cascade_property(&mut self, + _property_declaration: &PropertyDeclaration, + value: &DeclaredValue) + where T: HasViewportPercentage + { + // TODO: we can be smarter and keep a property bitfield to keep track of + // the last applying rule. + if value.has_viewport_percentage() { + self.saw_viewport_units = true; + } + } + + #[cfg(debug_assertions)] + fn mark_as_finished_if_appropriate(&mut self) { + self.finished = true; + } + + #[cfg(not(debug_assertions))] + fn mark_as_finished_if_appropriate(&mut self) {} + + #[allow(unsafe_code)] + pub fn finish(mut self, node: &N) { + self.mark_as_finished_if_appropriate(); + + if self.saw_viewport_units { + unsafe { + node.set_dirty_on_viewport_size_changed(); + } + } + } +} + +#[cfg(debug_assertions)] +impl Drop for CascadeInfo { + fn drop(&mut self) { + debug_assert!(self.finished, + "Didn't use the result of CascadeInfo, if you don't need it, consider passing None"); + } +} diff --git a/components/style/custom_properties.rs b/components/style/custom_properties.rs index 5a7109f77ac..5189bbeaa37 100644 --- a/components/style/custom_properties.rs +++ b/components/style/custom_properties.rs @@ -39,6 +39,12 @@ pub struct SpecifiedValue { references: HashSet, } +impl ::values::HasViewportPercentage for SpecifiedValue { + fn has_viewport_percentage(&self) -> bool { + panic!("has_viewport_percentage called before resolving!"); + } +} + pub struct BorrowedSpecifiedValue<'a> { css: &'a str, first_token_type: TokenSerializationType, diff --git a/components/style/lib.rs b/components/style/lib.rs index 1f760aa6d10..4cab0c84b47 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -74,6 +74,7 @@ pub mod animation; pub mod attr; pub mod bezier; pub mod cache; +pub mod cascade_info; pub mod context; pub mod custom_properties; pub mod data; diff --git a/components/style/matching.rs b/components/style/matching.rs index 16f0b7571b1..f3bd443ee43 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -9,6 +9,7 @@ use animation; use arc_ptr_eq; use cache::{LRUCache, SimpleHashCache}; +use cascade_info::CascadeInfo; use context::{StyleContext, SharedStyleContext}; use data::PrivateStyleData; use dom::{TElement, TNode, TRestyleDamage}; @@ -443,6 +444,7 @@ trait PrivateMatchMethods: TNode { &mut old_style) && cacheable; } + let mut cascade_info = CascadeInfo::new(); let (this_style, is_cacheable) = match parent_style { Some(ref parent_style) => { let cache_entry = applicable_declarations_cache.find(applicable_declarations); @@ -456,6 +458,7 @@ trait PrivateMatchMethods: TNode { shareable, Some(&***parent_style), cached_computed_values, + Some(&mut cascade_info), shared_context.error_reporter.clone()) } None => { @@ -464,9 +467,11 @@ trait PrivateMatchMethods: TNode { shareable, None, None, + Some(&mut cascade_info), shared_context.error_reporter.clone()) } }; + cascade_info.finish(self); cacheable = cacheable && is_cacheable; @@ -497,7 +502,6 @@ trait PrivateMatchMethods: TNode { cacheable = cacheable && !animations_started } - // Cache the resolved style if it was cacheable. if cacheable { applicable_declarations_cache.insert(applicable_declarations.to_vec(), diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index fc6caa1cda3..09ebc9318bc 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -167,6 +167,7 @@ use parser::{ParserContext, ParserContextExtraData}; use properties::{CSSWideKeyword, DeclaredValue, Shorthand}; % endif + use cascade_info::CascadeInfo; use error_reporting::ParseErrorReporter; use properties::longhands; use properties::property_bit_field::PropertyBitField; @@ -185,6 +186,7 @@ context: &mut computed::Context, seen: &mut PropertyBitField, cacheable: &mut bool, + cascade_info: &mut Option<<&mut CascadeInfo>, error_reporter: &mut StdBox) { let declared_value = match *declaration { PropertyDeclaration::${property.camel_case}(ref declared_value) => { @@ -200,7 +202,13 @@ { let custom_props = context.style().custom_properties(); ::properties::substitute_variables_${property.ident}( - declared_value, &custom_props, |value| match *value { + declared_value, &custom_props, + |value| { + if let Some(ref mut cascade_info) = *cascade_info { + cascade_info.on_cascade_property(&declaration, + &value); + } + match *value { DeclaredValue::Value(ref specified_value) => { let computed = specified_value.to_computed_value(context); context.mutate_style().mutate_${data.current_style_struct.name_lower}() @@ -226,8 +234,8 @@ context.mutate_style().mutate_${data.current_style_struct.name_lower}() .copy_${property.ident}_from(inherited_struct); } - }, error_reporter - ); + } + }, error_reporter); } % if property.custom_cascade: diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 5e908f17d9a..986e0bc3e43 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -34,6 +34,7 @@ use stylesheets::Origin; use values::LocalToCss; use values::HasViewportPercentage; use values::computed::{self, ToComputedValue}; +use cascade_info::CascadeInfo; #[cfg(feature = "servo")] use values::specified::BorderStyle; use self::property_bit_field::PropertyBitField; @@ -773,6 +774,19 @@ pub enum DeclaredValue { // depending on whether the property is inherited. } +impl HasViewportPercentage for DeclaredValue { + fn has_viewport_percentage(&self) -> bool { + match *self { + DeclaredValue::Value(ref v) + => v.has_viewport_percentage(), + DeclaredValue::WithVariables { .. } + => panic!("DeclaredValue::has_viewport_percentage without resolving variables!"), + DeclaredValue::Initial | + DeclaredValue::Inherit => false, + } + } +} + impl ToCss for DeclaredValue { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { match *self { @@ -797,6 +811,20 @@ pub enum PropertyDeclaration { Custom(::custom_properties::Name, DeclaredValue<::custom_properties::SpecifiedValue>), } +impl HasViewportPercentage for PropertyDeclaration { + fn has_viewport_percentage(&self) -> bool { + match *self { + % for property in data.longhands: + PropertyDeclaration::${property.camel_case}(ref val) => { + val.has_viewport_percentage() + }, + % endfor + PropertyDeclaration::Custom(_, ref val) => { + val.has_viewport_percentage() + } + } + } +} #[derive(Eq, PartialEq, Copy, Clone)] pub enum PropertyDeclarationParseResult { @@ -854,19 +882,6 @@ impl ToCss for PropertyDeclaration { } } -impl HasViewportPercentage for PropertyDeclaration { - fn has_viewport_percentage(&self) -> bool { - match *self { - % for property in data.longhands: - PropertyDeclaration::${property.camel_case}(DeclaredValue::Value(ref val)) => { - val.has_viewport_percentage() - }, - % endfor - _ => false - } - } -} - impl PropertyDeclaration { pub fn name(&self) -> PropertyDeclarationName { match *self { @@ -1622,6 +1637,7 @@ fn cascade_with_cached_declarations( parent_style: &ComputedValues, cached_style: &ComputedValues, custom_properties: Option>, + mut cascade_info: Option<<&mut CascadeInfo>, mut error_reporter: StdBox) -> ComputedValues { let mut context = computed::Context { @@ -1664,7 +1680,12 @@ fn cascade_with_cached_declarations( let custom_props = context.style().custom_properties(); substitute_variables_${property.ident}( declared_value, &custom_props, - |value| match *value { + |value| { + if let Some(ref mut cascade_info) = cascade_info { + cascade_info.on_cascade_property(&declaration, + &value); + } + match *value { DeclaredValue::Value(ref specified_value) => { let computed = specified_value.to_computed_value(&context); @@ -1688,8 +1709,8 @@ fn cascade_with_cached_declarations( .copy_${property.ident}_from(inherited_struct); } DeclaredValue::WithVariables { .. } => unreachable!() - }, &mut error_reporter - ); + } + }, &mut error_reporter); % endif % if property.name in data.derived_longhands: @@ -1728,6 +1749,7 @@ pub type CascadePropertyFn = context: &mut computed::Context, seen: &mut PropertyBitField, cacheable: &mut bool, + cascade_info: &mut Option<<&mut CascadeInfo>, error_reporter: &mut StdBox); #[cfg(feature = "servo")] @@ -1760,6 +1782,7 @@ pub fn cascade(viewport_size: Size2D, shareable: bool, parent_style: Option<<&ComputedValues>, cached_style: Option<<&ComputedValues>, + mut cascade_info: Option<<&mut CascadeInfo>, mut error_reporter: StdBox) -> (ComputedValues, bool) { let initial_values = ComputedValues::initial_values(); @@ -1794,6 +1817,7 @@ pub fn cascade(viewport_size: Size2D, parent_style, cached_style, custom_properties, + cascade_info, error_reporter); return (style, false) } @@ -1863,6 +1887,7 @@ pub fn cascade(viewport_size: Size2D, &mut context, &mut seen, &mut cacheable, + &mut cascade_info, &mut error_reporter); } } diff --git a/components/style/selector_matching.rs b/components/style/selector_matching.rs index 7b18a897d73..2ae05ce1d6f 100644 --- a/components/style/selector_matching.rs +++ b/components/style/selector_matching.rs @@ -230,6 +230,7 @@ impl Stylist { &declarations, false, parent.map(|p| &**p), None, + None, Box::new(StdoutErrorReporter)); Some(Arc::new(computed)) } else { @@ -242,8 +243,9 @@ impl Stylist { pseudo: &PseudoElement, parent: &Arc) -> Option> - where E: Element + - PresentationalHintsSynthetizer { + where E: Element + + PresentationalHintsSynthetizer + { debug_assert!(TheSelectorImpl::pseudo_element_cascade_type(pseudo).is_lazy()); if self.pseudos_map.get(pseudo).is_none() { return None; @@ -262,8 +264,10 @@ impl Stylist { let (computed, _) = properties::cascade(self.device.au_viewport_size(), &declarations, false, - Some(&**parent), None, + Some(&**parent), None, None, Box::new(StdoutErrorReporter)); + + Some(Arc::new(computed)) } diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs index 64751c35c82..25bb03174e0 100644 --- a/components/style/stylesheets.rs +++ b/components/style/stylesheets.rs @@ -169,7 +169,8 @@ impl Stylesheet { origin: origin, rules: rules, media: None, - dirty_on_viewport_size_change: input.seen_viewport_percentages(), + dirty_on_viewport_size_change: + input.seen_viewport_percentages(), } } diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 9bab63b2692..0a5fdd2cfcd 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -370,20 +370,6 @@ pub fn recalc_style_at<'a, N, C>(context: &'a C, // NB: flow construction updates the bloom filter on the way up. put_thread_local_bloom_filter(bf, &unsafe_layout_node, context.shared_context()); - // Mark the node as DIRTY_ON_VIEWPORT_SIZE_CHANGE is it uses viewport - // percentage units. - if !node.needs_dirty_on_viewport_size_changed() { - if let Some(element) = node.as_element() { - if let Some(ref property_declaration_block) = *element.style_attribute() { - if property_declaration_block.declarations().any(|d| d.0.has_viewport_percentage()) { - unsafe { - node.set_dirty_on_viewport_size_changed(); - } - } - } - } - } - if nonincremental_layout { RestyleResult::Continue } else { diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index d27b0d6f893..77df3f94bdb 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -197,6 +197,7 @@ impl HasViewportPercentage for Length { fn has_viewport_percentage(&self) -> bool { match *self { Length::ViewportPercentage(_) => true, + Length::Calc(ref calc) => calc.has_viewport_percentage(), _ => false } } @@ -208,7 +209,7 @@ impl ToCss for Length { Length::Absolute(length) => write!(dest, "{}px", length.to_f32_px()), Length::FontRelative(length) => length.to_css(dest), Length::ViewportPercentage(length) => length.to_css(dest), - Length::Calc(calc) => calc.to_css(dest), + Length::Calc(ref calc) => calc.to_css(dest), Length::ServoCharacterWidth(_) => panic!("internal CSS values should never be serialized"), } @@ -467,6 +468,7 @@ pub struct CalcLengthOrPercentage { pub rem: Option, pub percentage: Option, } + impl CalcLengthOrPercentage { fn parse_sum(input: &mut Parser, expected_unit: CalcUnit) -> Result { let mut products = Vec::new(); @@ -751,6 +753,13 @@ impl CalcLengthOrPercentage { } } +impl HasViewportPercentage for CalcLengthOrPercentage { + fn has_viewport_percentage(&self) -> bool { + self.vw.is_some() || self.vh.is_some() || + self.vmin.is_some() || self.vmax.is_some() + } +} + impl ToCss for CalcLengthOrPercentage { #[allow(unused_assignments)] fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { @@ -823,7 +832,8 @@ pub enum LengthOrPercentage { impl HasViewportPercentage for LengthOrPercentage { fn has_viewport_percentage(&self) -> bool { match *self { - LengthOrPercentage::Length(length) => length.has_viewport_percentage(), + LengthOrPercentage::Length(ref length) => length.has_viewport_percentage(), + LengthOrPercentage::Calc(ref calc) => calc.has_viewport_percentage(), _ => false } } @@ -882,7 +892,8 @@ pub enum LengthOrPercentageOrAuto { impl HasViewportPercentage for LengthOrPercentageOrAuto { fn has_viewport_percentage(&self) -> bool { match *self { - LengthOrPercentageOrAuto::Length(length) => length.has_viewport_percentage(), + LengthOrPercentageOrAuto::Length(ref length) => length.has_viewport_percentage(), + LengthOrPercentageOrAuto::Calc(ref calc) => calc.has_viewport_percentage(), _ => false } } @@ -941,7 +952,8 @@ pub enum LengthOrPercentageOrNone { impl HasViewportPercentage for LengthOrPercentageOrNone { fn has_viewport_percentage(&self) -> bool { match *self { - LengthOrPercentageOrNone::Length(length) => length.has_viewport_percentage(), + LengthOrPercentageOrNone::Length(ref length) => length.has_viewport_percentage(), + LengthOrPercentageOrNone::Calc(ref calc) => calc.has_viewport_percentage(), _ => false } } @@ -950,9 +962,9 @@ impl HasViewportPercentage for LengthOrPercentageOrNone { impl ToCss for LengthOrPercentageOrNone { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { match *self { - LengthOrPercentageOrNone::Length(length) => length.to_css(dest), - LengthOrPercentageOrNone::Percentage(percentage) => percentage.to_css(dest), - LengthOrPercentageOrNone::Calc(calc) => calc.to_css(dest), + LengthOrPercentageOrNone::Length(ref length) => length.to_css(dest), + LengthOrPercentageOrNone::Percentage(ref percentage) => percentage.to_css(dest), + LengthOrPercentageOrNone::Calc(ref calc) => calc.to_css(dest), LengthOrPercentageOrNone::None => dest.write_str("none"), } } @@ -997,7 +1009,7 @@ pub enum LengthOrNone { impl HasViewportPercentage for LengthOrNone { fn has_viewport_percentage(&self) -> bool { match *self { - LengthOrNone::Length(length) => length.has_viewport_percentage(), + LengthOrNone::Length(ref length) => length.has_viewport_percentage(), _ => false } } @@ -1051,6 +1063,7 @@ impl HasViewportPercentage for LengthOrPercentageOrAutoOrContent { fn has_viewport_percentage(&self) -> bool { match *self { LengthOrPercentageOrAutoOrContent::Length(length) => length.has_viewport_percentage(), + LengthOrPercentageOrAutoOrContent::Calc(ref calc) => calc.has_viewport_percentage(), _ => false } } diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index da98ef5f303..cfe571f4892 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -1356,6 +1356,18 @@ "url": "/_mozilla/css/direction_style_caching.html" } ], + "css/dirty_viewport.html": [ + { + "path": "css/dirty_viewport.html", + "references": [ + [ + "/_mozilla/css/dirty_viewport_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/dirty_viewport.html" + } + ], "css/empty_cells_a.html": [ { "path": "css/empty_cells_a.html", @@ -10576,6 +10588,18 @@ "url": "/_mozilla/css/direction_style_caching.html" } ], + "css/dirty_viewport.html": [ + { + "path": "css/dirty_viewport.html", + "references": [ + [ + "/_mozilla/css/dirty_viewport_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/dirty_viewport.html" + } + ], "css/empty_cells_a.html": [ { "path": "css/empty_cells_a.html", diff --git a/tests/wpt/mozilla/tests/css/dirty_viewport.html b/tests/wpt/mozilla/tests/css/dirty_viewport.html new file mode 100644 index 00000000000..5adc2687d51 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/dirty_viewport.html @@ -0,0 +1,11 @@ + + +Test elements subject to viewport units are restyled correctly + + + diff --git a/tests/wpt/mozilla/tests/css/dirty_viewport_inner.html b/tests/wpt/mozilla/tests/css/dirty_viewport_inner.html new file mode 100644 index 00000000000..de6a46602a6 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/dirty_viewport_inner.html @@ -0,0 +1,11 @@ + +Dirty viewport test (inner) + +
diff --git a/tests/wpt/mozilla/tests/css/dirty_viewport_ref.html b/tests/wpt/mozilla/tests/css/dirty_viewport_ref.html new file mode 100644 index 00000000000..759da0245ed --- /dev/null +++ b/tests/wpt/mozilla/tests/css/dirty_viewport_ref.html @@ -0,0 +1,9 @@ + + +