diff --git a/components/style/data.rs b/components/style/data.rs index 76d64956f19..f486b24dc58 100644 --- a/components/style/data.rs +++ b/components/style/data.rs @@ -175,6 +175,25 @@ impl ElementStyles { self.primary().get_box().clone_display().is_none() } + /// Whether this element uses viewport units. + pub fn uses_viewport_units(&self) -> bool { + use crate::computed_value_flags::ComputedValueFlags; + + if self.primary().flags.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS) { + return true; + } + + for pseudo_style in self.pseudos.as_array() { + if let Some(ref pseudo_style) = pseudo_style { + if pseudo_style.flags.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS) { + return true; + } + } + } + + false + } + #[cfg(feature = "gecko")] fn size_of_excluding_cvs(&self, _ops: &mut MallocSizeOfOps) -> usize { // As the method name suggests, we don't measures the ComputedValues diff --git a/components/style/invalidation/element/restyle_hints.rs b/components/style/invalidation/element/restyle_hints.rs index c0bf2424d52..c423d853905 100644 --- a/components/style/invalidation/element/restyle_hints.rs +++ b/components/style/invalidation/element/restyle_hints.rs @@ -60,7 +60,12 @@ impl RestyleHint { /// Returns whether this hint invalidates the element and all its /// descendants. pub fn contains_subtree(&self) -> bool { - self.contains(RestyleHint::RESTYLE_SELF | RestyleHint::RESTYLE_DESCENDANTS) + self.contains(Self::restyle_subtree()) + } + + /// Returns whether we'll recascade all of the descendants. + pub fn will_recascade_subtree(&self) -> bool { + self.contains_subtree() || self.contains(Self::recascade_subtree()) } /// Returns whether we need to restyle this element. diff --git a/components/style/invalidation/mod.rs b/components/style/invalidation/mod.rs index a59d9474b0a..12b0d06853b 100644 --- a/components/style/invalidation/mod.rs +++ b/components/style/invalidation/mod.rs @@ -7,3 +7,4 @@ pub mod element; pub mod media_queries; pub mod stylesheets; +pub mod viewport_units; diff --git a/components/style/invalidation/viewport_units.rs b/components/style/invalidation/viewport_units.rs new file mode 100644 index 00000000000..acf8b095f9f --- /dev/null +++ b/components/style/invalidation/viewport_units.rs @@ -0,0 +1,57 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +//! Invalidates style of all elements that depend on viewport units. + +use crate::dom::{TElement, TNode}; +use crate::invalidation::element::restyle_hints::RestyleHint; + +/// Invalidates style of all elements that depend on viewport units. +/// +/// Returns whether any element was invalidated. +pub fn invalidate(root: E) -> bool +where + E: TElement, +{ + debug!("invalidation::viewport_units::invalidate({:?})", root); + invalidate_recursively(root) +} + +fn invalidate_recursively(element: E) -> bool +where + E: TElement, +{ + let mut data = match element.mutate_data() { + Some(data) => data, + None => return false, + }; + + if data.hint.will_recascade_subtree() { + debug!("invalidate_recursively: {:?} was already invalid", element); + return false; + } + + let uses_viewport_units = data.styles.uses_viewport_units(); + if uses_viewport_units { + debug!("invalidate_recursively: {:?} uses viewport units", element); + data.hint.insert(RestyleHint::RECASCADE_SELF); + } + + let mut any_children_invalid = false; + for child in element.traversal_children() { + if let Some(child) = child.as_element() { + any_children_invalid |= invalidate_recursively(child); + } + } + + if any_children_invalid { + debug!( + "invalidate_recursively: Children of {:?} changed, setting dirty descendants", + element + ); + unsafe { element.set_dirty_descendants() } + } + + uses_viewport_units || any_children_invalid +} diff --git a/components/style/properties/computed_value_flags.rs b/components/style/properties/computed_value_flags.rs index b3e5fe4f7f9..0942bbd0d13 100644 --- a/components/style/properties/computed_value_flags.rs +++ b/components/style/properties/computed_value_flags.rs @@ -101,6 +101,9 @@ bitflags! { /// Whether there are author-specified rules for `word-spacing`. const HAS_AUTHOR_SPECIFIED_WORD_SPACING = 1 << 19; + + /// Whether the style depends on viewport units. + const USES_VIEWPORT_UNITS = 1 << 20; } } diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs index 3f8c04abaab..3c4f7a2dbdc 100644 --- a/components/style/values/computed/length.rs +++ b/components/style/values/computed/length.rs @@ -5,6 +5,7 @@ //! `` computed values, and related ones. use super::{Context, Number, ToComputedValue}; +use crate::computed_value_flags::ComputedValueFlags; use crate::values::animated::ToAnimatedValue; use crate::values::computed::NonNegativeNumber; use crate::values::generics::length as generics; @@ -36,6 +37,7 @@ impl ToComputedValue for specified::NoCalcLength { length.to_computed_value(context, FontBaseSize::CurrentStyle) }, specified::NoCalcLength::ViewportPercentage(length) => { + context.builder.add_flags(ComputedValueFlags::USES_VIEWPORT_UNITS); length.to_computed_value(context.viewport_size_for_viewport_unit_resolution()) }, specified::NoCalcLength::ServoCharacterWidth(length) => {