зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #17570 - Implement :visited support for lazy pseudo-elements (from bzbarsky:visited-pseudos); r=jryans
<!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix https://bugzilla.mozilla.org/show_bug.cgi?id=1364242 <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: ff19e69d2388725cc15784b12c897c755ab06b6b --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : 3f8a4c1f743b5373d812bc451f4abdab9820c9cb
This commit is contained in:
Родитель
a0fc47069d
Коммит
4fdbba3a10
|
@ -426,6 +426,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
|
||||||
&style_pseudo,
|
&style_pseudo,
|
||||||
RuleInclusion::All,
|
RuleInclusion::All,
|
||||||
data.styles.primary(),
|
data.styles.primary(),
|
||||||
|
/* is_probe = */ false,
|
||||||
&ServoMetricsProvider)
|
&ServoMetricsProvider)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clone()
|
.clone()
|
||||||
|
|
|
@ -184,7 +184,7 @@ impl Default for CascadeInputs {
|
||||||
|
|
||||||
impl CascadeInputs {
|
impl CascadeInputs {
|
||||||
/// Construct inputs from previous cascade results, if any.
|
/// Construct inputs from previous cascade results, if any.
|
||||||
fn new_from_style(style: &Arc<ComputedValues>) -> Self {
|
pub fn new_from_style(style: &Arc<ComputedValues>) -> Self {
|
||||||
CascadeInputs {
|
CascadeInputs {
|
||||||
rules: style.rules.clone(),
|
rules: style.rules.clone(),
|
||||||
visited_rules: style.get_visited_style().and_then(|v| v.rules.clone()),
|
visited_rules: style.get_visited_style().and_then(|v| v.rules.clone()),
|
||||||
|
@ -204,6 +204,11 @@ impl CascadeInputs {
|
||||||
self.rules.as_mut()
|
self.rules.as_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets a reference to the rule node, if any.
|
||||||
|
pub fn get_rules(&self) -> Option<&StrongRuleNode> {
|
||||||
|
self.rules.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets a reference to the rule node. Panic if the element does not have
|
/// Gets a reference to the rule node. Panic if the element does not have
|
||||||
/// rule node.
|
/// rule node.
|
||||||
pub fn rules(&self) -> &StrongRuleNode {
|
pub fn rules(&self) -> &StrongRuleNode {
|
||||||
|
|
|
@ -151,7 +151,10 @@ impl CascadeVisitedMode {
|
||||||
fn rules<'a>(&self, inputs: &'a CascadeInputs) -> &'a StrongRuleNode {
|
fn rules<'a>(&self, inputs: &'a CascadeInputs) -> &'a StrongRuleNode {
|
||||||
match *self {
|
match *self {
|
||||||
CascadeVisitedMode::Unvisited => inputs.rules(),
|
CascadeVisitedMode::Unvisited => inputs.rules(),
|
||||||
CascadeVisitedMode::Visited => inputs.visited_rules(),
|
CascadeVisitedMode::Visited => match inputs.get_visited_rules() {
|
||||||
|
Some(rules) => rules,
|
||||||
|
None => inputs.rules(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +169,7 @@ impl CascadeVisitedMode {
|
||||||
/// Returns the computed values based on the cascade mode. In visited mode,
|
/// Returns the computed values based on the cascade mode. In visited mode,
|
||||||
/// visited values are only returned if they already exist. If they don't,
|
/// visited values are only returned if they already exist. If they don't,
|
||||||
/// we fallback to the regular, unvisited styles.
|
/// we fallback to the regular, unvisited styles.
|
||||||
fn values<'a>(&self, values: &'a Arc<ComputedValues>) -> &'a Arc<ComputedValues> {
|
pub fn values<'a>(&self, values: &'a Arc<ComputedValues>) -> &'a Arc<ComputedValues> {
|
||||||
if *self == CascadeVisitedMode::Visited && values.get_visited_style().is_some() {
|
if *self == CascadeVisitedMode::Visited && values.get_visited_style().is_some() {
|
||||||
return values.visited_style();
|
return values.visited_style();
|
||||||
}
|
}
|
||||||
|
@ -288,10 +291,35 @@ trait PrivateMatchMethods: TElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the ComputedValues (if any) for our inheritance parent.
|
||||||
|
fn get_inherited_style_and_parent(&self) -> ParentElementAndStyle<Self> {
|
||||||
|
let parent_el = self.inheritance_parent();
|
||||||
|
let parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
|
||||||
|
let parent_style = parent_data.as_ref().map(|d| {
|
||||||
|
// Sometimes Gecko eagerly styles things without processing
|
||||||
|
// pending restyles first. In general we'd like to avoid this,
|
||||||
|
// but there can be good reasons (for example, needing to
|
||||||
|
// construct a frame for some small piece of newly-added
|
||||||
|
// content in order to do something specific with that frame,
|
||||||
|
// but not wanting to flush all of layout).
|
||||||
|
debug_assert!(cfg!(feature = "gecko") ||
|
||||||
|
parent_el.unwrap().has_current_styles(d));
|
||||||
|
d.styles.primary()
|
||||||
|
});
|
||||||
|
|
||||||
|
ParentElementAndStyle {
|
||||||
|
element: parent_el,
|
||||||
|
style: parent_style.cloned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A common path for the cascade used by both primary elements and eager
|
/// A common path for the cascade used by both primary elements and eager
|
||||||
/// pseudo-elements after collecting the appropriate rules to use.
|
/// pseudo-elements after collecting the appropriate rules to use.
|
||||||
///
|
///
|
||||||
/// `primary_style` is expected to be Some for eager pseudo-elements.
|
/// `primary_style` is expected to be Some for eager pseudo-elements.
|
||||||
|
///
|
||||||
|
/// `parent_info` is our style parent and its primary style, if
|
||||||
|
/// it's already been computed.
|
||||||
fn cascade_with_rules(&self,
|
fn cascade_with_rules(&self,
|
||||||
shared_context: &SharedStyleContext,
|
shared_context: &SharedStyleContext,
|
||||||
font_metrics_provider: &FontMetricsProvider,
|
font_metrics_provider: &FontMetricsProvider,
|
||||||
|
@ -299,6 +327,7 @@ trait PrivateMatchMethods: TElement {
|
||||||
primary_style: Option<&Arc<ComputedValues>>,
|
primary_style: Option<&Arc<ComputedValues>>,
|
||||||
cascade_target: CascadeTarget,
|
cascade_target: CascadeTarget,
|
||||||
cascade_visited: CascadeVisitedMode,
|
cascade_visited: CascadeVisitedMode,
|
||||||
|
parent_info: Option<&ParentElementAndStyle<Self>>,
|
||||||
visited_values_to_insert: Option<Arc<ComputedValues>>)
|
visited_values_to_insert: Option<Arc<ComputedValues>>)
|
||||||
-> Arc<ComputedValues> {
|
-> Arc<ComputedValues> {
|
||||||
let mut cascade_info = CascadeInfo::new();
|
let mut cascade_info = CascadeInfo::new();
|
||||||
|
@ -318,23 +347,18 @@ trait PrivateMatchMethods: TElement {
|
||||||
|
|
||||||
// Grab the inherited values.
|
// Grab the inherited values.
|
||||||
let parent_el;
|
let parent_el;
|
||||||
let parent_data;
|
let element_and_style; // So parent_el and style_to_inherit_from are known live.
|
||||||
let style_to_inherit_from = match cascade_target {
|
let style_to_inherit_from = match cascade_target {
|
||||||
CascadeTarget::Normal => {
|
CascadeTarget::Normal => {
|
||||||
parent_el = self.inheritance_parent();
|
let info = match parent_info {
|
||||||
parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
|
Some(element_and_style) => element_and_style,
|
||||||
let parent_style = parent_data.as_ref().map(|d| {
|
None => {
|
||||||
// Sometimes Gecko eagerly styles things without processing
|
element_and_style = self.get_inherited_style_and_parent();
|
||||||
// pending restyles first. In general we'd like to avoid this,
|
&element_and_style
|
||||||
// but there can be good reasons (for example, needing to
|
}
|
||||||
// construct a frame for some small piece of newly-added
|
};
|
||||||
// content in order to do something specific with that frame,
|
parent_el = info.element;
|
||||||
// but not wanting to flush all of layout).
|
info.style.as_ref().map(|s| cascade_visited.values(s))
|
||||||
debug_assert!(cfg!(feature = "gecko") ||
|
|
||||||
parent_el.unwrap().has_current_styles(d));
|
|
||||||
d.styles.primary()
|
|
||||||
});
|
|
||||||
parent_style.map(|s| cascade_visited.values(s))
|
|
||||||
}
|
}
|
||||||
CascadeTarget::EagerPseudo => {
|
CascadeTarget::EagerPseudo => {
|
||||||
parent_el = Some(self.clone());
|
parent_el = Some(self.clone());
|
||||||
|
@ -391,11 +415,15 @@ trait PrivateMatchMethods: TElement {
|
||||||
/// pseudo-elements.
|
/// pseudo-elements.
|
||||||
///
|
///
|
||||||
/// `primary_style` is expected to be Some for eager pseudo-elements.
|
/// `primary_style` is expected to be Some for eager pseudo-elements.
|
||||||
|
///
|
||||||
|
/// `parent_info` is our style parent and its primary style, if
|
||||||
|
/// it's already been computed.
|
||||||
fn cascade_internal(&self,
|
fn cascade_internal(&self,
|
||||||
context: &StyleContext<Self>,
|
context: &StyleContext<Self>,
|
||||||
primary_style: Option<&Arc<ComputedValues>>,
|
primary_style: Option<&Arc<ComputedValues>>,
|
||||||
primary_inputs: &CascadeInputs,
|
primary_inputs: &CascadeInputs,
|
||||||
eager_pseudo_inputs: Option<&CascadeInputs>,
|
eager_pseudo_inputs: Option<&CascadeInputs>,
|
||||||
|
parent_info: Option<&ParentElementAndStyle<Self>>,
|
||||||
cascade_visited: CascadeVisitedMode)
|
cascade_visited: CascadeVisitedMode)
|
||||||
-> Arc<ComputedValues> {
|
-> Arc<ComputedValues> {
|
||||||
if let Some(pseudo) = self.implemented_pseudo_element() {
|
if let Some(pseudo) = self.implemented_pseudo_element() {
|
||||||
|
@ -459,15 +487,19 @@ trait PrivateMatchMethods: TElement {
|
||||||
primary_style,
|
primary_style,
|
||||||
cascade_target,
|
cascade_target,
|
||||||
cascade_visited,
|
cascade_visited,
|
||||||
|
parent_info,
|
||||||
visited_values_to_insert)
|
visited_values_to_insert)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes values and damage for the primary style of an element, setting
|
/// Computes values and damage for the primary style of an element, setting
|
||||||
/// them on the ElementData.
|
/// them on the ElementData.
|
||||||
|
///
|
||||||
|
/// `parent_info` is our style parent and its primary style.
|
||||||
fn cascade_primary(&self,
|
fn cascade_primary(&self,
|
||||||
context: &mut StyleContext<Self>,
|
context: &mut StyleContext<Self>,
|
||||||
data: &mut ElementData,
|
data: &mut ElementData,
|
||||||
important_rules_changed: bool,
|
important_rules_changed: bool,
|
||||||
|
parent_info: &ParentElementAndStyle<Self>,
|
||||||
cascade_visited: CascadeVisitedMode)
|
cascade_visited: CascadeVisitedMode)
|
||||||
-> ChildCascadeRequirement {
|
-> ChildCascadeRequirement {
|
||||||
debug!("Cascade primary for {:?}, visited: {:?}", self, cascade_visited);
|
debug!("Cascade primary for {:?}, visited: {:?}", self, cascade_visited);
|
||||||
|
@ -485,7 +517,10 @@ trait PrivateMatchMethods: TElement {
|
||||||
// visited case. This early return is especially important for the
|
// visited case. This early return is especially important for the
|
||||||
// `cascade_primary_and_pseudos` path since we rely on the state of
|
// `cascade_primary_and_pseudos` path since we rely on the state of
|
||||||
// some previous matching run.
|
// some previous matching run.
|
||||||
if !cascade_visited.has_rules(primary_inputs) {
|
//
|
||||||
|
// Note that we cannot take this early return if our parent has
|
||||||
|
// visited style, because then we too have visited style.
|
||||||
|
if !cascade_visited.has_rules(primary_inputs) && !parent_info.has_visited_style() {
|
||||||
return ChildCascadeRequirement::CanSkipCascade
|
return ChildCascadeRequirement::CanSkipCascade
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,6 +529,7 @@ trait PrivateMatchMethods: TElement {
|
||||||
None,
|
None,
|
||||||
primary_inputs,
|
primary_inputs,
|
||||||
None,
|
None,
|
||||||
|
/* parent_info = */ None,
|
||||||
cascade_visited)
|
cascade_visited)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -582,6 +618,7 @@ trait PrivateMatchMethods: TElement {
|
||||||
data.styles.get_primary(),
|
data.styles.get_primary(),
|
||||||
primary_inputs,
|
primary_inputs,
|
||||||
Some(pseudo_inputs),
|
Some(pseudo_inputs),
|
||||||
|
/* parent_info = */ None,
|
||||||
cascade_visited)
|
cascade_visited)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -625,6 +662,7 @@ trait PrivateMatchMethods: TElement {
|
||||||
Some(primary_style),
|
Some(primary_style),
|
||||||
CascadeTarget::Normal,
|
CascadeTarget::Normal,
|
||||||
CascadeVisitedMode::Unvisited,
|
CascadeVisitedMode::Unvisited,
|
||||||
|
/* parent_info = */ None,
|
||||||
None))
|
None))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -877,6 +915,23 @@ trait PrivateMatchMethods: TElement {
|
||||||
|
|
||||||
impl<E: TElement> PrivateMatchMethods for E {}
|
impl<E: TElement> PrivateMatchMethods for E {}
|
||||||
|
|
||||||
|
/// A struct that holds an element we inherit from and its ComputedValues.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ParentElementAndStyle<E: TElement> {
|
||||||
|
/// Our style parent element.
|
||||||
|
element: Option<E>,
|
||||||
|
/// Element's primary ComputedValues. Not a borrow because we can't prove
|
||||||
|
/// that the thing hanging off element won't change while we're passing this
|
||||||
|
/// struct around.
|
||||||
|
style: Option<Arc<ComputedValues>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: TElement> ParentElementAndStyle<E> {
|
||||||
|
fn has_visited_style(&self) -> bool {
|
||||||
|
self.style.as_ref().map_or(false, |v| { v.get_visited_style().is_some() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Collects the outputs of the primary matching process, including the rule
|
/// Collects the outputs of the primary matching process, including the rule
|
||||||
/// node and other associated data.
|
/// node and other associated data.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -944,13 +999,21 @@ pub trait MatchMethods : TElement {
|
||||||
let relevant_link_found = primary_results.relevant_link_found;
|
let relevant_link_found = primary_results.relevant_link_found;
|
||||||
if relevant_link_found {
|
if relevant_link_found {
|
||||||
self.match_primary(context, data, VisitedHandlingMode::RelevantLinkVisited);
|
self.match_primary(context, data, VisitedHandlingMode::RelevantLinkVisited);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Even if there is no relevant link, we need to cascade visited styles
|
||||||
|
// if our parent has visited styles.
|
||||||
|
let parent_and_styles = self.get_inherited_style_and_parent();
|
||||||
|
if relevant_link_found || parent_and_styles.has_visited_style() {
|
||||||
self.cascade_primary(context, data, important_rules_changed,
|
self.cascade_primary(context, data, important_rules_changed,
|
||||||
|
&parent_and_styles,
|
||||||
CascadeVisitedMode::Visited);
|
CascadeVisitedMode::Visited);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cascade properties and compute primary values.
|
// Cascade properties and compute primary values.
|
||||||
let child_cascade_requirement =
|
let child_cascade_requirement =
|
||||||
self.cascade_primary(context, data, important_rules_changed,
|
self.cascade_primary(context, data, important_rules_changed,
|
||||||
|
&parent_and_styles,
|
||||||
CascadeVisitedMode::Unvisited);
|
CascadeVisitedMode::Unvisited);
|
||||||
|
|
||||||
// Match and cascade eager pseudo-elements.
|
// Match and cascade eager pseudo-elements.
|
||||||
|
@ -1017,10 +1080,13 @@ pub trait MatchMethods : TElement {
|
||||||
// visited ComputedValues are placed within the regular ComputedValues,
|
// visited ComputedValues are placed within the regular ComputedValues,
|
||||||
// which is immutable after the cascade. If there aren't any visited
|
// which is immutable after the cascade. If there aren't any visited
|
||||||
// rules, these calls will return without cascading.
|
// rules, these calls will return without cascading.
|
||||||
|
let parent_and_styles = self.get_inherited_style_and_parent();
|
||||||
self.cascade_primary(context, &mut data, important_rules_changed,
|
self.cascade_primary(context, &mut data, important_rules_changed,
|
||||||
|
&parent_and_styles,
|
||||||
CascadeVisitedMode::Visited);
|
CascadeVisitedMode::Visited);
|
||||||
let child_cascade_requirement =
|
let child_cascade_requirement =
|
||||||
self.cascade_primary(context, &mut data, important_rules_changed,
|
self.cascade_primary(context, &mut data, important_rules_changed,
|
||||||
|
&parent_and_styles,
|
||||||
CascadeVisitedMode::Unvisited);
|
CascadeVisitedMode::Unvisited);
|
||||||
self.cascade_pseudos(context, &mut data, CascadeVisitedMode::Visited);
|
self.cascade_pseudos(context, &mut data, CascadeVisitedMode::Visited);
|
||||||
self.cascade_pseudos(context, &mut data, CascadeVisitedMode::Unvisited);
|
self.cascade_pseudos(context, &mut data, CascadeVisitedMode::Unvisited);
|
||||||
|
@ -1211,8 +1277,10 @@ pub trait MatchMethods : TElement {
|
||||||
// Compute rule nodes for eagerly-cascaded pseudo-elements.
|
// Compute rule nodes for eagerly-cascaded pseudo-elements.
|
||||||
let mut matches_different_pseudos = false;
|
let mut matches_different_pseudos = false;
|
||||||
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
||||||
// For pseudo-elements, we only try to match visited rules if there
|
// For eager pseudo-elements, we only try to match visited rules if
|
||||||
// are also unvisited rules. (This matches Gecko's behavior.)
|
// there are also unvisited rules. (This matches Gecko's behavior
|
||||||
|
// for probing pseudo-elements, and for eager pseudo-elements Gecko
|
||||||
|
// does not try to resolve style if the probe says there isn't any.)
|
||||||
if visited_handling == VisitedHandlingMode::RelevantLinkVisited &&
|
if visited_handling == VisitedHandlingMode::RelevantLinkVisited &&
|
||||||
!context.cascade_inputs().pseudos.has(&pseudo) {
|
!context.cascade_inputs().pseudos.has(&pseudo) {
|
||||||
return
|
return
|
||||||
|
@ -1587,6 +1655,7 @@ pub trait MatchMethods : TElement {
|
||||||
Some(primary_style),
|
Some(primary_style),
|
||||||
CascadeTarget::Normal,
|
CascadeTarget::Normal,
|
||||||
CascadeVisitedMode::Unvisited,
|
CascadeVisitedMode::Unvisited,
|
||||||
|
/* parent_info = */ None,
|
||||||
None)
|
None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
use {Atom, LocalName, Namespace};
|
use {Atom, LocalName, Namespace};
|
||||||
use applicable_declarations::{ApplicableDeclarationBlock, ApplicableDeclarationList};
|
use applicable_declarations::{ApplicableDeclarationBlock, ApplicableDeclarationList};
|
||||||
use bit_vec::BitVec;
|
use bit_vec::BitVec;
|
||||||
use context::QuirksMode;
|
use context::{CascadeInputs, QuirksMode};
|
||||||
use dom::TElement;
|
use dom::TElement;
|
||||||
use element_state::ElementState;
|
use element_state::ElementState;
|
||||||
use error_reporting::create_error_reporter;
|
use error_reporting::create_error_reporter;
|
||||||
|
@ -16,6 +16,7 @@ use font_metrics::FontMetricsProvider;
|
||||||
use gecko_bindings::structs::{nsIAtom, StyleRuleInclusion};
|
use gecko_bindings::structs::{nsIAtom, StyleRuleInclusion};
|
||||||
use invalidation::element::invalidation_map::InvalidationMap;
|
use invalidation::element::invalidation_map::InvalidationMap;
|
||||||
use invalidation::media_queries::EffectiveMediaQueryResults;
|
use invalidation::media_queries::EffectiveMediaQueryResults;
|
||||||
|
use matching::CascadeVisitedMode;
|
||||||
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};
|
||||||
|
@ -27,7 +28,7 @@ use selector_parser::{SelectorImpl, PseudoElement};
|
||||||
use selectors::attr::NamespaceConstraint;
|
use selectors::attr::NamespaceConstraint;
|
||||||
use selectors::bloom::BloomFilter;
|
use selectors::bloom::BloomFilter;
|
||||||
use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContext, MatchingMode};
|
use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContext, MatchingMode};
|
||||||
use selectors::matching::AFFECTED_BY_PRESENTATIONAL_HINTS;
|
use selectors::matching::{VisitedHandlingMode, AFFECTED_BY_PRESENTATIONAL_HINTS};
|
||||||
use selectors::parser::{AncestorHashes, Combinator, Component, Selector, SelectorAndHashes};
|
use selectors::parser::{AncestorHashes, Combinator, Component, Selector, SelectorAndHashes};
|
||||||
use selectors::parser::{SelectorIter, SelectorMethods};
|
use selectors::parser::{SelectorIter, SelectorMethods};
|
||||||
use selectors::sink::Push;
|
use selectors::sink::Push;
|
||||||
|
@ -679,47 +680,90 @@ impl Stylist {
|
||||||
element: &E,
|
element: &E,
|
||||||
pseudo: &PseudoElement,
|
pseudo: &PseudoElement,
|
||||||
rule_inclusion: RuleInclusion,
|
rule_inclusion: RuleInclusion,
|
||||||
parent_style: &ComputedValues,
|
parent_style: &Arc<ComputedValues>,
|
||||||
|
is_probe: bool,
|
||||||
font_metrics: &FontMetricsProvider)
|
font_metrics: &FontMetricsProvider)
|
||||||
-> Option<Arc<ComputedValues>>
|
-> Option<Arc<ComputedValues>>
|
||||||
where E: TElement,
|
where E: TElement,
|
||||||
{
|
{
|
||||||
let rule_node =
|
let cascade_inputs =
|
||||||
self.lazy_pseudo_rules(guards, element, pseudo, rule_inclusion);
|
self.lazy_pseudo_rules(guards, element, pseudo, is_probe, rule_inclusion);
|
||||||
self.compute_pseudo_element_style_with_rulenode(rule_node.as_ref(),
|
self.compute_pseudo_element_style_with_inputs(&cascade_inputs,
|
||||||
guards,
|
guards,
|
||||||
parent_style,
|
parent_style,
|
||||||
font_metrics)
|
font_metrics)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes a pseudo-element style lazily using the given rulenode. This
|
/// Computes a pseudo-element style lazily using the given CascadeInputs.
|
||||||
/// can be used for truly lazy pseudo-elements or to avoid redoing selector
|
/// This can be used for truly lazy pseudo-elements or to avoid redoing
|
||||||
/// matching for eager pseudo-elements when we need to recompute their style
|
/// selector matching for eager pseudo-elements when we need to recompute
|
||||||
/// with a new parent style.
|
/// their style with a new parent style.
|
||||||
pub fn compute_pseudo_element_style_with_rulenode(&self,
|
pub fn compute_pseudo_element_style_with_inputs(&self,
|
||||||
rule_node: Option<&StrongRuleNode>,
|
inputs: &CascadeInputs,
|
||||||
guards: &StylesheetGuards,
|
guards: &StylesheetGuards,
|
||||||
parent_style: &ComputedValues,
|
parent_style: &Arc<ComputedValues>,
|
||||||
font_metrics: &FontMetricsProvider)
|
font_metrics: &FontMetricsProvider)
|
||||||
-> Option<Arc<ComputedValues>>
|
-> Option<Arc<ComputedValues>>
|
||||||
{
|
{
|
||||||
let rule_node = match rule_node {
|
// We may have only visited rules in cases when we are actually
|
||||||
Some(rule_node) => rule_node,
|
// resolving, not probing, pseudo-element style.
|
||||||
None => return None
|
if !inputs.has_rules() && !inputs.has_visited_rules() {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to compute visited values if we have visited rules or if our
|
||||||
|
// parent has visited values.
|
||||||
|
let visited_values = if inputs.has_visited_rules() || parent_style.get_visited_style().is_some() {
|
||||||
|
// Slightly annoying: we know that inputs has either rules or
|
||||||
|
// visited rules, but we can't do inputs.rules() up front because
|
||||||
|
// maybe it just has visited rules, so can't unwrap_or.
|
||||||
|
let rule_node = match inputs.get_visited_rules() {
|
||||||
|
Some(rules) => rules,
|
||||||
|
None => inputs.rules()
|
||||||
|
};
|
||||||
|
// We want to use the visited bits (if any) from our parent style as
|
||||||
|
// our parent.
|
||||||
|
let mode = CascadeVisitedMode::Visited;
|
||||||
|
let inherited_style = mode.values(parent_style);
|
||||||
|
let computed =
|
||||||
|
properties::cascade(&self.device,
|
||||||
|
rule_node,
|
||||||
|
guards,
|
||||||
|
Some(inherited_style),
|
||||||
|
Some(inherited_style),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
&create_error_reporter(),
|
||||||
|
font_metrics,
|
||||||
|
CascadeFlags::empty(),
|
||||||
|
self.quirks_mode);
|
||||||
|
|
||||||
|
Some(Arc::new(computed))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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 root;
|
||||||
|
let rules = if let Some(rules) = inputs.get_rules() {
|
||||||
|
rules
|
||||||
|
} else {
|
||||||
|
root = self.rule_tree.root();
|
||||||
|
&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").
|
||||||
// Bug 1364242: We need to add visited support for lazy pseudos
|
|
||||||
let computed =
|
let computed =
|
||||||
properties::cascade(&self.device,
|
properties::cascade(&self.device,
|
||||||
rule_node,
|
rules,
|
||||||
guards,
|
guards,
|
||||||
Some(parent_style),
|
Some(parent_style),
|
||||||
Some(parent_style),
|
Some(parent_style),
|
||||||
None,
|
visited_values,
|
||||||
None,
|
None,
|
||||||
&create_error_reporter(),
|
&create_error_reporter(),
|
||||||
font_metrics,
|
font_metrics,
|
||||||
|
@ -729,7 +773,7 @@ impl Stylist {
|
||||||
Some(Arc::new(computed))
|
Some(Arc::new(computed))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the rule node for a lazily-cascaded pseudo-element.
|
/// Computes the cascade inputs for a lazily-cascaded pseudo-element.
|
||||||
///
|
///
|
||||||
/// See the documentation on lazy pseudo-elements in
|
/// See the documentation on lazy pseudo-elements in
|
||||||
/// docs/components/style.md
|
/// docs/components/style.md
|
||||||
|
@ -737,14 +781,15 @@ impl Stylist {
|
||||||
guards: &StylesheetGuards,
|
guards: &StylesheetGuards,
|
||||||
element: &E,
|
element: &E,
|
||||||
pseudo: &PseudoElement,
|
pseudo: &PseudoElement,
|
||||||
|
is_probe: bool,
|
||||||
rule_inclusion: RuleInclusion)
|
rule_inclusion: RuleInclusion)
|
||||||
-> Option<StrongRuleNode>
|
-> CascadeInputs
|
||||||
where E: TElement
|
where E: TElement
|
||||||
{
|
{
|
||||||
let pseudo = pseudo.canonical();
|
let pseudo = pseudo.canonical();
|
||||||
debug_assert!(pseudo.is_lazy());
|
debug_assert!(pseudo.is_lazy());
|
||||||
if self.pseudos_map.get(&pseudo).is_none() {
|
if self.pseudos_map.get(&pseudo).is_none() {
|
||||||
return None
|
return CascadeInputs::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the selector flags. We should be in sequential mode
|
// Apply the selector flags. We should be in sequential mode
|
||||||
|
@ -777,7 +822,7 @@ impl Stylist {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Bug 1364242: We need to add visited support for lazy pseudos
|
let mut inputs = CascadeInputs::default();
|
||||||
let mut declarations = ApplicableDeclarationList::new();
|
let mut declarations = ApplicableDeclarationList::new();
|
||||||
let mut matching_context =
|
let mut matching_context =
|
||||||
MatchingContext::new(MatchingMode::ForStatelessPseudoElement,
|
MatchingContext::new(MatchingMode::ForStatelessPseudoElement,
|
||||||
|
@ -792,19 +837,52 @@ impl Stylist {
|
||||||
&mut declarations,
|
&mut declarations,
|
||||||
&mut matching_context,
|
&mut matching_context,
|
||||||
&mut set_selector_flags);
|
&mut set_selector_flags);
|
||||||
if declarations.is_empty() {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
|
|
||||||
let rule_node =
|
if !declarations.is_empty() {
|
||||||
self.rule_tree.insert_ordered_rules_with_important(
|
let rule_node = self.rule_tree.insert_ordered_rules_with_important(
|
||||||
declarations.into_iter().map(|a| a.order_and_level()),
|
declarations.into_iter().map(|a| a.order_and_level()),
|
||||||
guards);
|
guards);
|
||||||
if rule_node == self.rule_tree.root() {
|
if rule_node != self.rule_tree.root() {
|
||||||
None
|
inputs.set_rules(VisitedHandlingMode::AllLinksUnvisited,
|
||||||
} else {
|
rule_node);
|
||||||
Some(rule_node)
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_probe && !inputs.has_rules() {
|
||||||
|
// When probing, don't compute visited styles if we have no
|
||||||
|
// unvisited styles.
|
||||||
|
return inputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if matching_context.relevant_link_found {
|
||||||
|
let mut declarations = ApplicableDeclarationList::new();
|
||||||
|
let mut matching_context =
|
||||||
|
MatchingContext::new_for_visited(MatchingMode::ForStatelessPseudoElement,
|
||||||
|
None,
|
||||||
|
VisitedHandlingMode::RelevantLinkVisited,
|
||||||
|
self.quirks_mode);
|
||||||
|
self.push_applicable_declarations(element,
|
||||||
|
Some(&pseudo),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
AnimationRules(None, None),
|
||||||
|
rule_inclusion,
|
||||||
|
&mut declarations,
|
||||||
|
&mut matching_context,
|
||||||
|
&mut set_selector_flags);
|
||||||
|
if !declarations.is_empty() {
|
||||||
|
let rule_node =
|
||||||
|
self.rule_tree.insert_ordered_rules_with_important(
|
||||||
|
declarations.into_iter().map(|a| a.order_and_level()),
|
||||||
|
guards);
|
||||||
|
if rule_node != self.rule_tree.root() {
|
||||||
|
inputs.set_rules(VisitedHandlingMode::RelevantLinkVisited,
|
||||||
|
rule_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
|
|
|
@ -10,7 +10,7 @@ use selectors::Element;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use style::context::{QuirksMode, SharedStyleContext, StyleContext};
|
use style::context::{CascadeInputs, QuirksMode, SharedStyleContext, StyleContext};
|
||||||
use style::context::ThreadLocalStyleContext;
|
use style::context::ThreadLocalStyleContext;
|
||||||
use style::data::{ElementData, ElementStyles, RestyleData};
|
use style::data::{ElementData, ElementStyles, RestyleData};
|
||||||
use style::dom::{AnimationOnlyDirtyDescendants, DirtyDescendants};
|
use style::dom::{AnimationOnlyDirtyDescendants, DirtyDescendants};
|
||||||
|
@ -1479,7 +1479,7 @@ pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
|
||||||
&pseudo,
|
&pseudo,
|
||||||
RuleInclusion::All,
|
RuleInclusion::All,
|
||||||
&data.styles,
|
&data.styles,
|
||||||
ComputedValues::arc_from_borrowed(&inherited_style).map(|v| v.as_ref()),
|
ComputedValues::arc_from_borrowed(&inherited_style),
|
||||||
&*doc_data,
|
&*doc_data,
|
||||||
is_probe
|
is_probe
|
||||||
);
|
);
|
||||||
|
@ -1533,7 +1533,7 @@ fn get_pseudo_style(
|
||||||
pseudo: &PseudoElement,
|
pseudo: &PseudoElement,
|
||||||
rule_inclusion: RuleInclusion,
|
rule_inclusion: RuleInclusion,
|
||||||
styles: &ElementStyles,
|
styles: &ElementStyles,
|
||||||
inherited_styles: Option<&ComputedValues>,
|
inherited_styles: Option<&Arc<ComputedValues>>,
|
||||||
doc_data: &PerDocumentStyleDataImpl,
|
doc_data: &PerDocumentStyleDataImpl,
|
||||||
is_probe: bool,
|
is_probe: bool,
|
||||||
) -> Option<Arc<ComputedValues>> {
|
) -> Option<Arc<ComputedValues>> {
|
||||||
|
@ -1550,20 +1550,20 @@ fn get_pseudo_style(
|
||||||
inherited_styles.unwrap_or(styles.primary());
|
inherited_styles.unwrap_or(styles.primary());
|
||||||
let guards = StylesheetGuards::same(guard);
|
let guards = StylesheetGuards::same(guard);
|
||||||
let metrics = get_metrics_provider_for_product();
|
let metrics = get_metrics_provider_for_product();
|
||||||
let rule_node = match styles.pseudos.get(&pseudo) {
|
let inputs = match styles.pseudos.get(&pseudo) {
|
||||||
Some(styles) => styles.rules.as_ref(),
|
Some(styles) => CascadeInputs::new_from_style(styles),
|
||||||
None => None,
|
None => return None,
|
||||||
};
|
};
|
||||||
doc_data.stylist
|
doc_data.stylist
|
||||||
.compute_pseudo_element_style_with_rulenode(
|
.compute_pseudo_element_style_with_inputs(
|
||||||
rule_node,
|
&inputs,
|
||||||
&guards,
|
&guards,
|
||||||
inherited_styles,
|
inherited_styles,
|
||||||
&metrics)
|
&metrics)
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
debug_assert!(inherited_styles.is_none() ||
|
debug_assert!(inherited_styles.is_none() ||
|
||||||
ptr::eq(inherited_styles.unwrap(),
|
ptr::eq(&**inherited_styles.unwrap(),
|
||||||
&**styles.primary()));
|
&**styles.primary()));
|
||||||
styles.pseudos.get(&pseudo).cloned()
|
styles.pseudos.get(&pseudo).cloned()
|
||||||
},
|
},
|
||||||
|
@ -1572,7 +1572,7 @@ fn get_pseudo_style(
|
||||||
PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"),
|
PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"),
|
||||||
PseudoElementCascadeType::Lazy => {
|
PseudoElementCascadeType::Lazy => {
|
||||||
debug_assert!(inherited_styles.is_none() ||
|
debug_assert!(inherited_styles.is_none() ||
|
||||||
ptr::eq(inherited_styles.unwrap(),
|
ptr::eq(&**inherited_styles.unwrap(),
|
||||||
&**styles.primary()));
|
&**styles.primary()));
|
||||||
let base = if pseudo.inherits_from_default_values() {
|
let base = if pseudo.inherits_from_default_values() {
|
||||||
doc_data.default_computed_values()
|
doc_data.default_computed_values()
|
||||||
|
@ -1588,6 +1588,7 @@ fn get_pseudo_style(
|
||||||
&pseudo,
|
&pseudo,
|
||||||
rule_inclusion,
|
rule_inclusion,
|
||||||
base,
|
base,
|
||||||
|
is_probe,
|
||||||
&metrics)
|
&metrics)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
Загрузка…
Ссылка в новой задаче