style: Use CascadeFlags for what they're for.

Now that we have an Element around on cascade, we can stop using the cascade
flags mechanism to pass various element-related state, like "is this element the
root", or "should it use the item-based display fixup".

That fixes handwaviness in the handling of those flags from style reparenting,
and code duplication to handle tricky stuff like :visited.

There are a number of other changes that are worth noticing:

 * skip_root_and_item_based_display_fixup is renamed to skip_item_display_fixup:

   TElement::is_root() already implies being the document element, which by
   definition is not native anonymous and not a pseudo-element.

   Thus, you never get fixed-up if your NAC or a pseudo, which is what the code
   tried to avoid, so the only fixup with a point is the item one, which is
   necessary.

 * The pseudo-element probing code was refactored to return early a
   Option::<CascadeInputs>::None, which is nicer than what it was doing.

 * The visited_links_enabled check has moved to selector-matching time. The rest
   of the checks aren't based on whether the element is a link, or are properly
   guarded by parent_style.visited_style().is_some() or visited_rules.is_some().

   Thus you can transitively infer that no element will end up with a :visited
   style, not even from style reparenting.

Anyway, the underlying reason why I want the element in StyleAdjuster is because
we're going to implement an adjustment in there depending on the tag of the
element (converting display: contents to display: none depending on the tag), so
computing that information eagerly, including a hash lookup, wouldn't be nice.
This commit is contained in:
Emilio Cobos Álvarez 2018-01-22 23:53:03 +01:00
parent 104f5c2553
commit cd04664fb9
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
11 changed files with 233 additions and 298 deletions

View file

@ -455,7 +455,7 @@ impl<'le> TElement for ServoLayoutElement<'le> {
} }
} }
fn skip_root_and_item_based_display_fixup(&self) -> bool { fn skip_item_display_fixup(&self) -> bool {
false false
} }

View file

@ -22,7 +22,7 @@ use style::data::ElementData;
use style::dom::{LayoutIterator, NodeInfo, TElement, TNode}; use style::dom::{LayoutIterator, NodeInfo, TElement, TNode};
use style::dom::OpaqueNode; use style::dom::OpaqueNode;
use style::font_metrics::ServoMetricsProvider; use style::font_metrics::ServoMetricsProvider;
use style::properties::{CascadeFlags, ComputedValues}; use style::properties::ComputedValues;
use style::selector_parser::{PseudoElement, PseudoElementCascadeType, SelectorImpl}; use style::selector_parser::{PseudoElement, PseudoElementCascadeType, SelectorImpl};
use style::stylist::RuleInclusion; use style::stylist::RuleInclusion;
use webrender_api::ClipId; use webrender_api::ClipId;
@ -393,7 +393,6 @@ pub trait ThreadSafeLayoutElement
&context.guards, &context.guards,
&style_pseudo, &style_pseudo,
Some(data.styles.primary()), Some(data.styles.primary()),
CascadeFlags::empty(),
&ServoMetricsProvider, &ServoMetricsProvider,
) )
} }

View file

@ -680,7 +680,7 @@ pub trait TElement
/// Whether we should skip any root- or item-based display property /// Whether we should skip any root- or item-based display property
/// blockification on this element. (This function exists so that Gecko /// blockification on this element. (This function exists so that Gecko
/// native anonymous content can opt out of this style fixup.) /// native anonymous content can opt out of this style fixup.)
fn skip_root_and_item_based_display_fixup(&self) -> bool; fn skip_item_display_fixup(&self) -> bool;
/// Sets selector flags, which indicate what kinds of selectors may have /// Sets selector flags, which indicate what kinds of selectors may have
/// matched on this element and therefore what kind of work may need to /// matched on this element and therefore what kind of work may need to

View file

@ -10,7 +10,7 @@
use cssparser::{ToCss, serialize_identifier}; use cssparser::{ToCss, serialize_identifier};
use gecko_bindings::structs::{self, CSSPseudoElementType}; use gecko_bindings::structs::{self, CSSPseudoElementType};
use properties::{ComputedValues, PropertyFlags}; use properties::{CascadeFlags, ComputedValues, PropertyFlags};
use properties::longhands::display::computed_value::T as Display; use properties::longhands::display::computed_value::T as Display;
use selector_parser::{NonTSPseudoClass, PseudoElementCascadeType, SelectorImpl}; use selector_parser::{NonTSPseudoClass, PseudoElementCascadeType, SelectorImpl};
use std::fmt; use std::fmt;
@ -51,6 +51,15 @@ impl PseudoElement {
PseudoElementCascadeType::Lazy PseudoElementCascadeType::Lazy
} }
/// The CascadeFlags needed to cascade this pseudo-element.
///
/// This is only needed to support the broken INHERIT_ALL pseudo mode for
/// Servo.
#[inline]
pub fn cascade_flags(&self) -> CascadeFlags {
CascadeFlags::empty()
}
/// Whether the pseudo-element should inherit from the default computed /// Whether the pseudo-element should inherit from the default computed
/// values instead of from the parent element. /// values instead of from the parent element.
/// ///
@ -128,7 +137,7 @@ impl PseudoElement {
/// Whether this pseudo-element skips flex/grid container display-based /// Whether this pseudo-element skips flex/grid container display-based
/// fixup. /// fixup.
#[inline] #[inline]
pub fn skip_item_based_display_fixup(&self) -> bool { pub fn skip_item_display_fixup(&self) -> bool {
(self.flags() & structs::CSS_PSEUDO_ELEMENT_IS_FLEX_OR_GRID_ITEM) == 0 (self.flags() & structs::CSS_PSEUDO_ELEMENT_IS_FLEX_OR_GRID_ITEM) == 0
} }

View file

@ -1285,15 +1285,11 @@ impl<'le> TElement for GeckoElement<'le> {
} }
#[inline] #[inline]
fn skip_root_and_item_based_display_fixup(&self) -> bool { fn skip_item_display_fixup(&self) -> bool {
if !self.is_native_anonymous() { debug_assert!(
return false; self.implemented_pseudo_element().is_none(),
} "Just don't call me if I'm a pseudo, you should know the answer already"
);
if let Some(p) = self.implemented_pseudo_element() {
return p.skip_item_based_display_fixup();
}
self.is_root_of_native_anonymous_subtree() self.is_root_of_native_anonymous_subtree()
} }

View file

@ -3148,30 +3148,8 @@ bitflags! {
/// present, non-inherited styles are reset to their initial values. /// present, non-inherited styles are reset to their initial values.
const INHERIT_ALL = 1; const INHERIT_ALL = 1;
/// Whether to skip any display style fixup for root element, flex/grid
/// item, and ruby descendants.
const SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP = 1 << 1;
/// Whether to only cascade properties that are visited dependent. /// Whether to only cascade properties that are visited dependent.
const VISITED_DEPENDENT_ONLY = 1 << 2; const VISITED_DEPENDENT_ONLY = 1 << 1;
/// Whether the given element we're styling is the document element,
/// that is, matches :root.
///
/// Not set for native anonymous content since some NAC form their own
/// root, but share the device.
///
/// This affects some style adjustments, like blockification, and means
/// that it may affect global state, like the Device's root font-size.
const IS_ROOT_ELEMENT = 1 << 3;
/// Whether we're computing the style of a link, either visited or
/// unvisited.
const IS_LINK = 1 << 4;
/// Whether we're computing the style of a link element that happens to
/// be visited.
const IS_VISITED_LINK = 1 << 5;
} }
} }
@ -3287,7 +3265,7 @@ pub fn apply_declarations<'a, E, F, I>(
quirks_mode: QuirksMode, quirks_mode: QuirksMode,
rule_cache: Option<<&RuleCache>, rule_cache: Option<<&RuleCache>,
rule_cache_conditions: &mut RuleCacheConditions, rule_cache_conditions: &mut RuleCacheConditions,
_element: Option<E>, element: Option<E>,
) -> Arc<ComputedValues> ) -> Arc<ComputedValues>
where where
E: TElement, E: TElement,
@ -3326,7 +3304,7 @@ where
}; };
let mut context = computed::Context { let mut context = computed::Context {
is_root_element: flags.contains(CascadeFlags::IS_ROOT_ELEMENT), is_root_element: pseudo.is_none() && element.map_or(false, |e| e.is_root()),
// We'd really like to own the rules here to avoid refcount traffic, but // We'd really like to own the rules here to avoid refcount traffic, but
// animation's usage of `apply_declarations` make this tricky. See bug // animation's usage of `apply_declarations` make this tricky. See bug
// 1375525. // 1375525.
@ -3610,8 +3588,11 @@ where
builder.clear_modified_reset(); builder.clear_modified_reset();
StyleAdjuster::new(&mut builder) StyleAdjuster::new(&mut builder).adjust(
.adjust(layout_parent_style, flags); layout_parent_style,
element,
flags,
);
if builder.modified_reset() || !apply_reset { if builder.modified_reset() || !apply_reset {
// If we adjusted any reset structs, we can't cache this ComputedValues. // If we adjusted any reset structs, we can't cache this ComputedValues.

View file

@ -14,8 +14,7 @@ use element_state::{DocumentState, ElementState};
use fnv::FnvHashMap; use fnv::FnvHashMap;
use invalidation::element::document_state::InvalidationMatchingData; use invalidation::element::document_state::InvalidationMatchingData;
use invalidation::element::element_wrapper::ElementSnapshot; use invalidation::element::element_wrapper::ElementSnapshot;
use properties::ComputedValues; use properties::{CascadeFlags, ComputedValues, PropertyFlags};
use properties::PropertyFlags;
use properties::longhands::display::computed_value::T as Display; use properties::longhands::display::computed_value::T as Display;
use selector_parser::{AttrValue as SelectorAttrValue, PseudoElementCascadeType, SelectorParser}; use selector_parser::{AttrValue as SelectorAttrValue, PseudoElementCascadeType, SelectorParser};
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint, CaseSensitivity}; use selectors::attr::{AttrSelectorOperation, NamespaceConstraint, CaseSensitivity};
@ -174,10 +173,10 @@ impl PseudoElement {
self.is_precomputed() self.is_precomputed()
} }
/// Whether this pseudo-element skips flex/grid container /// Whether this pseudo-element skips flex/grid container display-based
/// display-based fixup. /// fixup.
#[inline] #[inline]
pub fn skip_item_based_display_fixup(&self) -> bool { pub fn skip_item_display_fixup(&self) -> bool {
!self.is_before_or_after() !self.is_before_or_after()
} }
@ -213,6 +212,43 @@ impl PseudoElement {
} }
} }
/// For most (but not all) anon-boxes, we inherit all values from the
/// parent, this is the hook in the style system to allow this.
///
/// FIXME(emilio): It's likely that this is broken in a variety of
/// situations, and what it really wants is just inherit some reset
/// properties... Also, I guess it just could do all: inherit on the
/// stylesheet, though chances are that'd be kinda slow if we don't cache
/// them...
pub fn cascade_flags(&self) -> CascadeFlags {
match *self {
PseudoElement::After |
PseudoElement::Before |
PseudoElement::Selection |
PseudoElement::DetailsContent |
PseudoElement::DetailsSummary => CascadeFlags::empty(),
// Anonymous table flows shouldn't inherit their parents properties in order
// to avoid doubling up styles such as transformations.
PseudoElement::ServoAnonymousTableCell |
PseudoElement::ServoAnonymousTableRow |
PseudoElement::ServoText |
PseudoElement::ServoInputText => CascadeFlags::empty(),
// For tables, we do want style to inherit, because TableWrapper is
// responsible for handling clipping and scrolling, while Table is
// responsible for creating stacking contexts.
//
// StackingContextCollectionFlags makes sure this is processed
// properly.
PseudoElement::ServoAnonymousTable |
PseudoElement::ServoAnonymousTableWrapper |
PseudoElement::ServoTableWrapper |
PseudoElement::ServoAnonymousBlock |
PseudoElement::ServoInlineBlockWrapper |
PseudoElement::ServoInlineAbsolute => CascadeFlags::INHERIT_ALL,
}
}
/// Covert non-canonical pseudo-element to canonical one, and keep a /// Covert non-canonical pseudo-element to canonical one, and keep a
/// canonical one as it is. /// canonical one as it is.
pub fn canonical(&self) -> PseudoElement { pub fn canonical(&self) -> PseudoElement {

View file

@ -6,6 +6,7 @@
//! a computed style needs in order for it to adhere to the CSS spec. //! a computed style needs in order for it to adhere to the CSS spec.
use app_units::Au; use app_units::Au;
use dom::TElement;
use properties::{self, CascadeFlags, ComputedValues, StyleBuilder}; use properties::{self, CascadeFlags, ComputedValues, StyleBuilder};
use properties::longhands::display::computed_value::T as Display; use properties::longhands::display::computed_value::T as Display;
use properties::longhands::float::computed_value::T as Float; use properties::longhands::float::computed_value::T as Float;
@ -50,13 +51,30 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
} }
} }
/// Whether we should skip any item-based display property blockification on
/// this element.
fn skip_item_display_fixup<E>(&self, element: Option<E>) -> bool
where
E: TElement,
{
if let Some(pseudo) = self.style.pseudo {
return pseudo.skip_item_display_fixup();
}
element.map_or(false, |e| e.skip_item_display_fixup())
}
/// Apply the blockification rules based on the table in CSS 2.2 section 9.7. /// Apply the blockification rules based on the table in CSS 2.2 section 9.7.
/// <https://drafts.csswg.org/css2/visuren.html#dis-pos-flo> /// <https://drafts.csswg.org/css2/visuren.html#dis-pos-flo>
fn blockify_if_necessary( fn blockify_if_necessary<E>(
&mut self, &mut self,
layout_parent_style: &ComputedValues, layout_parent_style: &ComputedValues,
flags: CascadeFlags, element: Option<E>,
) { )
where
E: TElement,
{
let mut blockify = false; let mut blockify = false;
macro_rules! blockify_if { macro_rules! blockify_if {
($if_what:expr) => { ($if_what:expr) => {
@ -66,8 +84,9 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
} }
} }
if !flags.contains(CascadeFlags::SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP) { let is_root = self.style.pseudo.is_none() && element.map_or(false, |e| e.is_root());
blockify_if!(flags.contains(CascadeFlags::IS_ROOT_ELEMENT)); blockify_if!(is_root);
if !self.skip_item_display_fixup(element) {
blockify_if!(layout_parent_style.get_box().clone_display().is_item_container()); blockify_if!(layout_parent_style.get_box().clone_display().is_item_container());
} }
@ -81,8 +100,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
} }
let display = self.style.get_box().clone_display(); let display = self.style.get_box().clone_display();
let blockified_display = let blockified_display = display.equivalent_block_display(is_root);
display.equivalent_block_display(flags.contains(CascadeFlags::IS_ROOT_ELEMENT));
if display != blockified_display { if display != blockified_display {
self.style.mutate_box().set_adjusted_display( self.style.mutate_box().set_adjusted_display(
blockified_display, blockified_display,
@ -477,12 +495,14 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
/// * suppress border and padding for ruby level containers, /// * suppress border and padding for ruby level containers,
/// * correct unicode-bidi. /// * correct unicode-bidi.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
fn adjust_for_ruby( fn adjust_for_ruby<E>(
&mut self, &mut self,
layout_parent_style: &ComputedValues, layout_parent_style: &ComputedValues,
flags: CascadeFlags, element: Option<E>,
) { )
use properties::CascadeFlags; where
E: TElement,
{
use properties::computed_value_flags::ComputedValueFlags; use properties::computed_value_flags::ComputedValueFlags;
use properties::longhands::unicode_bidi::computed_value::T as UnicodeBidi; use properties::longhands::unicode_bidi::computed_value::T as UnicodeBidi;
@ -491,7 +511,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
if self.should_suppress_linebreak(layout_parent_style) { if self.should_suppress_linebreak(layout_parent_style) {
self.style.flags.insert(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK); self.style.flags.insert(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
// Inlinify the display type if allowed. // Inlinify the display type if allowed.
if !flags.contains(CascadeFlags::SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP) { if !self.skip_item_display_fixup(element) {
let inline_display = self_display.inlinify(); let inline_display = self_display.inlinify();
if self_display != inline_display { if self_display != inline_display {
self.style.mutate_box().set_adjusted_display(inline_display, false); self.style.mutate_box().set_adjusted_display(inline_display, false);
@ -531,16 +551,22 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
/// ///
/// FIXME(emilio): This isn't technically a style adjustment thingie, could /// FIXME(emilio): This isn't technically a style adjustment thingie, could
/// it move somewhere else? /// it move somewhere else?
fn adjust_for_visited(&mut self, flags: CascadeFlags) { fn adjust_for_visited<E>(&mut self, element: Option<E>)
use properties::CascadeFlags; where
E: TElement,
{
use properties::computed_value_flags::ComputedValueFlags; use properties::computed_value_flags::ComputedValueFlags;
if !self.style.has_visited_style() { if !self.style.has_visited_style() {
return; return;
} }
let relevant_link_visited = if flags.contains(CascadeFlags::IS_LINK) { let is_link_element =
flags.contains(CascadeFlags::IS_VISITED_LINK) self.style.pseudo.is_none() &&
element.map_or(false, |e| e.is_link());
let relevant_link_visited = if is_link_element {
element.unwrap().is_visited_link()
} else { } else {
self.style.inherited_flags().contains(ComputedValueFlags::IS_RELEVANT_LINK_VISITED) self.style.inherited_flags().contains(ComputedValueFlags::IS_RELEVANT_LINK_VISITED)
}; };
@ -586,11 +612,35 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
/// When comparing to Gecko, this is similar to the work done by /// When comparing to Gecko, this is similar to the work done by
/// `nsStyleContext::ApplyStyleFixups`, plus some parts of /// `nsStyleContext::ApplyStyleFixups`, plus some parts of
/// `nsStyleSet::GetContext`. /// `nsStyleSet::GetContext`.
pub fn adjust( pub fn adjust<E>(
&mut self, &mut self,
layout_parent_style: &ComputedValues, layout_parent_style: &ComputedValues,
element: Option<E>,
flags: CascadeFlags, flags: CascadeFlags,
) { )
where
E: TElement,
{
if cfg!(debug_assertions) {
if element.and_then(|e| e.implemented_pseudo_element()).is_some() {
// It'd be nice to assert `self.style.pseudo == Some(&pseudo)`,
// but we do resolve ::-moz-list pseudos on ::before / ::after
// content, sigh.
debug_assert!(
self.style.pseudo.is_some(),
"Someone really messed up"
);
}
}
// FIXME(emilio): The apply_declarations callsite in Servo's
// animation, and the font stuff for Gecko
// (Stylist::compute_for_declarations) should pass an element to
// cascade(), then we can make this assertion hold everywhere.
// debug_assert!(
// element.is_some() || self.style.pseudo.is_some(),
// "Should always have an element around for non-pseudo styles"
// );
// Don't adjust visited styles, visited-dependent properties aren't // Don't adjust visited styles, visited-dependent properties aren't
// affected by these adjustments and it'd be just wasted work anyway. // affected by these adjustments and it'd be just wasted work anyway.
// //
@ -600,14 +650,14 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
return; return;
} }
self.adjust_for_visited(flags); self.adjust_for_visited(element);
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
{ {
self.adjust_for_prohibited_display_contents(); self.adjust_for_prohibited_display_contents();
self.adjust_for_fieldset_content(layout_parent_style); self.adjust_for_fieldset_content(layout_parent_style);
} }
self.adjust_for_top_layer(); self.adjust_for_top_layer();
self.blockify_if_necessary(layout_parent_style, flags); self.blockify_if_necessary(layout_parent_style, element);
self.adjust_for_position(); self.adjust_for_position();
self.adjust_for_overflow(); self.adjust_for_overflow();
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
@ -627,7 +677,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
self.adjust_for_text_decoration_lines(layout_parent_style); self.adjust_for_text_decoration_lines(layout_parent_style);
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
{ {
self.adjust_for_ruby(layout_parent_style, flags); self.adjust_for_ruby(layout_parent_style, element);
} }
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
{ {

View file

@ -9,9 +9,8 @@ use context::{CascadeInputs, ElementCascadeInputs, StyleContext};
use data::{ElementStyles, EagerPseudoStyles}; use data::{ElementStyles, EagerPseudoStyles};
use dom::TElement; use dom::TElement;
use log::LogLevel::Trace; use log::LogLevel::Trace;
use matching::{CascadeVisitedMode, MatchMethods}; use matching::MatchMethods;
use properties::{AnimationRules, CascadeFlags, ComputedValues}; use properties::{AnimationRules, ComputedValues};
use properties::cascade;
use properties::longhands::display::computed_value::T as Display; use properties::longhands::display::computed_value::T as Display;
use rule_tree::StrongRuleNode; use rule_tree::StrongRuleNode;
use selector_parser::{PseudoElement, SelectorImpl}; use selector_parser::{PseudoElement, SelectorImpl};
@ -161,7 +160,8 @@ where
parent_style.map_or(false, |s| s.visited_style().is_some()); parent_style.map_or(false, |s| s.visited_style().is_some());
let visited_rules = let visited_rules =
if inside_link || self.element.is_link() { if self.context.shared.visited_styles_enabled &&
(inside_link || self.element.is_link()) {
let visited_matching_results = let visited_matching_results =
self.match_primary(VisitedHandlingMode::RelevantLinkVisited); self.match_primary(VisitedHandlingMode::RelevantLinkVisited);
Some(visited_matching_results.rule_node) Some(visited_matching_results.rule_node)
@ -291,29 +291,37 @@ where
layout_parent_style: Option<&ComputedValues>, layout_parent_style: Option<&ComputedValues>,
pseudo: Option<&PseudoElement>, pseudo: Option<&PseudoElement>,
) -> ResolvedStyle { ) -> ResolvedStyle {
let mut style_if_visited = None; debug_assert!(
if parent_style.map_or(false, |s| s.visited_style().is_some()) || self.element.implemented_pseudo_element().is_none() || pseudo.is_none(),
inputs.visited_rules.is_some() { "Pseudo-elements can't have other pseudos!"
style_if_visited = Some(self.cascade_style( );
inputs.visited_rules.as_ref().or(inputs.rules.as_ref()), debug_assert!(pseudo.map_or(true, |p| p.is_eager()));
/* style_if_visited = */ None,
parent_style,
layout_parent_style,
CascadeVisitedMode::Visited,
pseudo,
));
}
ResolvedStyle( let implemented_pseudo = self.element.implemented_pseudo_element();
self.cascade_style( let pseudo = pseudo.or(implemented_pseudo.as_ref());
inputs.rules.as_ref(),
style_if_visited, let mut conditions = Default::default();
parent_style, let values = self.context.shared.stylist.cascade_style_and_visited(
layout_parent_style, Some(self.element),
CascadeVisitedMode::Unvisited, pseudo,
pseudo, inputs,
) &self.context.shared.guards,
) parent_style,
parent_style,
layout_parent_style,
&self.context.thread_local.font_metrics_provider,
Some(&self.context.thread_local.rule_cache),
&mut conditions,
);
self.context.thread_local.rule_cache.insert_if_possible(
&self.context.shared.guards,
&values,
pseudo,
&conditions
);
ResolvedStyle(values)
} }
/// Cascade the element and pseudo-element styles with the default parents. /// Cascade the element and pseudo-element styles with the default parents.
@ -469,7 +477,7 @@ where
) -> Option<StrongRuleNode> { ) -> Option<StrongRuleNode> {
debug!("Match pseudo {:?} for {:?}, visited: {:?}", debug!("Match pseudo {:?} for {:?}, visited: {:?}",
self.element, pseudo_element, visited_handling); self.element, pseudo_element, visited_handling);
debug_assert!(pseudo_element.is_eager() || pseudo_element.is_lazy()); debug_assert!(pseudo_element.is_eager());
debug_assert!(self.element.implemented_pseudo_element().is_none(), debug_assert!(self.element.implemented_pseudo_element().is_none(),
"Element pseudos can't have any other pseudo."); "Element pseudos can't have any other pseudo.");
@ -524,87 +532,4 @@ where
Some(rule_node) Some(rule_node)
} }
fn cascade_style(
&mut self,
rules: Option<&StrongRuleNode>,
style_if_visited: Option<Arc<ComputedValues>>,
mut parent_style: Option<&ComputedValues>,
layout_parent_style: Option<&ComputedValues>,
cascade_visited: CascadeVisitedMode,
pseudo: Option<&PseudoElement>,
) -> Arc<ComputedValues> {
debug_assert!(
self.element.implemented_pseudo_element().is_none() || pseudo.is_none(),
"Pseudo-elements can't have other pseudos!"
);
debug_assert!(pseudo.map_or(true, |p| p.is_eager()));
let mut cascade_flags = CascadeFlags::empty();
if self.element.skip_root_and_item_based_display_fixup() ||
pseudo.map_or(false, |p| p.skip_item_based_display_fixup()) {
cascade_flags.insert(CascadeFlags::SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP);
}
if pseudo.is_none() && self.element.is_link() {
cascade_flags.insert(CascadeFlags::IS_LINK);
if self.element.is_visited_link() &&
self.context.shared.visited_styles_enabled {
cascade_flags.insert(CascadeFlags::IS_VISITED_LINK);
}
}
if cascade_visited.visited_dependent_only() {
// If this element is a link, we want its visited style to inherit
// from the regular style of its parent, because only the
// visitedness of the relevant link should influence style.
if pseudo.is_some() || !self.element.is_link() {
parent_style = parent_style.map(|s| {
s.visited_style().unwrap_or(s)
});
}
cascade_flags.insert(CascadeFlags::VISITED_DEPENDENT_ONLY);
}
if !self.element.is_native_anonymous() &&
pseudo.is_none() &&
self.element.is_root()
{
cascade_flags.insert(CascadeFlags::IS_ROOT_ELEMENT);
}
let implemented_pseudo = self.element.implemented_pseudo_element();
let pseudo = pseudo.or(implemented_pseudo.as_ref());
let mut conditions = Default::default();
let values =
cascade(
self.context.shared.stylist.device(),
pseudo,
rules.unwrap_or(self.context.shared.stylist.rule_tree().root()),
&self.context.shared.guards,
parent_style,
parent_style,
layout_parent_style,
style_if_visited,
&self.context.thread_local.font_metrics_provider,
cascade_flags,
self.context.shared.quirks_mode(),
Some(&self.context.thread_local.rule_cache),
&mut conditions,
Some(self.element),
);
self.context
.thread_local
.rule_cache
.insert_if_possible(
&self.context.shared.guards,
&values,
pseudo,
&conditions
);
values
}
} }

View file

@ -22,6 +22,7 @@ use malloc_size_of::MallocUnconditionalShallowSizeOf;
use media_queries::Device; use media_queries::Device;
use properties::{self, CascadeFlags, ComputedValues}; use properties::{self, CascadeFlags, ComputedValues};
use properties::{AnimationRules, PropertyDeclarationBlock}; use properties::{AnimationRules, PropertyDeclarationBlock};
use rule_cache::{RuleCache, RuleCacheConditions};
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource}; use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
use selector_map::{PrecomputedHashMap, SelectorMap, SelectorMapEntry}; use selector_map::{PrecomputedHashMap, SelectorMap, SelectorMapEntry};
use selector_parser::{SelectorImpl, PerPseudoElementMap, PseudoElement}; use selector_parser::{SelectorImpl, PerPseudoElementMap, PseudoElement};
@ -650,7 +651,6 @@ impl Stylist {
guards: &StylesheetGuards, guards: &StylesheetGuards,
pseudo: &PseudoElement, pseudo: &PseudoElement,
parent: Option<&ComputedValues>, parent: Option<&ComputedValues>,
cascade_flags: CascadeFlags,
font_metrics: &FontMetricsProvider, font_metrics: &FontMetricsProvider,
) -> Arc<ComputedValues> ) -> Arc<ComputedValues>
where where
@ -668,7 +668,6 @@ impl Stylist {
guards, guards,
pseudo, pseudo,
parent, parent,
cascade_flags,
font_metrics, font_metrics,
rule_node rule_node
) )
@ -684,25 +683,23 @@ impl Stylist {
guards: &StylesheetGuards, guards: &StylesheetGuards,
pseudo: &PseudoElement, pseudo: &PseudoElement,
parent: Option<&ComputedValues>, parent: Option<&ComputedValues>,
cascade_flags: CascadeFlags,
font_metrics: &FontMetricsProvider, font_metrics: &FontMetricsProvider,
rule_node: StrongRuleNode rules: StrongRuleNode
) -> Arc<ComputedValues> ) -> Arc<ComputedValues>
where where
E: TElement, E: TElement,
{ {
self.compute_pseudo_element_style_with_inputs::<E>( self.compute_pseudo_element_style_with_inputs::<E>(
&CascadeInputs { CascadeInputs {
rules: Some(rule_node), rules: Some(rules),
visited_rules: None, visited_rules: None,
}, },
pseudo, pseudo,
guards, guards,
parent, parent,
font_metrics, font_metrics,
cascade_flags,
None, None,
).unwrap() )
} }
/// Returns the rule node for given precomputed pseudo-element. /// Returns the rule node for given precomputed pseudo-element.
@ -757,44 +754,10 @@ impl Stylist {
E: TElement, E: TElement,
{ {
use font_metrics::ServoMetricsProvider; use font_metrics::ServoMetricsProvider;
// For most (but not all) pseudo-elements, we inherit all values from the parent.
let inherit_all = match *pseudo {
// Anonymous table flows shouldn't inherit their parents properties in order
// to avoid doubling up styles such as transformations.
PseudoElement::ServoAnonymousTableCell |
PseudoElement::ServoAnonymousTableRow |
PseudoElement::ServoText |
PseudoElement::ServoInputText => false,
PseudoElement::ServoAnonymousBlock |
// For tables, we do want style to inherit, because TableWrapper is responsible
// for handling clipping and scrolling, while Table is responsible for creating
// stacking contexts. StackingContextCollectionFlags makes sure this is processed
// properly.
PseudoElement::ServoAnonymousTable |
PseudoElement::ServoAnonymousTableWrapper |
PseudoElement::ServoTableWrapper |
PseudoElement::ServoInlineBlockWrapper |
PseudoElement::ServoInlineAbsolute => true,
PseudoElement::Before |
PseudoElement::After |
PseudoElement::Selection |
PseudoElement::DetailsSummary |
PseudoElement::DetailsContent => {
unreachable!("That pseudo doesn't represent an anonymous box!")
}
};
let mut cascade_flags = CascadeFlags::empty();
if inherit_all {
cascade_flags.insert(CascadeFlags::INHERIT_ALL);
}
self.precomputed_values_for_pseudo::<E>( self.precomputed_values_for_pseudo::<E>(
guards, guards,
&pseudo, &pseudo,
Some(parent_style), Some(parent_style),
cascade_flags,
&ServoMetricsProvider, &ServoMetricsProvider,
) )
} }
@ -828,17 +791,16 @@ impl Stylist {
is_probe, is_probe,
rule_inclusion, rule_inclusion,
matching_fn matching_fn
); )?;
self.compute_pseudo_element_style_with_inputs( Some(self.compute_pseudo_element_style_with_inputs(
&cascade_inputs, cascade_inputs,
pseudo, pseudo,
guards, guards,
Some(parent_style), Some(parent_style),
font_metrics, font_metrics,
CascadeFlags::empty(),
Some(element), Some(element),
) ))
} }
/// Computes a pseudo-element style lazily using the given CascadeInputs. /// Computes a pseudo-element style lazily using the given CascadeInputs.
@ -847,23 +809,16 @@ impl Stylist {
/// their style with a new parent style. /// their style with a new parent style.
pub fn compute_pseudo_element_style_with_inputs<E>( pub fn compute_pseudo_element_style_with_inputs<E>(
&self, &self,
inputs: &CascadeInputs, inputs: CascadeInputs,
pseudo: &PseudoElement, pseudo: &PseudoElement,
guards: &StylesheetGuards, guards: &StylesheetGuards,
parent_style: Option<&ComputedValues>, parent_style: Option<&ComputedValues>,
font_metrics: &FontMetricsProvider, font_metrics: &FontMetricsProvider,
cascade_flags: CascadeFlags,
element: Option<E>, element: Option<E>,
) -> Option<Arc<ComputedValues>> ) -> Arc<ComputedValues>
where where
E: TElement, E: TElement,
{ {
// We may have only visited rules in cases when we are actually
// resolving, not probing, pseudo-element style.
if inputs.rules.is_none() && inputs.visited_rules.is_none() {
return None
}
// FIXME(emilio): The lack of layout_parent_style here could be // FIXME(emilio): The lack of layout_parent_style here could be
// worrying, but we're probably dropping the display fixup for // worrying, but we're probably dropping the display fixup for
// pseudos other than before and after, so it's probably ok. // pseudos other than before and after, so it's probably ok.
@ -876,17 +831,18 @@ impl Stylist {
// <fieldset style="display: contents">. That is, the computed value of // <fieldset style="display: contents">. That is, the computed value of
// display for the fieldset is "contents", even though it's not the used // display for the fieldset is "contents", even though it's not the used
// value, so we don't need to adjust in a different way anyway. // value, so we don't need to adjust in a different way anyway.
Some(self.compute_style_with_inputs( self.cascade_style_and_visited(
inputs, element,
Some(pseudo), Some(pseudo),
inputs,
guards, guards,
parent_style, parent_style,
parent_style, parent_style,
parent_style, parent_style,
font_metrics, font_metrics,
cascade_flags, /* rule_cache = */ None,
element, &mut RuleCacheConditions::default(),
)) )
} }
/// Computes a style using the given CascadeInputs. This can be used to /// Computes a style using the given CascadeInputs. This can be used to
@ -904,38 +860,43 @@ impl Stylist {
/// ///
/// is_link should be true if we're computing style for a link; that affects /// is_link should be true if we're computing style for a link; that affects
/// how :visited handling is done. /// how :visited handling is done.
pub fn compute_style_with_inputs<E>( pub fn cascade_style_and_visited<E>(
&self, &self,
inputs: &CascadeInputs, element: Option<E>,
pseudo: Option<&PseudoElement>, pseudo: Option<&PseudoElement>,
inputs: CascadeInputs,
guards: &StylesheetGuards, guards: &StylesheetGuards,
parent_style: Option<&ComputedValues>, parent_style: Option<&ComputedValues>,
parent_style_ignoring_first_line: Option<&ComputedValues>, parent_style_ignoring_first_line: Option<&ComputedValues>,
layout_parent_style: Option<&ComputedValues>, layout_parent_style: Option<&ComputedValues>,
font_metrics: &FontMetricsProvider, font_metrics: &FontMetricsProvider,
cascade_flags: CascadeFlags, rule_cache: Option<&RuleCache>,
element: Option<E>, rule_cache_conditions: &mut RuleCacheConditions,
) -> Arc<ComputedValues> ) -> Arc<ComputedValues>
where where
E: TElement, E: TElement,
{ {
debug_assert!(pseudo.is_some() || element.is_some(), "Huh?");
let cascade_flags =
pseudo.map_or(CascadeFlags::empty(), |p| p.cascade_flags());
// We need to compute visited values if we have visited rules or if our // We need to compute visited values if we have visited rules or if our
// parent has visited values. // parent has visited values.
let mut visited_values = None; let mut visited_values = None;
if inputs.visited_rules.is_some() || if inputs.visited_rules.is_some() ||
parent_style.and_then(|s| s.visited_style()).is_some() parent_style.and_then(|s| s.visited_style()).is_some()
{ {
// At this point inputs may have visited rules, or rules, or both, // At this point inputs may have visited rules, or rules.
// or neither (e.g. if it's a text style it may have neither). So
// we have to be a bit careful here.
let rule_node = match inputs.visited_rules.as_ref() { let rule_node = match inputs.visited_rules.as_ref() {
Some(rules) => rules, Some(rules) => rules,
None => inputs.rules.as_ref().unwrap_or(self.rule_tree().root()), None => inputs.rules.as_ref().unwrap_or(self.rule_tree.root()),
}; };
let inherited_style; let inherited_style;
let inherited_style_ignoring_first_line; let inherited_style_ignoring_first_line;
let layout_parent_style_for_visited; let layout_parent_style_for_visited;
if cascade_flags.contains(CascadeFlags::IS_LINK) { if pseudo.is_none() && element.unwrap().is_link() {
// We just want to use our parent style as our parent. // We just want to use our parent style as our parent.
inherited_style = parent_style; inherited_style = parent_style;
inherited_style_ignoring_first_line = parent_style_ignoring_first_line; inherited_style_ignoring_first_line = parent_style_ignoring_first_line;
@ -969,24 +930,22 @@ impl Stylist {
font_metrics, font_metrics,
cascade_flags | CascadeFlags::VISITED_DEPENDENT_ONLY, cascade_flags | CascadeFlags::VISITED_DEPENDENT_ONLY,
self.quirks_mode, self.quirks_mode,
/* rule_cache = */ None, rule_cache,
&mut Default::default(), rule_cache_conditions,
element, element,
)); ));
} }
// We may not have non-visited rules, if we only had visited ones. In
// that case we want to use the root rulenode for our non-visited rules.
let rules = inputs.rules.as_ref().unwrap_or(self.rule_tree.root());
// Read the comment on `precomputed_values_for_pseudo` to see why it's // Read the comment on `precomputed_values_for_pseudo` to see why it's
// difficult to assert that display: contents nodes never arrive here // difficult to assert that display: contents nodes never arrive here
// (tl;dr: It doesn't apply for replaced elements and such, but the // (tl;dr: It doesn't apply for replaced elements and such, but the
// computed value is still "contents"). // computed value is still "contents").
//
// FIXME(emilio): We should assert that it holds if pseudo.is_none()!
properties::cascade::<E>( properties::cascade::<E>(
&self.device, &self.device,
pseudo, pseudo,
rules, inputs.rules.as_ref().unwrap_or(self.rule_tree.root()),
guards, guards,
parent_style, parent_style,
parent_style_ignoring_first_line, parent_style_ignoring_first_line,
@ -995,9 +954,9 @@ impl Stylist {
font_metrics, font_metrics,
cascade_flags, cascade_flags,
self.quirks_mode, self.quirks_mode,
/* rule_cache = */ None, rule_cache,
&mut Default::default(), rule_cache_conditions,
None, element,
) )
} }
@ -1014,7 +973,7 @@ impl Stylist {
is_probe: bool, is_probe: bool,
rule_inclusion: RuleInclusion, rule_inclusion: RuleInclusion,
matching_fn: Option<&Fn(&PseudoElement) -> bool>, matching_fn: Option<&Fn(&PseudoElement) -> bool>,
) -> CascadeInputs ) -> Option<CascadeInputs>
where where
E: TElement E: TElement
{ {
@ -1051,7 +1010,6 @@ impl Stylist {
} }
}; };
let mut inputs = CascadeInputs::default();
let mut declarations = ApplicableDeclarationList::new(); let mut declarations = ApplicableDeclarationList::new();
let mut matching_context = MatchingContext::new( let mut matching_context = MatchingContext::new(
MatchingMode::ForStatelessPseudoElement, MatchingMode::ForStatelessPseudoElement,
@ -1074,19 +1032,14 @@ impl Stylist {
&mut set_selector_flags &mut set_selector_flags
); );
if !declarations.is_empty() { if declarations.is_empty() && is_probe {
let rule_node = return None;
self.rule_tree.compute_rule_node(&mut declarations, guards);
debug_assert!(rule_node != *self.rule_tree.root());
inputs.rules = Some(rule_node);
} }
if is_probe && inputs.rules.is_none() { let rules =
// When probing, don't compute visited styles if we have no self.rule_tree.compute_rule_node(&mut declarations, guards);
// unvisited styles.
return inputs;
}
let mut visited_rules = None;
if parent_style.visited_style().is_some() { if parent_style.visited_style().is_some() {
let mut declarations = ApplicableDeclarationList::new(); let mut declarations = ApplicableDeclarationList::new();
let mut matching_context = let mut matching_context =
@ -1114,14 +1067,15 @@ impl Stylist {
let rule_node = let rule_node =
self.rule_tree.insert_ordered_rules_with_important( self.rule_tree.insert_ordered_rules_with_important(
declarations.drain().map(|a| a.order_and_level()), declarations.drain().map(|a| a.order_and_level()),
guards); guards,
);
if rule_node != *self.rule_tree.root() { if rule_node != *self.rule_tree.root() {
inputs.visited_rules = Some(rule_node); visited_rules = Some(rule_node);
} }
} }
} }
inputs Some(CascadeInputs { rules: Some(rules), visited_rules })
} }
/// Set a given device, which may change the styles that apply to the /// Set a given device, which may change the styles that apply to the
@ -1596,7 +1550,7 @@ impl Stylist {
self.quirks_mode, self.quirks_mode,
/* rule_cache = */ None, /* rule_cache = */ None,
&mut Default::default(), &mut Default::default(),
None, /* element = */ None,
) )
} }

View file

@ -6,7 +6,7 @@ use cssparser::{ParseErrorKind, Parser, ParserInput};
use cssparser::ToCss as ParserToCss; use cssparser::ToCss as ParserToCss;
use env_logger::LogBuilder; use env_logger::LogBuilder;
use malloc_size_of::MallocSizeOfOps; use malloc_size_of::MallocSizeOfOps;
use selectors::{Element, NthIndexCache}; use selectors::NthIndexCache;
use selectors::matching::{MatchingContext, MatchingMode, matches_selector}; use selectors::matching::{MatchingContext, MatchingMode, matches_selector};
use servo_arc::{Arc, ArcBorrow, RawOffsetArc}; use servo_arc::{Arc, ArcBorrow, RawOffsetArc};
use smallvec::SmallVec; use smallvec::SmallVec;
@ -122,7 +122,7 @@ use style::gecko_properties;
use style::invalidation::element::restyle_hints; use style::invalidation::element::restyle_hints;
use style::media_queries::{Device, MediaList, parse_media_query_list}; use style::media_queries::{Device, MediaList, parse_media_query_list};
use style::parser::{Parse, ParserContext, self}; use style::parser::{Parse, ParserContext, self};
use style::properties::{CascadeFlags, ComputedValues, DeclarationSource, Importance}; use style::properties::{ComputedValues, DeclarationSource, Importance};
use style::properties::{LonghandId, LonghandIdSet, PropertyDeclaration, PropertyDeclarationBlock, PropertyId}; use style::properties::{LonghandId, LonghandIdSet, PropertyDeclaration, PropertyDeclarationBlock, PropertyId};
use style::properties::{PropertyDeclarationId, ShorthandId}; use style::properties::{PropertyDeclarationId, ShorthandId};
use style::properties::{SourcePropertyDeclaration, StyleBuilder}; use style::properties::{SourcePropertyDeclaration, StyleBuilder};
@ -2039,12 +2039,10 @@ pub extern "C" fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null:
page_decls, page_decls,
); );
let cascade_flags = CascadeFlags::SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP;
data.stylist.precomputed_values_for_pseudo_with_rule_node::<GeckoElement>( data.stylist.precomputed_values_for_pseudo_with_rule_node::<GeckoElement>(
&guards, &guards,
&pseudo, &pseudo,
parent_style_or_null.map(|x| &*x), parent_style_or_null.map(|x| &*x),
cascade_flags,
&metrics, &metrics,
rule_node rule_node
).into() ).into()
@ -2220,7 +2218,7 @@ fn get_pseudo_style(
PseudoElementCascadeType::Eager => { PseudoElementCascadeType::Eager => {
match *pseudo { match *pseudo {
PseudoElement::FirstLetter => { PseudoElement::FirstLetter => {
styles.pseudos.get(&pseudo).and_then(|pseudo_styles| { styles.pseudos.get(&pseudo).map(|pseudo_styles| {
// inherited_styles can be None when doing lazy resolution // inherited_styles can be None when doing lazy resolution
// (e.g. for computed style) or when probing. In that case // (e.g. for computed style) or when probing. In that case
// we just inherit from our element, which is what Gecko // we just inherit from our element, which is what Gecko
@ -2232,12 +2230,11 @@ fn get_pseudo_style(
let metrics = get_metrics_provider_for_product(); let metrics = get_metrics_provider_for_product();
let inputs = CascadeInputs::new_from_style(pseudo_styles); let inputs = CascadeInputs::new_from_style(pseudo_styles);
doc_data.stylist.compute_pseudo_element_style_with_inputs( doc_data.stylist.compute_pseudo_element_style_with_inputs(
&inputs, inputs,
pseudo, pseudo,
&guards, &guards,
Some(inherited_styles), Some(inherited_styles),
&metrics, &metrics,
CascadeFlags::empty(),
Some(element), Some(element),
) )
}) })
@ -3633,29 +3630,17 @@ pub extern "C" fn Servo_ReparentStyle(
let pseudo = style_to_reparent.pseudo(); let pseudo = style_to_reparent.pseudo();
let element = element.map(GeckoElement); let element = element.map(GeckoElement);
let mut cascade_flags = CascadeFlags::empty(); doc_data.stylist.cascade_style_and_visited(
if style_to_reparent.is_anon_box() { element,
cascade_flags.insert(CascadeFlags::SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP);
}
if let Some(element) = element {
if element.is_link() {
cascade_flags.insert(CascadeFlags::IS_LINK);
if element.is_visited_link() && doc_data.visited_styles_enabled() {
cascade_flags.insert(CascadeFlags::IS_VISITED_LINK);
}
};
}
doc_data.stylist.compute_style_with_inputs(
&inputs,
pseudo.as_ref(), pseudo.as_ref(),
inputs,
&StylesheetGuards::same(&guard), &StylesheetGuards::same(&guard),
Some(parent_style), Some(parent_style),
Some(parent_style_ignoring_first_line), Some(parent_style_ignoring_first_line),
Some(layout_parent_style), Some(layout_parent_style),
&metrics, &metrics,
cascade_flags, /* rule_cache = */ None,
element, &mut RuleCacheConditions::default(),
).into() ).into()
} }