servo: Merge #17033 - style: Add support for resolving default computed styles (from heycam:default-cs); r=emilio

Servo-side changes from https://bugzilla.mozilla.org/show_bug.cgi?id=1366157.

Source-Repo: https://github.com/servo/servo
Source-Revision: 7780ef5e431b7c346c0f50ff914fbda9b5017900

--HG--
extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear
extra : subtree_revision : c4752fdd70885e2630d195618d06fb0d9ae1d120
This commit is contained in:
Cameron McCormack 2017-05-25 05:50:52 -05:00
Родитель 05e5c16a8e
Коммит 40bf61aba0
9 изменённых файлов: 2784 добавлений и 2437 удалений

Просмотреть файл

@ -25,6 +25,7 @@ use style::font_metrics::ServoMetricsProvider;
use style::properties::{CascadeFlags, ServoComputedValues};
use style::selector_parser::{PseudoElement, PseudoElementCascadeType, SelectorImpl};
use style::stylearc::Arc;
use style::stylist::RuleInclusion;
use webrender_traits::ClipId;
#[derive(Copy, PartialEq, Clone, Debug)]
@ -423,6 +424,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
&context.guards,
unsafe { &self.unsafe_get() },
&style_pseudo,
RuleInclusion::All,
data.styles().primary.values(),
&ServoMetricsProvider)
.unwrap()

Просмотреть файл

@ -205,6 +205,7 @@ use gecko_bindings::structs::UpdateAnimationsTasks;
use gecko_bindings::structs::ParsingMode;
use gecko_bindings::structs::InheritTarget;
use gecko_bindings::structs::URLMatchingFunction;
use gecko_bindings::structs::StyleRuleInclusion;
pub type nsTArrayBorrowed_uintptr_t<'a> = &'a mut ::gecko_bindings::structs::nsTArray<usize>;
pub type RawServoStyleSetOwned = ::gecko_bindings::sugar::ownership::Owned<RawServoStyleSet>;
pub type RawServoStyleSetOwnedOrNull = ::gecko_bindings::sugar::ownership::OwnedOrNull<RawServoStyleSet>;
@ -2417,6 +2418,11 @@ extern "C" {
target: InheritTarget)
-> ServoComputedValuesStrong;
}
extern "C" {
pub fn Servo_ComputedValues_GetVisitedStyle(values:
ServoComputedValuesBorrowed)
-> ServoComputedValuesStrong;
}
extern "C" {
pub fn Servo_Initialize(dummy_url_data: *mut RawGeckoURLExtraData);
}
@ -2457,6 +2463,7 @@ extern "C" {
extern "C" {
pub fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
pseudo_type: CSSPseudoElementType,
rule_inclusion: StyleRuleInclusion,
snapshots:
*const ServoElementSnapshotTable,
set: RawServoStyleSetBorrowed)

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -72,6 +72,8 @@ use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::mem;
use std::ops::DerefMut;
use std::ptr;
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
use stylearc::Arc;
@ -422,6 +424,29 @@ impl<'le> GeckoElement<'le> {
}
}
/// Sets the specified element data, return any existing data.
///
/// Like `ensure_data`, only safe to call with exclusive access to the
/// element.
pub unsafe fn set_data(&self, replace_data: Option<ElementData>) -> Option<ElementData> {
match (self.get_data(), replace_data) {
(Some(old), Some(replace_data)) => {
Some(mem::replace(old.borrow_mut().deref_mut(), replace_data))
}
(Some(old), None) => {
let old_data = mem::replace(old.borrow_mut().deref_mut(), ElementData::new(None));
self.0.mServoData.set(ptr::null_mut());
Some(old_data)
}
(None, Some(replace_data)) => {
let ptr = Box::into_raw(Box::new(AtomicRefCell::new(replace_data)));
self.0.mServoData.set(ptr);
None
}
(None, None) => None,
}
}
#[inline]
fn has_id(&self) -> bool {
self.as_node().get_bool_flag(nsINode_BooleanFlag::ElementHasID)

Просмотреть файл

@ -24,7 +24,7 @@ use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, S
use selectors::matching::{VisitedHandlingMode, AFFECTED_BY_PSEUDO_ELEMENTS};
use sharing::{StyleSharingBehavior, StyleSharingResult};
use stylearc::Arc;
use stylist::ApplicableDeclarationList;
use stylist::{ApplicableDeclarationList, RuleInclusion};
/// The way a style should be inherited.
enum InheritMode {
@ -346,7 +346,13 @@ trait PrivateMatchMethods: TElement {
// We could make that a bit better if the complexity cost is not too
// big, but given further restyles are posted directly to
// pseudo-elements, it doesn't seem worth the effort at a glance.
if pseudo.is_eager() {
//
// For the same reason as described in match_primary, if we are
// computing default styles, we aren't guaranteed the parent
// will have eagerly computed our styles, so we just handled it
// below like a lazy pseudo.
let only_default_rules = context.shared.traversal_flags.for_default_styles();
if pseudo.is_eager() && !only_default_rules {
let parent = self.parent_element().unwrap();
if !parent.may_have_animations() ||
primary_style.rules.get_animation_rules().is_empty() {
@ -896,13 +902,24 @@ pub trait MatchMethods : TElement {
{
debug!("Match primary for {:?}, visited: {:?}", self, visited_handling);
let only_default_rules = context.shared.traversal_flags.for_default_styles();
let implemented_pseudo = self.implemented_pseudo_element();
if let Some(ref pseudo) = implemented_pseudo {
// We don't expect to match against a non-canonical pseudo-element.
debug_assert_eq!(*pseudo, pseudo.canonical());
if pseudo.is_eager() {
// If it's an eager element-backed pseudo, just grab the matched
// rules from the parent, and update animations.
if pseudo.is_eager() && !only_default_rules {
// If it's an eager element-backed pseudo, we can generally just
// grab the matched rules from the parent, and then update
// animations.
//
// However, if we're computing default styles, then we might
// have traversed to this pseudo-implementing element without
// any pseudo styles stored on the parent. For example, if
// document-level style sheets cause the element to exist, due
// to ::before rules, then those rules won't be found when
// computing default styles on the parent, so we won't have
// bothered to store pseudo styles there. In this case, we just
// treat it like a lazily computed pseudo.
let parent = self.parent_element().unwrap();
let parent_data = parent.borrow_data().unwrap();
let pseudo_style =
@ -965,6 +982,12 @@ pub trait MatchMethods : TElement {
self.apply_selector_flags(map, element, flags);
};
let rule_inclusion = if only_default_rules {
RuleInclusion::DefaultOnly
} else {
RuleInclusion::All
};
let bloom_filter = context.thread_local.bloom_filter.filter();
let mut matching_context =
MatchingContext::new_for_visited(MatchingMode::Normal,
@ -985,6 +1008,7 @@ pub trait MatchMethods : TElement {
style_attribute,
smil_override,
animation_rules,
rule_inclusion,
&mut applicable_declarations,
&mut matching_context,
&mut set_selector_flags);
@ -1052,7 +1076,14 @@ pub trait MatchMethods : TElement {
let stylist = &context.shared.stylist;
let guards = &context.shared.guards;
let rule_inclusion = if context.shared.traversal_flags.for_default_styles() {
RuleInclusion::DefaultOnly
} else {
RuleInclusion::All
};
let bloom_filter = context.thread_local.bloom_filter.filter();
let mut matching_context =
MatchingContext::new_for_visited(MatchingMode::ForStatelessPseudoElement,
Some(bloom_filter),
@ -1076,6 +1107,7 @@ pub trait MatchMethods : TElement {
None,
None,
AnimationRules(None, None),
rule_inclusion,
&mut applicable_declarations,
&mut matching_context,
&mut set_selector_flags);

Просмотреть файл

@ -13,7 +13,7 @@ use element_state::ElementState;
use error_reporting::RustLogReporter;
use font_metrics::FontMetricsProvider;
#[cfg(feature = "gecko")]
use gecko_bindings::structs::nsIAtom;
use gecko_bindings::structs::{nsIAtom, StyleRuleInclusion};
use keyframes::KeyframesAnimation;
use media_queries::Device;
use properties::{self, CascadeFlags, ComputedValues};
@ -205,6 +205,27 @@ impl<'a> ExtraStyleData<'a> {
fn clear(&mut self) {}
}
/// What cascade levels to include when styling elements.
#[derive(Copy, Clone, PartialEq)]
pub enum RuleInclusion {
/// Include rules for style sheets at all cascade levels. This is the
/// normal rule inclusion mode.
All,
/// Only include rules from UA and user level sheets. Used to implement
/// `getDefaultComputedStyle`.
DefaultOnly,
}
#[cfg(feature = "gecko")]
impl From<StyleRuleInclusion> for RuleInclusion {
fn from(value: StyleRuleInclusion) -> Self {
match value {
StyleRuleInclusion::All => RuleInclusion::All,
StyleRuleInclusion::DefaultOnly => RuleInclusion::DefaultOnly,
}
}
}
impl Stylist {
/// Construct a new `Stylist`, using given `Device` and `QuirksMode`.
/// If more members are added here, think about whether they should
@ -625,13 +646,14 @@ impl Stylist {
guards: &StylesheetGuards,
element: &E,
pseudo: &PseudoElement,
rule_inclusion: RuleInclusion,
parent_style: &ComputedValues,
font_metrics: &FontMetricsProvider)
-> Option<ComputedStyle>
where E: TElement,
{
let rule_node =
match self.lazy_pseudo_rules(guards, element, pseudo) {
match self.lazy_pseudo_rules(guards, element, pseudo, rule_inclusion) {
Some(rule_node) => rule_node,
None => return None
};
@ -664,7 +686,8 @@ impl Stylist {
pub fn lazy_pseudo_rules<E>(&self,
guards: &StylesheetGuards,
element: &E,
pseudo: &PseudoElement)
pseudo: &PseudoElement,
rule_inclusion: RuleInclusion)
-> Option<StrongRuleNode>
where E: TElement
{
@ -683,6 +706,12 @@ impl Stylist {
unreachable!("internal pseudo generated slow selector flags?");
}
// No need to bother setting the selector flags when we're computing
// default styles.
if rule_inclusion == RuleInclusion::DefaultOnly {
return;
}
// Gecko calls this from sequential mode, so we can directly apply
// the flags.
debug_assert!(thread_state::get() == thread_state::LAYOUT);
@ -707,6 +736,7 @@ impl Stylist {
None,
None,
AnimationRules(None, None),
rule_inclusion,
&mut declarations,
&mut matching_context,
&mut set_selector_flags);
@ -836,6 +866,7 @@ impl Stylist {
style_attribute: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
smil_override: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
animation_rules: AnimationRules,
rule_inclusion: RuleInclusion,
applicable_declarations: &mut V,
context: &mut MatchingContext,
flags_setter: &mut F)
@ -871,6 +902,8 @@ impl Stylist {
debug!("Determining if style is shareable: pseudo: {}",
pseudo_element.is_some());
let only_default_rules = rule_inclusion == RuleInclusion::DefaultOnly;
// Step 1: Normal user-agent rules.
map.user_agent.get_all_matching_rules(element,
&rule_hash_target,
@ -880,7 +913,7 @@ impl Stylist {
CascadeLevel::UANormal);
debug!("UA normal: {:?}", context.relations);
if pseudo_element.is_none() {
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);
@ -905,7 +938,7 @@ impl Stylist {
//
// Which may be more what you would probably expect.
if rule_hash_target.matches_user_and_author_rules() {
// Step 3: User and author normal rules.
// Step 3a: User normal rules.
map.user.get_all_matching_rules(element,
&rule_hash_target,
applicable_declarations,
@ -913,6 +946,12 @@ impl Stylist {
flags_setter,
CascadeLevel::UserNormal);
debug!("user normal: {:?}", context.relations);
} else {
debug!("skipping user rules");
}
if rule_hash_target.matches_user_and_author_rules() && !only_default_rules {
// Step 3b: Author normal rules.
map.author.get_all_matching_rules(element,
&rule_hash_target,
applicable_declarations,
@ -961,15 +1000,20 @@ impl Stylist {
// rule tree insertion.
//
// Step 11: Transitions.
// The transitions sheet (CSS transitions that are tied to CSS markup)
if let Some(anim) = animation_rules.1 {
Push::push(
applicable_declarations,
ApplicableDeclarationBlock::from_declarations(anim.clone(),
CascadeLevel::Transitions));
if !only_default_rules {
// Step 11: Transitions.
// The transitions sheet (CSS transitions that are tied to CSS markup)
if let Some(anim) = animation_rules.1 {
Push::push(
applicable_declarations,
ApplicableDeclarationBlock::from_declarations(anim.clone(),
CascadeLevel::Transitions));
}
debug!("transition: {:?}", context.relations);
} else {
debug!("skipping transition rules");
}
debug!("transition: {:?}", context.relations);
debug!("push_applicable_declarations: shareable: {:?}", context.relations);
}

Просмотреть файл

@ -13,6 +13,7 @@ use restyle_hints::{HintComputationContext, RestyleHint};
use selector_parser::RestyleDamage;
use sharing::StyleSharingBehavior;
#[cfg(feature = "servo")] use servo_config::opts;
use smallvec::SmallVec;
use std::borrow::BorrowMut;
/// A per-traversal-level chunk of data. This is sent down by the traversal, and
@ -41,6 +42,8 @@ bitflags! {
/// Traverse and update all elements with CSS animations since
/// @keyframes rules may have changed
const FOR_CSS_RULE_CHANGES = 0x08,
/// Only include user agent style sheets when selector matching.
const FOR_DEFAULT_STYLES = 0x10,
}
}
@ -64,6 +67,12 @@ impl TraversalFlags {
pub fn for_css_rule_changes(&self) -> bool {
self.contains(FOR_CSS_RULE_CHANGES)
}
/// Returns true if the traversal is to compute the default computed
/// styles for an element.
pub fn for_default_styles(&self) -> bool {
self.contains(FOR_DEFAULT_STYLES)
}
}
/// This structure exists to enforce that callers invoke pre_traverse, and also
@ -537,10 +546,13 @@ fn resolve_style_internal<E, F>(context: &mut StyleContext<E>,
StyleSharingBehavior::Disallow);
context.thread_local.end_element(element);
// Conservatively mark us as having dirty descendants, since there might
// be other unstyled siblings we miss when walking straight up the parent
// chain.
unsafe { element.note_descendants::<DirtyDescendants>() };
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::<DirtyDescendants>() };
}
}
// If we're display:none and none of our ancestors are, we're the root
@ -598,6 +610,47 @@ pub fn resolve_style<E, F, G, H>(context: &mut StyleContext<E>, element: E,
}
}
/// 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<E, F, G, H>(context: &mut StyleContext<E>,
element: E,
ensure_data: &F,
set_data: &G,
callback: H)
where E: TElement,
F: Fn(E),
G: Fn(E, Option<ElementData>) -> Option<ElementData>,
H: FnOnce(&ElementStyles)
{
// Save and clear out element data from the element and its ancestors.
let mut old_data: SmallVec<[(E, Option<ElementData>); 8]> = SmallVec::new();
{
let mut e = element;
loop {
old_data.push((e, set_data(e, None)));
match e.parent_element() {
Some(parent) => e = parent,
None => break,
}
}
}
// 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);
}
}
/// Calculates the style for a single node.
#[inline]
#[allow(unsafe_code)]

Просмотреть файл

@ -72,6 +72,7 @@ use style::gecko_bindings::structs::IterationCompositeOperation;
use style::gecko_bindings::structs::Loader;
use style::gecko_bindings::structs::RawGeckoPresContextOwned;
use style::gecko_bindings::structs::ServoElementSnapshotTable;
use style::gecko_bindings::structs::StyleRuleInclusion;
use style::gecko_bindings::structs::URLExtraData;
use style::gecko_bindings::structs::nsCSSValueSharedList;
use style::gecko_bindings::structs::nsCompatibility;
@ -101,11 +102,13 @@ use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers};
use style::stylesheets::{ImportRule, KeyframesRule, MediaRule, NamespaceRule, Origin};
use style::stylesheets::{PageRule, Stylesheet, StyleRule, SupportsRule, DocumentRule};
use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
use style::stylist::RuleInclusion;
use style::supports::parse_condition_or_declaration;
use style::thread_state;
use style::timer::Timer;
use style::traversal::{ANIMATION_ONLY, FOR_CSS_RULE_CHANGES, FOR_RECONSTRUCT, UNSTYLED_CHILDREN_ONLY};
use style::traversal::{resolve_style, DomTraversal, TraversalDriver, TraversalFlags};
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::values::{CustomIdent, KeyframesName};
use style_traits::ToCss;
use super::stylesheet_loader::StylesheetLoader;
@ -1233,7 +1236,8 @@ pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
match get_pseudo_style(&guard, element, &pseudo, data.styles(), doc_data) {
match get_pseudo_style(&guard, element, &pseudo, RuleInclusion::All,
data.styles(), doc_data) {
Some(values) => values.into_strong(),
// FIXME(emilio): This looks pretty wrong! Shouldn't it be at least an
// empty style inheriting from the element?
@ -1265,6 +1269,7 @@ pub extern "C" fn Servo_HasAuthorSpecifiedRules(element: RawGeckoElementBorrowed
fn get_pseudo_style(guard: &SharedRwLockReadGuard,
element: GeckoElement,
pseudo: &PseudoElement,
rule_inclusion: RuleInclusion,
styles: &ElementStyles,
doc_data: &PerDocumentStyleData)
-> Option<Arc<ComputedValues>>
@ -1284,6 +1289,7 @@ fn get_pseudo_style(guard: &SharedRwLockReadGuard,
d.stylist.lazily_compute_pseudo_element_style(&guards,
&element,
&pseudo,
rule_inclusion,
base,
&metrics)
.map(|s| s.values().clone())
@ -2305,6 +2311,7 @@ pub extern "C" fn Servo_ResolveStyle(element: RawGeckoElementBorrowed,
#[no_mangle]
pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
pseudo_type: CSSPseudoElementType,
rule_inclusion: StyleRuleInclusion,
snapshots: *const ServoElementSnapshotTable,
raw_data: RawServoStyleSetBorrowed)
-> ServoComputedValuesStrong
@ -2314,26 +2321,35 @@ pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
let guard = global_style_data.shared_lock.read();
let element = GeckoElement(element);
let doc_data = PerDocumentStyleData::from_ffi(raw_data);
let rule_inclusion = RuleInclusion::from(rule_inclusion);
let finish = |styles: &ElementStyles| -> Arc<ComputedValues> {
PseudoElement::from_pseudo_type(pseudo_type).and_then(|ref pseudo| {
get_pseudo_style(&guard, element, pseudo, styles, doc_data)
get_pseudo_style(&guard, element, pseudo, rule_inclusion, styles, doc_data)
}).unwrap_or_else(|| styles.primary.values().clone())
};
// In the common case we already have the style. Check that before setting
// up all the computation machinery.
let mut result = element.mutate_data()
.and_then(|d| d.get_styles().map(&finish));
if result.is_some() {
return result.unwrap().into_strong();
// up all the computation machinery. (Don't use it when we're getting
// default styles, though.)
if rule_inclusion == RuleInclusion::All {
if let Some(result) = element.mutate_data()
.and_then(|d| d.get_styles().map(&finish)) {
return result.into_strong();
}
}
let traversal_flags = match rule_inclusion {
RuleInclusion::All => TraversalFlags::empty(),
RuleInclusion::DefaultOnly => FOR_DEFAULT_STYLES,
};
// We don't have the style ready. Go ahead and compute it as necessary.
let mut result = None;
let data = doc_data.borrow();
let shared = create_shared_context(&global_style_data,
&guard,
&data,
TraversalFlags::empty(),
traversal_flags,
unsafe { &*snapshots });
let mut tlc = ThreadLocalStyleContext::new(&shared);
let mut context = StyleContext {
@ -2341,9 +2357,19 @@ pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
thread_local: &mut tlc,
};
let ensure = |el: GeckoElement| { unsafe { el.ensure_data(); } };
let clear = |el: GeckoElement| el.clear_data();
resolve_style(&mut context, element, &ensure, &clear,
|styles| result = Some(finish(styles)));
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()
}