diff --git a/components/layout/query.rs b/components/layout/query.rs index ae7a506124b..e96d50890be 100644 --- a/components/layout/query.rs +++ b/components/layout/query.rs @@ -37,7 +37,7 @@ use style::selector_parser::PseudoElement; use style_traits::ToCss; use style_traits::cursor::Cursor; use webrender_traits::ClipId; -use wrapper::{LayoutNodeHelpers, LayoutNodeLayoutData}; +use wrapper::LayoutNodeLayoutData; /// Mutable data belonging to the LayoutThread. /// @@ -680,7 +680,9 @@ pub fn process_resolved_style_request<'a, N>(context: &LayoutContext, layout_root: &mut Flow) -> String where N: LayoutNode, { + use style::stylist::RuleInclusion; use style::traversal::resolve_style; + let element = node.as_element().unwrap(); // We call process_resolved_style_request after performing a whole-document @@ -689,30 +691,43 @@ pub fn process_resolved_style_request<'a, N>(context: &LayoutContext, return process_resolved_style_request_internal(node, pseudo, property, layout_root); } - // However, the element may be in a display:none subtree. The style system - // has a mechanism to give us that within a defined scope (after which point - // it's cleared to maintained style system invariants). + // In a display: none subtree. No pseudo-element exists. + if pseudo.is_some() { + return String::new(); + } + let mut tlc = ThreadLocalStyleContext::new(&context.style_context); let mut context = StyleContext { shared: &context.style_context, thread_local: &mut tlc, }; - let mut result = None; - let ensure = |el: N::ConcreteElement| el.as_node().initialize_data(); - let clear = |el: N::ConcreteElement| el.as_node().clear_data(); - resolve_style(&mut context, element, &ensure, &clear, |_: &_| { - let s = process_resolved_style_request_internal(node, pseudo, property, layout_root); - result = Some(s); - }); - result.unwrap() + + let styles = resolve_style(&mut context, element, RuleInclusion::All); + let style = styles.primary(); + let longhand_id = match *property { + PropertyId::Longhand(id) => id, + // Firefox returns blank strings for the computed value of shorthands, + // so this should be web-compatible. + PropertyId::Shorthand(_) => return String::new(), + PropertyId::Custom(ref name) => { + return style.computed_value_to_string(PropertyDeclarationId::Custom(name)) + } + }; + + // No need to care about used values here, since we're on a display: none + // subtree, use the resolved value. + style.computed_value_to_string(PropertyDeclarationId::Longhand(longhand_id)) } /// The primary resolution logic, which assumes that the element is styled. -fn process_resolved_style_request_internal<'a, N>(requested_node: N, - pseudo: &Option, - property: &PropertyId, - layout_root: &mut Flow) -> String - where N: LayoutNode, +fn process_resolved_style_request_internal<'a, N>( + requested_node: N, + pseudo: &Option, + property: &PropertyId, + layout_root: &mut Flow, +) -> String +where + N: LayoutNode, { let layout_el = requested_node.to_threadsafe().as_element().unwrap(); let layout_el = match *pseudo { @@ -721,6 +736,8 @@ fn process_resolved_style_request_internal<'a, N>(requested_node: N, Some(PseudoElement::DetailsSummary) | Some(PseudoElement::DetailsContent) | Some(PseudoElement::Selection) => None, + // FIXME(emilio): What about the other pseudos? Probably they shouldn't + // just return the element's style! _ => Some(layout_el) }; @@ -735,7 +752,6 @@ fn process_resolved_style_request_internal<'a, N>(requested_node: N, }; let style = &*layout_el.resolved_style(); - let longhand_id = match *property { PropertyId::Longhand(id) => id, diff --git a/components/style/style_resolver.rs b/components/style/style_resolver.rs index cecb52cd9eb..e926ba69f95 100644 --- a/components/style/style_resolver.rs +++ b/components/style/style_resolver.rs @@ -21,14 +21,16 @@ use stylearc::Arc; use stylist::RuleInclusion; /// A struct that takes care of resolving the style of a given element. -pub struct StyleResolverForElement<'a, 'b, E> +pub struct StyleResolverForElement<'a, 'ctx, 'le, E> where - 'b: 'a, - E: TElement + MatchMethods + 'static, + 'ctx: 'a, + 'le: 'ctx, + E: TElement + MatchMethods + 'le, { element: E, - context: &'a mut StyleContext<'b, E>, + context: &'a mut StyleContext<'ctx, E>, rule_inclusion: RuleInclusion, + _marker: ::std::marker::PhantomData<&'le E>, } struct MatchingResults { @@ -47,18 +49,24 @@ pub struct PrimaryStyle { pub relevant_link_found: bool, } -impl<'a, 'b, E> StyleResolverForElement<'a, 'b, E> +impl<'a, 'ctx, 'le, E> StyleResolverForElement<'a, 'ctx, 'le, E> where - 'b: 'a, - E: TElement + MatchMethods + 'static, + 'ctx: 'a, + 'le: 'ctx, + E: TElement + MatchMethods + 'le, { /// Trivially construct a new StyleResolverForElement. pub fn new( element: E, - context: &'a mut StyleContext<'b, E>, + context: &'a mut StyleContext<'ctx, E>, rule_inclusion: RuleInclusion, ) -> Self { - Self { element, context, rule_inclusion, } + Self { + element, + context, + rule_inclusion, + _marker: ::std::marker::PhantomData, + } } /// Resolve just the style of a given element. diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 47203980fc5..d7b0835a991 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -7,12 +7,12 @@ use atomic_refcell::AtomicRefCell; use context::{ElementCascadeInputs, StyleContext, SharedStyleContext}; use data::{ElementData, ElementStyles}; -use dom::{DirtyDescendants, NodeInfo, OpaqueNode, TElement, TNode}; +use dom::{NodeInfo, OpaqueNode, TElement, TNode}; use invalidation::element::restyle_hints::{RECASCADE_SELF, RECASCADE_DESCENDANTS, RestyleHint}; use matching::{ChildCascadeRequirement, MatchMethods}; use sharing::{StyleSharingBehavior, StyleSharingTarget}; -#[cfg(feature = "servo")] use servo_config::opts; use smallvec::SmallVec; +use stylist::RuleInclusion; /// A per-traversal-level chunk of data. This is sent down by the traversal, and /// currently only holds the dom depth for the bloom filter. @@ -126,6 +126,8 @@ impl TraversalDriver { #[cfg(feature = "servo")] fn is_servo_nonincremental_layout() -> bool { + use servo_config::opts; + opts::get().nonincremental_layout } @@ -520,151 +522,87 @@ pub trait DomTraversal : Sync { fn is_parallel(&self) -> bool; } -/// Helper for the function below. -fn resolve_style_internal( - context: &mut StyleContext, - element: E, ensure_data: &F -) -> Option - where E: TElement, - F: Fn(E), -{ - ensure_data(element); - let mut data = element.mutate_data().unwrap(); - let mut display_none_root = None; - - // If the Element isn't styled, we need to compute its style. - if !data.has_styles() { - // Compute the parent style if necessary. - let parent = element.traversal_parent(); - if let Some(p) = parent { - display_none_root = resolve_style_internal(context, p, ensure_data); - } - - // Maintain the bloom filter. If it doesn't exist, we need to build it - // from scratch. Otherwise we just need to push the parent. - if context.thread_local.bloom_filter.is_empty() { - context.thread_local.bloom_filter.rebuild(element); - } else { - context.thread_local.bloom_filter.push(parent.unwrap()); - context.thread_local.bloom_filter.assert_complete(element); - } - - // Compute our style. - context.thread_local.begin_element(element, &data); - element.match_and_cascade(context, - &mut data, - StyleSharingBehavior::Disallow); - context.thread_local.end_element(element); - - if !context.shared.traversal_flags.for_default_styles() { - // Conservatively mark us as having dirty descendants, since there - // might be other unstyled siblings we miss when walking straight up - // the parent chain. - // - // No need to do this if we're computing default styles, since - // resolve_default_style will want the tree to be left as it is. - unsafe { element.note_descendants::() }; - } - } - - // If we're display:none and none of our ancestors are, we're the root of a - // display:none subtree. - if display_none_root.is_none() && data.styles.is_display_none() { - display_none_root = Some(element); - } - - return display_none_root -} - /// Manually resolve style by sequentially walking up the parent chain to the /// first styled Element, ignoring pending restyles. The resolved style is made /// available via a callback, and can be dropped by the time this function /// returns in the display:none subtree case. -pub fn resolve_style(context: &mut StyleContext, element: E, - ensure_data: &F, clear_data: &G, callback: H) - where E: TElement, - F: Fn(E), - G: Fn(E), - H: FnOnce(&ElementStyles) +pub fn resolve_style( + context: &mut StyleContext, + element: E, + rule_inclusion: RuleInclusion, +) -> ElementStyles +where + E: TElement, { + use style_resolver::StyleResolverForElement; + + debug_assert!(rule_inclusion == RuleInclusion::DefaultOnly || + element.borrow_data().map_or(true, |d| !d.has_styles()), + "Why are we here?"); + let mut ancestors_requiring_style_resolution = SmallVec::<[E; 16]>::new(); + // Clear the bloom filter, just in case the caller is reusing TLS. context.thread_local.bloom_filter.clear(); - // Resolve styles up the tree. - let display_none_root = resolve_style_internal(context, element, ensure_data); - - // Make them available for the scope of the callback. The callee may use the - // argument, or perform any other processing that requires the styles to - // exist on the Element. - callback(&element.borrow_data().unwrap().styles); - - // Clear any styles in display:none subtrees or subtrees not in the - // document, to leave the tree in a valid state. For display:none subtrees, - // we leave the styles on the display:none root, but for subtrees not in the - // document, we clear styles all the way up to the root of the disconnected - // subtree. - let in_doc = element.as_node().is_in_doc(); - if !in_doc || display_none_root.is_some() { - let mut curr = element; - loop { - unsafe { - curr.unset_dirty_descendants(); - curr.unset_animation_only_dirty_descendants(); - } - if in_doc && curr == display_none_root.unwrap() { - break; - } - clear_data(curr); - curr = match curr.traversal_parent() { - Some(parent) => parent, - None => break, - }; - } - } -} - -/// Manually resolve default styles for the given Element, which are the styles -/// only taking into account user agent and user cascade levels. The resolved -/// style is made available via a callback, and will be dropped by the time this -/// function returns. -pub fn resolve_default_style( - context: &mut StyleContext, - element: E, - ensure_data: &F, - set_data: &G, - callback: H -) -where - E: TElement, - F: Fn(E), - G: Fn(E, Option) -> Option, - H: FnOnce(&ElementStyles), -{ - // Save and clear out element data from the element and its ancestors. - let mut old_data: SmallVec<[(E, Option); 8]> = SmallVec::new(); - { - let mut e = element; - loop { - old_data.push((e, set_data(e, None))); - match e.traversal_parent() { - Some(parent) => e = parent, - None => break, + let mut style = None; + let mut ancestor = element.traversal_parent(); + while let Some(current) = ancestor { + if rule_inclusion == RuleInclusion::All { + if let Some(data) = element.borrow_data() { + if let Some(ancestor_style) = data.styles.get_primary() { + style = Some(ancestor_style.clone()); + break; + } } } + ancestors_requiring_style_resolution.push(current); + ancestor = current.traversal_parent(); } - // Resolve styles up the tree. - resolve_style_internal(context, element, ensure_data); - - // Make them available for the scope of the callback. The callee may use the - // argument, or perform any other processing that requires the styles to - // exist on the Element. - callback(&element.borrow_data().unwrap().styles); - - // Swap the old element data back into the element and its ancestors. - for entry in old_data { - set_data(entry.0, entry.1); + if let Some(ancestor) = ancestor { + context.thread_local.bloom_filter.rebuild(ancestor); + context.thread_local.bloom_filter.push(ancestor); } + + let mut layout_parent_style = style.clone(); + while let Some(style) = layout_parent_style.take() { + if !style.is_display_contents() { + layout_parent_style = Some(style); + break; + } + + ancestor = ancestor.unwrap().traversal_parent(); + layout_parent_style = ancestor.map(|a| { + a.borrow_data().unwrap().styles.primary().clone() + }); + } + + for ancestor in ancestors_requiring_style_resolution.iter().rev() { + context.thread_local.bloom_filter.assert_complete(*ancestor); + + let primary_style = + StyleResolverForElement::new(*ancestor, context, rule_inclusion) + .resolve_primary_style( + style.as_ref().map(|s| &**s), + layout_parent_style.as_ref().map(|s| &**s) + ); + + let is_display_contents = primary_style.style.is_display_contents(); + + style = Some(primary_style.style); + if !is_display_contents { + layout_parent_style = style.clone(); + } + + context.thread_local.bloom_filter.push(*ancestor); + } + + context.thread_local.bloom_filter.assert_complete(element); + StyleResolverForElement::new(element, context, rule_inclusion) + .resolve_style( + style.as_ref().map(|s| &**s), + layout_parent_style.as_ref().map(|s| &**s) + ) } /// Calculates the style for a single node. diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 5edcc134fa3..3e049e1da3d 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -114,7 +114,7 @@ use style::thread_state; use style::timer::Timer; use style::traversal::{ANIMATION_ONLY, DomTraversal, FOR_CSS_RULE_CHANGES, FOR_RECONSTRUCT}; use style::traversal::{FOR_DEFAULT_STYLES, TraversalDriver, TraversalFlags, UNSTYLED_CHILDREN_ONLY}; -use style::traversal::{resolve_style, resolve_default_style}; +use style::traversal::resolve_style; use style::values::{CustomIdent, KeyframesName}; use style::values::computed::Context; use style_traits::{PARSING_MODE_DEFAULT, ToCss}; @@ -2775,7 +2775,6 @@ pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed, }; // We don't have the style ready. Go ahead and compute it as necessary. - let mut result = None; let shared = create_shared_context(&global_style_data, &guard, &data, @@ -2786,22 +2785,9 @@ pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed, shared: &shared, thread_local: &mut tlc, }; - let ensure = |el: GeckoElement| { unsafe { el.ensure_data(); } }; - match rule_inclusion { - RuleInclusion::All => { - let clear = |el: GeckoElement| el.clear_data(); - resolve_style(&mut context, element, &ensure, &clear, - |styles| result = Some(finish(styles))); - } - RuleInclusion::DefaultOnly => { - let set_data = |el: GeckoElement, data| { unsafe { el.set_data(data) } }; - resolve_default_style(&mut context, element, &ensure, &set_data, - |styles| result = Some(finish(styles))); - } - } - - result.unwrap().into_strong() + let styles = resolve_style(&mut context, element, rule_inclusion); + finish(&styles).into_strong() } #[cfg(feature = "gecko_debug")]