diff --git a/components/style/dom.rs b/components/style/dom.rs index 12eff9c7c12..a7f68a96541 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -550,6 +550,11 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone + unsafe fn unset_animation_only_dirty_descendants(&self) { } + /// Returns true if this element is a visited link. + /// + /// Servo doesn't support visited styles yet. + fn is_visited_link(&self) -> bool { false } + /// Returns true if this element is native anonymous (only Gecko has native /// anonymous content). fn is_native_anonymous(&self) -> bool { false } diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 59f128086ed..c8028acaee2 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -1011,6 +1011,11 @@ impl<'le> TElement for GeckoElement<'le> { self.unset_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32) } + fn is_visited_link(&self) -> bool { + use element_state::IN_VISITED_STATE; + self.get_state().intersects(IN_VISITED_STATE) + } + fn is_native_anonymous(&self) -> bool { self.flags() & (NODE_IS_NATIVE_ANONYMOUS as u32) != 0 } diff --git a/components/style/properties/computed_value_flags.rs b/components/style/properties/computed_value_flags.rs index 2dc3d91e1f6..3363fd76869 100644 --- a/components/style/properties/computed_value_flags.rs +++ b/components/style/properties/computed_value_flags.rs @@ -33,5 +33,9 @@ bitflags! { /// /// This is used from Gecko's layout engine. const IS_TEXT_COMBINED = 1 << 2, + + /// A flag used to mark styles under a relevant link that is also + /// visited. + const IS_RELEVANT_LINK_VISITED = 1 << 3, } } diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 6e1bf5e7477..33d59ad4105 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -2650,6 +2650,11 @@ impl<'a> StyleBuilder<'a> { ) } + /// Returns whether we have a visited style. + pub fn has_visited_style(&self) -> bool { + self.visited_style.is_some() + } + /// Returns the style we're inheriting from. pub fn inherited_style(&self) -> &'a ComputedValues { self.inherited_style @@ -2805,14 +2810,14 @@ bitflags! { pub flags CascadeFlags: u8 { /// Whether to inherit all styles from the parent. If this flag is not /// present, non-inherited styles are reset to their initial values. - const INHERIT_ALL = 0x01, + 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 = 0x02, + const SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP = 1 << 1, /// Whether to only cascade properties that are visited dependent. - const VISITED_DEPENDENT_ONLY = 0x04, + const VISITED_DEPENDENT_ONLY = 1 << 2, /// Whether the given element we're styling is the document element, /// that is, matches :root. @@ -2822,15 +2827,19 @@ bitflags! { /// /// 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 = 0x08, + const IS_ROOT_ELEMENT = 1 << 3, /// Whether to convert display:contents into display:inline. This /// is used by Gecko to prevent display:contents on generated /// content. - const PROHIBIT_DISPLAY_CONTENTS = 0x10, + const PROHIBIT_DISPLAY_CONTENTS = 1 << 4, /// Whether we're styling the ::-moz-fieldset-content anonymous box. - const IS_FIELDSET_CONTENT = 0x20, + const IS_FIELDSET_CONTENT = 1 << 5, + + /// Whether we're computing the style of a link element that happens to + /// be visited. + const IS_VISITED_LINK = 1 << 6, } } diff --git a/components/style/style_adjuster.rs b/components/style/style_adjuster.rs index b7eeb560217..5ddcd42a095 100644 --- a/components/style/style_adjuster.rs +++ b/components/style/style_adjuster.rs @@ -426,14 +426,39 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { } } + /// Computes the RELEVANT_LINK_VISITED flag based on the parent style and on + /// whether we're a relevant link. + /// + /// NOTE(emilio): We don't do this for text styles, which is... dubious, but + /// Gecko doesn't seem to do it either. It's extremely easy to do if needed + /// though. + /// + /// FIXME(emilio): This isn't technically a style adjustment thingie, could + /// it move somewhere else? + fn adjust_for_visited(&mut self, flags: CascadeFlags) { + use properties::IS_VISITED_LINK; + use properties::computed_value_flags::IS_RELEVANT_LINK_VISITED; + + if !self.style.has_visited_style() { + return; + } + + if flags.contains(IS_VISITED_LINK) || + self.style.inherited_style().flags.contains(IS_RELEVANT_LINK_VISITED) { + self.style.flags.insert(IS_RELEVANT_LINK_VISITED); + } + } + /// Adjusts the style to account for various fixups that don't fit naturally /// into the cascade. /// /// When comparing to Gecko, this is similar to the work done by - /// `nsStyleContext::ApplyStyleFixups`. + /// `nsStyleContext::ApplyStyleFixups`, plus some parts of + /// `nsStyleSet::GetContext`. pub fn adjust(&mut self, layout_parent_style: &ComputedValues, flags: CascadeFlags) { + self.adjust_for_visited(flags); #[cfg(feature = "gecko")] { self.adjust_for_prohibited_display_contents(flags); diff --git a/components/style/style_resolver.rs b/components/style/style_resolver.rs index 71598ab14d2..dcb7134cf50 100644 --- a/components/style/style_resolver.rs +++ b/components/style/style_resolver.rs @@ -12,7 +12,7 @@ use dom::TElement; use log::LogLevel::Trace; use matching::{CascadeVisitedMode, MatchMethods}; use properties::{AnimationRules, CascadeFlags, ComputedValues}; -use properties::{IS_ROOT_ELEMENT, PROHIBIT_DISPLAY_CONTENTS, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP}; +use properties::{IS_ROOT_ELEMENT, IS_VISITED_LINK, PROHIBIT_DISPLAY_CONTENTS, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP}; use properties::{VISITED_DEPENDENT_ONLY, cascade}; use rule_tree::StrongRuleNode; use selector_parser::{PseudoElement, SelectorImpl}; @@ -473,6 +473,11 @@ where if self.element.skip_root_and_item_based_display_fixup() { cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP); } + + if pseudo.is_none() && self.element.is_visited_link() { + cascade_flags.insert(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 diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index c9c104bdd2c..e24e4ff8153 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -1736,6 +1736,9 @@ pub extern "C" fn Servo_ComputedValues_GetStyleBits(values: ServoStyleContextBor use style::properties::computed_value_flags::*; let flags = values.flags; let mut result = 0; + if flags.contains(IS_RELEVANT_LINK_VISITED) { + result |= structs::NS_STYLE_RELEVANT_LINK_VISITED as u64; + } if flags.contains(HAS_TEXT_DECORATION_LINES) { result |= structs::NS_STYLE_HAS_TEXT_DECORATION_LINES as u64; }