diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index 0b230e6081f..e915a2d07b1 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -51,7 +51,7 @@ use script_layout_interface::{OpaqueStyleAndLayoutData, StyleData}; use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, GetLayoutData, LayoutNode}; use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode}; use selectors::attr::{AttrSelectorOperation, NamespaceConstraint}; -use selectors::matching::{ElementSelectorFlags, MatchingContext, RelevantLinkStatus}; +use selectors::matching::{ElementSelectorFlags, MatchingContext, RelevantLinkStatus, VisitedHandlingMode}; use servo_atoms::Atom; use servo_url::ServoUrl; use std::fmt; @@ -364,7 +364,9 @@ impl<'le> fmt::Debug for ServoLayoutElement<'le> { } impl<'le> PresentationalHintsSynthesizer for ServoLayoutElement<'le> { - fn synthesize_presentational_hints_for_legacy_attributes(&self, hints: &mut V) + fn synthesize_presentational_hints_for_legacy_attributes(&self, + _visited_handling: VisitedHandlingMode, + hints: &mut V) where V: Push { unsafe { @@ -1228,6 +1230,8 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { } impl<'le> PresentationalHintsSynthesizer for ServoThreadSafeLayoutElement<'le> { - fn synthesize_presentational_hints_for_legacy_attributes(&self, _hints: &mut V) + fn synthesize_presentational_hints_for_legacy_attributes(&self, + _visited_handling: VisitedHandlingMode, + _hints: &mut V) where V: Push {} } diff --git a/components/style/dom.rs b/components/style/dom.rs index e2673593997..57011643301 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -18,7 +18,7 @@ use properties::{ComputedValues, PropertyDeclarationBlock}; #[cfg(feature = "gecko")] use properties::animated_properties::TransitionProperty; use rule_tree::CascadeLevel; use selector_parser::{ElementExt, PreExistingComputedValues, PseudoElement}; -use selectors::matching::ElementSelectorFlags; +use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode}; use shared_lock::Locked; use sink::Push; use std::fmt; @@ -270,7 +270,9 @@ pub unsafe fn raw_note_descendants(element: E) -> bool pub trait PresentationalHintsSynthesizer { /// Generate the proper applicable declarations due to presentational hints, /// and insert them into `hints`. - fn synthesize_presentational_hints_for_legacy_attributes(&self, hints: &mut V) + fn synthesize_presentational_hints_for_legacy_attributes(&self, + visited_handling: VisitedHandlingMode, + hints: &mut V) where V: Push; } diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 80d77abd694..d808f148d85 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -35,12 +35,15 @@ use gecko_bindings::bindings::Gecko_ClassOrClassList; use gecko_bindings::bindings::Gecko_ElementHasAnimations; use gecko_bindings::bindings::Gecko_ElementHasCSSAnimations; use gecko_bindings::bindings::Gecko_ElementHasCSSTransitions; +use gecko_bindings::bindings::Gecko_GetActiveLinkAttrDeclarationBlock; use gecko_bindings::bindings::Gecko_GetAnimationRule; use gecko_bindings::bindings::Gecko_GetExtraContentStyleDeclarations; use gecko_bindings::bindings::Gecko_GetHTMLPresentationAttrDeclarationBlock; use gecko_bindings::bindings::Gecko_GetSMILOverrideDeclarationBlock; use gecko_bindings::bindings::Gecko_GetStyleAttrDeclarationBlock; use gecko_bindings::bindings::Gecko_GetStyleContext; +use gecko_bindings::bindings::Gecko_GetUnvisitedLinkAttrDeclarationBlock; +use gecko_bindings::bindings::Gecko_GetVisitedLinkAttrDeclarationBlock; use gecko_bindings::bindings::Gecko_IsSignificantChild; use gecko_bindings::bindings::Gecko_MatchStringArgPseudo; use gecko_bindings::bindings::Gecko_UnsetDirtyStyleAttr; @@ -66,7 +69,8 @@ use rule_tree::CascadeLevel as ServoCascadeLevel; use selector_parser::ElementExt; use selectors::Element; use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint}; -use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, RelevantLinkStatus}; +use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode}; +use selectors::matching::{RelevantLinkStatus, VisitedHandlingMode}; use shared_lock::Locked; use sink::Push; use std::cell::RefCell; @@ -1036,7 +1040,9 @@ impl<'le> Hash for GeckoElement<'le> { } impl<'le> PresentationalHintsSynthesizer for GeckoElement<'le> { - fn synthesize_presentational_hints_for_legacy_attributes(&self, hints: &mut V) + fn synthesize_presentational_hints_for_legacy_attributes(&self, + visited_handling: VisitedHandlingMode, + hints: &mut V) where V: Push, { use properties::longhands::_x_lang::SpecifiedValue as SpecifiedLang; @@ -1098,6 +1104,37 @@ impl<'le> PresentationalHintsSynthesizer for GeckoElement<'le> { ); } + // Support for link, vlink, and alink presentation hints on + if self.is_link() { + // Unvisited vs. visited styles are computed up-front based on the + // visited mode (not the element's actual state). + let declarations = match visited_handling { + VisitedHandlingMode::AllLinksUnvisited => unsafe { + Gecko_GetUnvisitedLinkAttrDeclarationBlock(self.0) + }, + VisitedHandlingMode::RelevantLinkVisited => unsafe { + Gecko_GetVisitedLinkAttrDeclarationBlock(self.0) + }, + }; + let declarations = declarations.and_then(|s| s.as_arc_opt()); + if let Some(decl) = declarations { + hints.push( + ApplicableDeclarationBlock::from_declarations(Clone::clone(decl), ServoCascadeLevel::PresHints) + ); + } + + let active = self.get_state().intersects(NonTSPseudoClass::Active.state_flag()); + if active { + let declarations = unsafe { Gecko_GetActiveLinkAttrDeclarationBlock(self.0) }; + let declarations = declarations.and_then(|s| s.as_arc_opt()); + if let Some(decl) = declarations { + hints.push( + ApplicableDeclarationBlock::from_declarations(Clone::clone(decl), ServoCascadeLevel::PresHints) + ); + } + } + } + // xml:lang has precedence over lang, which can be // set by Gecko_GetHTMLPresentationAttrDeclarationBlock // diff --git a/components/style/sharing/mod.rs b/components/style/sharing/mod.rs index 4140d44a187..7da2fac3d5e 100644 --- a/components/style/sharing/mod.rs +++ b/components/style/sharing/mod.rs @@ -14,7 +14,7 @@ use dom::{TElement, SendElement}; use matching::{ChildCascadeRequirement, MatchMethods}; use properties::ComputedValues; use selectors::bloom::BloomFilter; -use selectors::matching::{ElementSelectorFlags, StyleRelations}; +use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode, StyleRelations}; use smallvec::SmallVec; use std::mem; use std::ops::Deref; @@ -66,7 +66,10 @@ impl ValidationData { { if self.pres_hints.is_none() { let mut pres_hints = SmallVec::new(); - element.synthesize_presentational_hints_for_legacy_attributes(&mut pres_hints); + element.synthesize_presentational_hints_for_legacy_attributes( + VisitedHandlingMode::AllLinksUnvisited, + &mut pres_hints + ); self.pres_hints = Some(pres_hints); } &*self.pres_hints.as_ref().unwrap() diff --git a/components/style/stylist.rs b/components/style/stylist.rs index e14ed0e27c5..eb2d43cf16c 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -959,7 +959,10 @@ impl Stylist { if pseudo_element.is_none() && !only_default_rules { // Step 2: Presentational hints. let length_before_preshints = applicable_declarations.len(); - element.synthesize_presentational_hints_for_legacy_attributes(applicable_declarations); + element.synthesize_presentational_hints_for_legacy_attributes( + context.visited_handling, + applicable_declarations + ); if applicable_declarations.len() != length_before_preshints { if cfg!(debug_assertions) { for declaration in &applicable_declarations[length_before_preshints..] {