Skip to content

Commit

Permalink
Add a Stylo API for reparenting a given style.
Browse files Browse the repository at this point in the history
Servo side of part 4 of the fix for Gecko bug 1324619.  r=emilio
  • Loading branch information
bzbarsky committed Jul 29, 2017
1 parent 8db39f8 commit 7161fff
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 37 deletions.
2 changes: 1 addition & 1 deletion components/style/context.rs
Expand Up @@ -191,7 +191,7 @@ pub struct CascadeInputs {

impl CascadeInputs {
/// Construct inputs from previous cascade results, if any.
pub fn new_from_style(style: &Arc<ComputedValues>) -> Self {
pub fn new_from_style(style: &ComputedValues) -> Self {
CascadeInputs {
rules: style.rules.clone(),
visited_rules: style.get_visited_style().and_then(|v| v.rules.clone()),
Expand Down
9 changes: 9 additions & 0 deletions components/style/gecko/data.rs
Expand Up @@ -204,6 +204,15 @@ impl PerDocumentStyleDataImpl {
pub fn clear_stylist(&mut self) {
self.stylist.clear();
}

/// Returns whether visited links are enabled.
fn visited_links_enabled(&self) -> bool {
unsafe { bindings::Gecko_AreVisitedLinksEnabled() }
}
/// Returns whether visited styles are enabled.
pub fn visited_styles_enabled(&self) -> bool {
self.visited_links_enabled() && !self.is_private_browsing_enabled()
}
}

unsafe impl HasFFI for PerDocumentStyleData {
Expand Down
10 changes: 10 additions & 0 deletions components/style/gecko/generated/bindings.rs
Expand Up @@ -2820,6 +2820,16 @@ extern "C" {
set: RawServoStyleSetBorrowed)
-> ServoStyleContextStrong;
}
extern "C" {
pub fn Servo_ReparentStyle(style_to_reparent: ServoStyleContextBorrowed,
parent_style: ServoStyleContextBorrowed,
parent_style_ignoring_first_line:
ServoStyleContextBorrowed,
layout_parent_style: ServoStyleContextBorrowed,
element: RawGeckoElementBorrowedOrNull,
set: RawServoStyleSetBorrowed)
-> ServoStyleContextStrong;
}
extern "C" {
pub fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed,
set: RawServoStyleSetBorrowed,
Expand Down
15 changes: 15 additions & 0 deletions components/style/properties/gecko.mako.rs
Expand Up @@ -41,6 +41,9 @@ use gecko_bindings::bindings::{Gecko_ResetFilters, Gecko_CopyFiltersFrom};
use gecko_bindings::bindings::RawGeckoPresContextBorrowed;
use gecko_bindings::structs;
use gecko_bindings::structs::nsCSSPropertyID;
use gecko_bindings::structs::mozilla::CSSPseudoElementType;
use gecko_bindings::structs::mozilla::CSSPseudoElementType_InheritingAnonBox;
use gecko_bindings::structs::root::NS_STYLE_CONTEXT_TYPE_SHIFT;
use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordData, CoordDataMut};
use gecko::values::convert_nscolor_to_rgba;
use gecko::values::convert_rgba_to_nscolor;
Expand Down Expand Up @@ -133,6 +136,18 @@ impl ComputedValues {
let atom = Atom::from(atom);
PseudoElement::from_atom(&atom)
}

fn get_pseudo_type(&self) -> CSSPseudoElementType {
let bits = (self.0)._base.mBits;
let our_type = bits >> NS_STYLE_CONTEXT_TYPE_SHIFT;
unsafe { transmute(our_type as u8) }
}

pub fn is_anon_box(&self) -> bool {
let our_type = self.get_pseudo_type();
return our_type == CSSPseudoElementType_InheritingAnonBox ||
our_type == CSSPseudoElementType::NonInheritingAnonBox;
}
}

impl Drop for ComputedValues {
Expand Down
103 changes: 77 additions & 26 deletions components/style/stylist.rs
Expand Up @@ -20,6 +20,7 @@ use properties::{self, CascadeFlags, ComputedValues};
use properties::{AnimationRules, PropertyDeclarationBlock};
#[cfg(feature = "servo")]
use properties::INHERIT_ALL;
use properties::IS_LINK;
use rule_tree::{CascadeLevel, RuleTree, StyleSource};
use selector_map::{SelectorMap, SelectorMapEntry};
use selector_parser::{SelectorImpl, PseudoElement};
Expand Down Expand Up @@ -727,6 +728,47 @@ impl Stylist {
return None
}

// FIXME(emilio): The lack of layout_parent_style here could be
// worrying, but we're probably dropping the display fixup for
// pseudos other than before and after, so it's probably ok.
//
// (Though the flags don't indicate so!)
Some(self.compute_style_with_inputs(inputs,
Some(pseudo),
guards,
parent_style,
parent_style,
parent_style,
font_metrics,
CascadeFlags::empty()))
}

/// Computes a style using the given CascadeInputs. This can be used to
/// compute a style any time we know what rules apply and just need to use
/// the given parent styles.
///
/// parent_style is the style to inherit from for properties affected by
/// first-line ancestors.
///
/// parent_style_ignoring_first_line is the style to inherit from for
/// properties not affected by first-line ancestors.
///
/// layout_parent_style is the style used for some property fixups. It's
/// the style of the nearest ancestor with a layout box.
///
/// is_link should be true if we're computing style for a link; that affects
/// how :visited handling is done.
pub fn compute_style_with_inputs(&self,
inputs: &CascadeInputs,
pseudo: Option<&PseudoElement>,
guards: &StylesheetGuards,
parent_style: &ComputedValues,
parent_style_ignoring_first_line: &ComputedValues,
layout_parent_style: &ComputedValues,
font_metrics: &FontMetricsProvider,
cascade_flags: CascadeFlags)
-> Arc<ComputedValues>
{
// We need to compute visited values if we have visited rules or if our
// parent has visited values.
let visited_values = if inputs.visited_rules.is_some() || parent_style.get_visited_style().is_some() {
Expand All @@ -737,28 +779,37 @@ impl Stylist {
Some(rules) => rules,
None => inputs.rules.as_ref().unwrap(),
};
// We want to use the visited bits (if any) from our parent style as
// our parent.
let inherited_style =
parent_style.get_visited_style().unwrap_or(parent_style);

// FIXME(emilio): The lack of layout_parent_style here could be
// worrying, but we're probably dropping the display fixup for
// pseudos other than before and after, so it's probably ok.
//
// (Though the flags don't indicate so!)
let inherited_style;
let inherited_style_ignoring_first_line;
let layout_parent_style_for_visited;
if cascade_flags.contains(IS_LINK) {
// We just want to use our parent style as our parent.
inherited_style = parent_style;
inherited_style_ignoring_first_line = parent_style_ignoring_first_line;
layout_parent_style_for_visited = layout_parent_style;
} else {
// We want to use the visited bits (if any) from our parent
// style as our parent.
inherited_style =
parent_style.get_visited_style().unwrap_or(parent_style);
inherited_style_ignoring_first_line =
parent_style_ignoring_first_line.get_visited_style().unwrap_or(parent_style_ignoring_first_line);
layout_parent_style_for_visited =
layout_parent_style.get_visited_style().unwrap_or(layout_parent_style);
}

let computed =
properties::cascade(&self.device,
Some(pseudo),
pseudo,
rule_node,
guards,
Some(inherited_style),
Some(inherited_style),
Some(inherited_style),
Some(inherited_style_ignoring_first_line),
Some(layout_parent_style_for_visited),
None,
None,
font_metrics,
CascadeFlags::empty(),
cascade_flags,
self.quirks_mode);

Some(computed)
Expand All @@ -774,18 +825,18 @@ impl Stylist {
// 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").
Some(properties::cascade(&self.device,
Some(pseudo),
rules,
guards,
Some(parent_style),
Some(parent_style),
Some(parent_style),
visited_values,
None,
font_metrics,
CascadeFlags::empty(),
self.quirks_mode))
properties::cascade(&self.device,
pseudo,
rules,
guards,
Some(parent_style),
Some(parent_style_ignoring_first_line),
Some(layout_parent_style),
visited_values,
None,
font_metrics,
cascade_flags,
self.quirks_mode)
}

/// Computes the cascade inputs for a lazily-cascaded pseudo-element.
Expand Down
88 changes: 78 additions & 10 deletions ports/geckolib/glue.rs
Expand Up @@ -95,10 +95,11 @@ use style::invalidation::element::restyle_hints::{self, RestyleHint};
use style::media_queries::{MediaList, parse_media_query_list};
use style::parallel;
use style::parser::{ParserContext, self};
use style::properties::{ComputedValues, Importance};
use style::properties::{IS_FIELDSET_CONTENT, LonghandIdSet};
use style::properties::{CascadeFlags, ComputedValues, Importance};
use style::properties::{IS_FIELDSET_CONTENT, IS_LINK, IS_VISITED_LINK, LonghandIdSet};
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyId, ShorthandId};
use style::properties::{SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, SourcePropertyDeclaration, StyleBuilder};
use style::properties::PROHIBIT_DISPLAY_CONTENTS;
use style::properties::animated_properties::{Animatable, AnimatableLonghand, AnimationValue};
use style::properties::animated_properties::compare_property_priority;
use style::properties::parse_one_declaration_into;
Expand Down Expand Up @@ -179,13 +180,9 @@ fn create_shared_context<'a>(global_style_data: &GlobalStyleData,
traversal_flags: TraversalFlags,
snapshot_map: &'a ServoElementSnapshotTable)
-> SharedStyleContext<'a> {
let visited_styles_enabled =
unsafe { bindings::Gecko_AreVisitedLinksEnabled() } &&
!per_doc_data.is_private_browsing_enabled();

SharedStyleContext {
stylist: &per_doc_data.stylist,
visited_styles_enabled: visited_styles_enabled,
visited_styles_enabled: per_doc_data.visited_styles_enabled(),
options: global_style_data.options.clone(),
guards: StylesheetGuards::same(guard),
timer: Timer::new(),
Expand Down Expand Up @@ -1661,9 +1658,27 @@ fn get_pseudo_style(
})
},
_ => {
debug_assert!(inherited_styles.is_none() ||
ptr::eq(inherited_styles.unwrap(),
&**styles.primary()));
// Unfortunately, we can't assert that inherited_styles, if
// present, is pointer-equal to styles.primary(), or even
// equal in any meaningful way. The way it can fail is as
// follows. Say we append an element with a ::before,
// ::after, or ::first-line to a parent with a ::first-line,
// such that the element ends up on the first line of the
// parent (e.g. it's an inline-block in the case it has a
// ::first-line, or any container in the ::before/::after
// cases). Then gecko will update its frame's style to
// inherit from the parent's ::first-line. The next time we
// try to get the ::before/::after/::first-line style for
// the kid, we'll likely pass in the frame's style as
// inherited_styles, but that's not pointer-identical to
// styles.primary(), because it got reparented.
//
// Now in practice this turns out to be OK, because all the
// cases in which there's a mismatch go ahead and reparent
// styles again as needed to make sure the ::first-line
// affects all the things it should affect. But it makes it
// impossible to assert anything about the two styles
// matching here, unfortunately.
styles.pseudos.get(&pseudo).cloned()
},
}
Expand Down Expand Up @@ -2903,6 +2918,59 @@ pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
finish(&styles).into()
}

#[no_mangle]
pub extern "C" fn Servo_ReparentStyle(style_to_reparent: ServoStyleContextBorrowed,
parent_style: ServoStyleContextBorrowed,
parent_style_ignoring_first_line: ServoStyleContextBorrowed,
layout_parent_style: ServoStyleContextBorrowed,
element: RawGeckoElementBorrowedOrNull,
raw_data: RawServoStyleSetBorrowed)
-> ServoStyleContextStrong
{
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow();
let inputs = CascadeInputs::new_from_style(style_to_reparent);
let metrics = get_metrics_provider_for_product();
let pseudo = style_to_reparent.pseudo();
let element = element.map(GeckoElement);

let mut cascade_flags = CascadeFlags::empty();
if style_to_reparent.is_anon_box() {
cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP);
}
if let Some(element) = element {
if element.is_link() {
cascade_flags.insert(IS_LINK);
if element.is_visited_link() &&
doc_data.visited_styles_enabled() {
cascade_flags.insert(IS_VISITED_LINK);
}
};

if element.is_native_anonymous() {
cascade_flags.insert(PROHIBIT_DISPLAY_CONTENTS);
}
}
if let Some(pseudo) = pseudo.as_ref() {
cascade_flags.insert(PROHIBIT_DISPLAY_CONTENTS);
if pseudo.is_fieldset_content() {
cascade_flags.insert(IS_FIELDSET_CONTENT);
}
}

doc_data.stylist
.compute_style_with_inputs(&inputs,
pseudo.as_ref(),
&StylesheetGuards::same(&guard),
parent_style,
parent_style_ignoring_first_line,
layout_parent_style,
&metrics,
cascade_flags)
.into()
}

#[cfg(feature = "gecko_debug")]
fn simulate_compute_values_failure(property: &PropertyValuePair) -> bool {
let p = property.mProperty;
Expand Down

0 comments on commit 7161fff

Please sign in to comment.