--- a/servo/components/style/animation.rs
+++ b/servo/components/style/animation.rs
@@ -499,17 +499,16 @@ where
context.stylist.device(),
/* pseudo = */ None,
previous_style.rules(),
&context.guards,
iter,
Some(previous_style),
Some(previous_style),
Some(previous_style),
- /* visited_style = */ None,
font_metrics_provider,
CascadeFlags::empty(),
context.quirks_mode(),
/* rule_cache = */ None,
&mut Default::default(),
/* element = */ None,
);
computed.shareable()
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -8,16 +8,17 @@
from data import to_camel_case, to_camel_case_lower
from data import Keyword
%>
<%namespace name="helpers" file="/helpers.mako.rs" />
use Atom;
use app_units::Au;
use custom_properties::CustomPropertiesMap;
+use dom::TElement;
use gecko_bindings::bindings;
% for style_struct in data.style_structs:
use gecko_bindings::structs::${style_struct.gecko_ffi_name};
use gecko_bindings::bindings::Gecko_Construct_Default_${style_struct.gecko_ffi_name};
use gecko_bindings::bindings::Gecko_CopyConstruct_${style_struct.gecko_ffi_name};
use gecko_bindings::bindings::Gecko_Destroy_${style_struct.gecko_ffi_name};
% endfor
use gecko_bindings::bindings::Gecko_CopyCounterStyle;
@@ -45,16 +46,17 @@ use gecko_bindings::sugar::refptr::RefPt
use gecko::values::convert_nscolor_to_rgba;
use gecko::values::convert_rgba_to_nscolor;
use gecko::values::GeckoStyleCoordConvertible;
use gecko::values::round_border_to_device_pixels;
use logical_geometry::WritingMode;
use media_queries::Device;
use properties::computed_value_flags::*;
use properties::longhands;
+use properties::StyleBuilder;
use rule_tree::StrongRuleNode;
use selector_parser::PseudoElement;
use servo_arc::{Arc, RawOffsetArc, UniqueArc};
use std::marker::PhantomData;
use std::mem::{forget, uninitialized, transmute, zeroed};
use std::{cmp, ops, ptr};
use values::{self, CustomIdent, Either, KeyframesName, None_};
use values::computed::{NonNegativeLength, Percentage, TransitionProperty};
@@ -84,44 +86,41 @@ impl ComputedValues {
pub fn new(
device: &Device,
parent: Option<<&ComputedValues>,
pseudo: Option<<&PseudoElement>,
custom_properties: Option<Arc<CustomPropertiesMap>>,
writing_mode: WritingMode,
flags: ComputedValueFlags,
rules: Option<StrongRuleNode>,
- visited_style: Option<Arc<ComputedValues>>,
% for style_struct in data.style_structs:
${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
% endfor
) -> UniqueArc<Self> {
ComputedValuesInner::new(
custom_properties,
writing_mode,
flags,
rules,
- visited_style,
% for style_struct in data.style_structs:
${style_struct.ident},
% endfor
).to_outer(
device.pres_context(),
parent,
pseudo.map(|p| p.pseudo_info())
)
}
pub fn default_values(pres_context: RawGeckoPresContextBorrowed) -> Arc<Self> {
ComputedValuesInner::new(
/* custom_properties = */ None,
/* writing_mode = */ WritingMode::empty(), // FIXME(bz): This seems dubious
ComputedValueFlags::empty(),
/* rules = */ None,
- /* visited_style = */ None,
% for style_struct in data.style_structs:
style_structs::${style_struct.name}::default(pres_context),
% endfor
).to_outer(pres_context, None, None).shareable()
}
pub fn pseudo(&self) -> Option<PseudoElement> {
let atom = (self.0).mPseudoTag.mRawPtr;
@@ -155,16 +154,82 @@ impl ComputedValues {
old_values.map_or(false, |old| {
let old_display_style = old.get_box().clone_display();
let new_display_style = self.get_box().clone_display();
old_display_style == Display::None &&
new_display_style != Display::None
})
}
+ fn has_visited_bit(&self) -> bool {
+ self.0.mBits & structs::ComputedStyleBit_RelevantLinkVisited != 0
+ }
+
+ fn set_visited_style_internal(
+ &mut self,
+ visited_style: Arc<ComputedValues>,
+ visited_bit: bool,
+ ) {
+ const VISITED: structs::ComputedStyleBit =
+ structs::ComputedStyleBit_RelevantLinkVisited;
+ debug_assert!(self.0.mBits & VISITED == 0,
+ "RelevantLinkVisited bit should not have been set");
+ let visited_style = Arc::into_raw_offset(visited_style);
+ self.0.mSource.visited_style = Some(visited_style);
+ if visited_bit {
+ self.0.mBits |= VISITED;
+ }
+ }
+
+ /// Setup visited style of this ComputedValues.
+ pub fn set_visited_style<E>(
+ &mut self,
+ parent_style: Option< &ComputedValues>,
+ visited_style: Arc<ComputedValues>,
+ element: Option<E>,
+ )
+ where
+ E: TElement
+ {
+ let mut visited_bit =
+ parent_style.map_or(false, |style| style.has_visited_bit());
+ if let Some(e) = element {
+ if e.is_link() && self.pseudo().is_none() {
+ visited_bit = e.is_visited_link();
+ }
+ }
+ self.set_visited_style_internal(visited_style, visited_bit);
+ }
+
+ /// Handle inherited visited style of this ComputedValues.
+ ///
+ /// This should only be called for pseudo-elements.
+ pub fn inherit_visited(
+ &mut self,
+ device: &Device,
+ parent: &ComputedValues,
+ ) {
+ debug_assert!(self.pseudo().is_some(),
+ "inherit_visited should only be called for pseudos");
+ let parent_visited_style = match parent.visited_style() {
+ Some(style) => style,
+ None => return,
+ };
+ // Rebuild the visited style from the parent, ensuring that it will also
+ // not have rules. This matches this unvisited style. This assumes
+ // that the caller doesn't need to adjust or process visited style, so
+ // we can just build visited style here for simplicity.
+ let visited_style = StyleBuilder::for_inheritance(
+ device,
+ Some(parent_visited_style),
+ self.pseudo().as_ref(),
+ ).build().shareable();
+ let visited_bit = parent.has_visited_bit();
+ self.set_visited_style_internal(visited_style, visited_bit);
+ }
}
impl Drop for ComputedValues {
fn drop(&mut self) {
unsafe {
bindings::Gecko_ComputedStyle_Destroy(&mut self.0);
}
}
@@ -197,26 +262,25 @@ impl Clone for ComputedValuesInner {
type PseudoInfo = (*mut structs::nsAtom, structs::CSSPseudoElementType);
type ParentComputedStyleInfo<'a> = Option< &'a ComputedValues>;
impl ComputedValuesInner {
pub fn new(custom_properties: Option<Arc<CustomPropertiesMap>>,
writing_mode: WritingMode,
flags: ComputedValueFlags,
rules: Option<StrongRuleNode>,
- visited_style: Option<Arc<ComputedValues>>,
% for style_struct in data.style_structs:
${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
% endfor
) -> Self {
ComputedValuesInner {
custom_properties: custom_properties,
writing_mode: writing_mode,
rules: rules,
- visited_style: visited_style.map(|x| Arc::into_raw_offset(x)),
+ visited_style: None,
flags: flags,
% for style_struct in data.style_structs:
${style_struct.gecko_name}: Arc::into_raw_offset(${style_struct.ident}),
% endfor
}
}
fn to_outer(
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -2722,17 +2722,16 @@ impl ComputedValues {
pub fn new(
_: &Device,
_: Option<<&ComputedValues>,
_: Option<<&PseudoElement>,
custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
writing_mode: WritingMode,
flags: ComputedValueFlags,
rules: Option<StrongRuleNode>,
- visited_style: Option<Arc<ComputedValues>>,
% for style_struct in data.active_style_structs():
${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
% endfor
) -> UniqueArc<Self> {
UniqueArc::new(Self {
inner: ComputedValuesInner {
custom_properties,
writing_mode,
@@ -2763,16 +2762,28 @@ impl ComputedValues {
PropertyDeclarationId::Custom(name) => {
self.custom_properties
.as_ref()
.and_then(|map| map.get(name))
.map_or(String::new(), |value| value.to_css_string())
}
}
}
+
+ /// Setup visited style of this ComputedValues.
+ pub fn set_visited_style<E>(
+ &mut self,
+ visited_style: Arc<ComputedValues>,
+ _element: Option<E>
+ )
+ where
+ E: TElement,
+ {
+ self.visited_style = Some(visited_style);
+ }
}
#[cfg(feature = "servo")]
impl ops::Deref for ComputedValues {
type Target = ComputedValuesInner;
fn deref(&self) -> &ComputedValuesInner {
&self.inner
}
@@ -3148,36 +3159,32 @@ pub struct StyleBuilder<'a> {
modified_reset: bool,
/// The writing mode flags.
///
/// TODO(emilio): Make private.
pub writing_mode: WritingMode,
/// Flags for the computed value.
pub flags: ComputedValueFlags,
- /// The element's style if visited, only computed if there's a relevant link
- /// for this element. A element's "relevant link" is the element being
- /// matched if it is a link or the nearest ancestor link.
- visited_style: Option<Arc<ComputedValues>>,
+
% for style_struct in data.active_style_structs():
${style_struct.ident}: StyleStructRef<'a, style_structs::${style_struct.name}>,
% endfor
}
impl<'a> StyleBuilder<'a> {
/// Trivially construct a `StyleBuilder`.
fn new(
device: &'a Device,
parent_style: Option<<&'a ComputedValues>,
parent_style_ignoring_first_line: Option<<&'a ComputedValues>,
pseudo: Option<<&'a PseudoElement>,
cascade_flags: CascadeFlags,
rules: Option<StrongRuleNode>,
custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
- visited_style: Option<Arc<ComputedValues>>,
) -> Self {
debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
#[cfg(feature = "gecko")]
debug_assert!(parent_style.is_none() ||
::std::ptr::eq(parent_style.unwrap(),
parent_style_ignoring_first_line.unwrap()) ||
parent_style.unwrap().pseudo() == Some(PseudoElement::FirstLine));
let reset_style = device.default_computed_values();
@@ -3205,17 +3212,16 @@ impl<'a> StyleBuilder<'a> {
inherited_style_ignoring_first_line,
reset_style,
pseudo,
rules,
modified_reset: false,
custom_properties,
writing_mode: inherited_style.writing_mode,
flags,
- visited_style,
% for style_struct in data.active_style_structs():
% if style_struct.inherited:
${style_struct.ident}: StyleStructRef::Borrowed(inherited_style.${style_struct.name_lower}_arc()),
% else:
${style_struct.ident}: StyleStructRef::Borrowed(reset_style.${style_struct.name_lower}_arc()),
% endif
% endfor
}
@@ -3250,17 +3256,16 @@ impl<'a> StyleBuilder<'a> {
inherited_style_ignoring_first_line: inherited_style,
reset_style,
pseudo: None,
modified_reset: false,
rules: None,
custom_properties: style_to_derive_from.custom_properties().cloned(),
writing_mode: style_to_derive_from.writing_mode,
flags: style_to_derive_from.flags,
- visited_style: None,
% for style_struct in data.active_style_structs():
${style_struct.ident}: StyleStructRef::Borrowed(
style_to_derive_from.${style_struct.name_lower}_arc()
),
% endfor
}
}
@@ -3357,47 +3362,27 @@ impl<'a> StyleBuilder<'a> {
/// Inherits style from the parent element, accounting for the default
/// computed values that need to be provided as well.
pub fn for_inheritance(
device: &'a Device,
parent: Option<<&'a ComputedValues>,
pseudo: Option<<&'a PseudoElement>,
) -> Self {
- // Rebuild the visited style from the parent, ensuring that it will also
- // not have rules. This matches the unvisited style that will be
- // produced by this builder. This assumes that the caller doesn't need
- // to adjust or process visited style, so we can just build visited
- // style here for simplicity.
- let visited_style = parent.and_then(|parent| {
- parent.visited_style().map(|style| {
- Self::for_inheritance(
- device,
- Some(style),
- pseudo,
- ).build().shareable()
- })
- });
Self::new(
device,
parent,
parent,
pseudo,
CascadeFlags::empty(),
/* rules = */ None,
parent.and_then(|p| p.custom_properties().cloned()),
- visited_style,
)
}
- /// Returns whether we have a visited style.
- pub fn has_visited_style(&self) -> bool {
- self.visited_style.is_some()
- }
-
/// Returns whether we're a pseudo-elements style.
pub fn is_pseudo_element(&self) -> bool {
self.pseudo.map_or(false, |p| !p.is_anon_box())
}
/// Returns the style we're getting reset properties from.
pub fn default_style(&self) -> &'a ComputedValues {
self.reset_style
@@ -3487,17 +3472,16 @@ impl<'a> StyleBuilder<'a> {
ComputedValues::new(
self.device,
self.parent_style,
self.pseudo,
self.custom_properties,
self.writing_mode,
self.flags,
self.rules,
- self.visited_style,
% for style_struct in data.active_style_structs():
self.${style_struct.ident}.build(),
% endfor
)
}
/// Get the custom properties map if necessary.
///
@@ -3626,17 +3610,16 @@ bitflags! {
pub fn cascade<E>(
device: &Device,
pseudo: Option<<&PseudoElement>,
rule_node: &StrongRuleNode,
guards: &StylesheetGuards,
parent_style: Option<<&ComputedValues>,
parent_style_ignoring_first_line: Option<<&ComputedValues>,
layout_parent_style: Option<<&ComputedValues>,
- visited_style: Option<Arc<ComputedValues>>,
font_metrics_provider: &FontMetricsProvider,
flags: CascadeFlags,
quirks_mode: QuirksMode,
rule_cache: Option<<&RuleCache>,
rule_cache_conditions: &mut RuleCacheConditions,
element: Option<E>,
) -> UniqueArc<ComputedValues>
where
@@ -3689,17 +3672,16 @@ where
device,
pseudo,
rule_node,
guards,
iter_declarations,
parent_style,
parent_style_ignoring_first_line,
layout_parent_style,
- visited_style,
font_metrics_provider,
flags,
quirks_mode,
rule_cache,
rule_cache_conditions,
element,
)
}
@@ -3710,17 +3692,16 @@ pub fn apply_declarations<'a, E, F, I>(
device: &Device,
pseudo: Option<<&PseudoElement>,
rules: &StrongRuleNode,
guards: &StylesheetGuards,
iter_declarations: F,
parent_style: Option<<&ComputedValues>,
parent_style_ignoring_first_line: Option<<&ComputedValues>,
layout_parent_style: Option<<&ComputedValues>,
- visited_style: Option<Arc<ComputedValues>>,
font_metrics_provider: &FontMetricsProvider,
flags: CascadeFlags,
quirks_mode: QuirksMode,
rule_cache: Option<<&RuleCache>,
rule_cache_conditions: &mut RuleCacheConditions,
element: Option<E>,
) -> UniqueArc<ComputedValues>
where
@@ -3767,17 +3748,16 @@ where
builder: StyleBuilder::new(
device,
parent_style,
parent_style_ignoring_first_line,
pseudo,
flags,
Some(rules.clone()),
custom_properties,
- visited_style,
),
cached_system_font: None,
in_media_query: false,
for_smil_animation: false,
for_non_inherited_property: None,
font_metrics_provider,
quirks_mode,
rule_cache_conditions: RefCell::new(rule_cache_conditions),
--- a/servo/components/style/style_adjuster.rs
+++ b/servo/components/style/style_adjuster.rs
@@ -600,51 +600,16 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
_ => None,
};
if let Some(new_value) = new_value {
self.style.mutate_text().set_unicode_bidi(new_value);
}
}
}
- /// 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<E>(&mut self, element: Option<E>)
- where
- E: TElement,
- {
- if !self.style.has_visited_style() {
- return;
- }
-
- let is_link_element = self.style.pseudo.is_none() && element.map_or(false, |e| e.is_link());
-
- if !is_link_element {
- return;
- }
-
- if element.unwrap().is_visited_link() {
- self.style
- .flags
- .insert(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
- } else {
- // Need to remove to handle unvisited link inside visited.
- self.style
- .flags
- .remove(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
- }
- }
-
/// Resolves "justify-items: legacy" based on the inherited style if needed
/// to comply with:
///
/// <https://drafts.csswg.org/css-align/#valdef-justify-items-legacy>
#[cfg(feature = "gecko")]
fn adjust_for_justify_items(&mut self) {
use values::specified::align;
let justify_items = self.style.get_position().clone_justify_items();
@@ -709,17 +674,16 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
// affected by these adjustments and it'd be just wasted work anyway.
//
// It also doesn't make much sense to adjust them, since we don't
// cascade most properties anyway, and they wouldn't be looked up.
if flags.contains(CascadeFlags::VISITED_DEPENDENT_ONLY) {
return;
}
- self.adjust_for_visited(element);
#[cfg(feature = "gecko")]
{
self.adjust_for_prohibited_display_contents(element);
self.adjust_for_fieldset_content(layout_parent_style);
}
self.adjust_for_top_layer();
self.blockify_if_necessary(layout_parent_style, element);
self.adjust_for_position();
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -842,19 +842,40 @@ impl Stylist {
) -> Arc<ComputedValues>
where
E: TElement,
{
debug_assert!(pseudo.is_some() || element.is_some(), "Huh?");
let cascade_flags = pseudo.map_or(CascadeFlags::empty(), |p| p.cascade_flags());
+ // Read the comment on `precomputed_values_for_pseudo` to see why it's
+ // difficult to assert that display: contents nodes never arrive here
+ // (tl;dr: It doesn't apply for replaced elements and such, but the
+ // computed value is still "contents").
+ //
+ // FIXME(emilio): We should assert that it holds if pseudo.is_none()!
+ let mut values = properties::cascade::<E>(
+ &self.device,
+ pseudo,
+ inputs.rules.as_ref().unwrap_or(self.rule_tree.root()),
+ guards,
+ parent_style,
+ parent_style_ignoring_first_line,
+ layout_parent_style,
+ font_metrics,
+ cascade_flags,
+ self.quirks_mode,
+ rule_cache,
+ rule_cache_conditions,
+ element,
+ );
+
// We need to compute visited values if we have visited rules or if our
// parent has visited values.
- let mut visited_values = None;
if inputs.visited_rules.is_some() || parent_style.and_then(|s| s.visited_style()).is_some()
{
// At this point inputs may have visited rules, or rules.
let rule_node = match inputs.visited_rules.as_ref() {
Some(rules) => rules,
None => inputs.rules.as_ref().unwrap_or(self.rule_tree.root()),
};
@@ -872,56 +893,35 @@ impl Stylist {
inherited_style = parent_style
.map(|parent_style| parent_style.visited_style().unwrap_or(parent_style));
inherited_style_ignoring_first_line = parent_style_ignoring_first_line
.map(|parent_style| parent_style.visited_style().unwrap_or(parent_style));
layout_parent_style_for_visited = layout_parent_style
.map(|parent_style| parent_style.visited_style().unwrap_or(parent_style));
}
- visited_values = Some(properties::cascade::<E>(
+ let visited_style = properties::cascade::<E>(
&self.device,
pseudo,
rule_node,
guards,
inherited_style,
inherited_style_ignoring_first_line,
layout_parent_style_for_visited,
- None,
font_metrics,
cascade_flags | CascadeFlags::VISITED_DEPENDENT_ONLY,
self.quirks_mode,
rule_cache,
rule_cache_conditions,
element,
- ).shareable());
+ ).shareable();
+ values.set_visited_style(parent_style, visited_style, element);
}
- // Read the comment on `precomputed_values_for_pseudo` to see why it's
- // difficult to assert that display: contents nodes never arrive here
- // (tl;dr: It doesn't apply for replaced elements and such, but the
- // computed value is still "contents").
- //
- // FIXME(emilio): We should assert that it holds if pseudo.is_none()!
- properties::cascade::<E>(
- &self.device,
- pseudo,
- inputs.rules.as_ref().unwrap_or(self.rule_tree.root()),
- guards,
- parent_style,
- parent_style_ignoring_first_line,
- layout_parent_style,
- visited_values,
- font_metrics,
- cascade_flags,
- self.quirks_mode,
- rule_cache,
- rule_cache_conditions,
- element,
- ).shareable()
+ values.shareable()
}
/// Computes the cascade inputs for a lazily-cascaded pseudo-element.
///
/// See the documentation on lazy pseudo-elements in
/// docs/components/style.md
fn lazy_pseudo_rules<E>(
&self,
@@ -1579,17 +1579,16 @@ impl Stylist {
&self.device,
/* pseudo = */ None,
self.rule_tree.root(),
guards,
iter_declarations,
Some(parent_style),
Some(parent_style),
Some(parent_style),
- None,
&metrics,
CascadeFlags::empty(),
self.quirks_mode,
/* rule_cache = */ None,
&mut Default::default(),
/* element = */ None,
).shareable()
}
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -3074,21 +3074,24 @@ fn get_pseudo_style(
},
};
if is_probe {
return style;
}
Some(style.unwrap_or_else(|| {
- StyleBuilder::for_inheritance(
- doc_data.stylist.device(),
+ let device = doc_data.stylist.device();
+ let mut style = StyleBuilder::for_inheritance(
+ device,
Some(styles.primary()),
Some(pseudo),
- ).build().shareable()
+ ).build();
+ style.inherit_visited(device, styles.primary());
+ style.shareable()
}))
}
#[no_mangle]
pub unsafe extern "C" fn Servo_ComputedValues_Inherit(
raw_data: RawServoStyleSetBorrowed,
pseudo_tag: *mut nsAtom,
parent_style_context: ComputedStyleBorrowedOrNull,
@@ -3096,27 +3099,32 @@ pub unsafe extern "C" fn Servo_ComputedV
) -> ComputedStyleStrong {
let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
let for_text = target == structs::InheritTarget::Text;
let atom = Atom::from_raw(pseudo_tag);
let pseudo = PseudoElement::from_anon_box_atom(&atom)
.expect("Not an anon-box? Gah!");
+ let device = data.stylist.device();
let mut style = StyleBuilder::for_inheritance(
- data.stylist.device(),
+ device,
parent_style_context,
Some(&pseudo)
);
if for_text {
StyleAdjuster::new(&mut style).adjust_for_text();
}
- style.build().shareable().into()
+ let mut style = style.build();
+ if let Some(parent) = parent_style_context {
+ style.inherit_visited(device, parent);
+ }
+ style.shareable().into()
}
#[no_mangle]
pub extern "C" fn Servo_ComputedValues_GetStyleBits(values: ComputedStyleBorrowed) -> u8 {
use style::properties::computed_value_flags::ComputedValueFlags;
// FIXME(emilio): We could do this more efficiently I'm quite sure.
let flags = values.flags;
let mut result = 0;