Bug 1336646 - Apply selector flags during traversal. r=emilio

This commit is contained in:
Bobby Holley 2017-02-04 13:11:02 -08:00
parent 37b8d5231d
commit 9e860df9df
17 changed files with 295 additions and 192 deletions

18
Cargo.lock generated
View file

@ -934,7 +934,7 @@ dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.17.0",
"selectors 0.18.0",
"servo_url 0.0.1",
"style 0.0.1",
"style_traits 0.0.1",
@ -1355,7 +1355,7 @@ dependencies = [
"rayon 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"script_layout_interface 0.0.1",
"script_traits 0.0.1",
"selectors 0.17.0",
"selectors 0.18.0",
"serde 0.8.20 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1401,7 +1401,7 @@ dependencies = [
"script 0.0.1",
"script_layout_interface 0.0.1",
"script_traits 0.0.1",
"selectors 0.17.0",
"selectors 0.18.0",
"serde_derive 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
"servo_config 0.0.1",
@ -2292,7 +2292,7 @@ dependencies = [
"rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
"script_layout_interface 0.0.1",
"script_traits 0.0.1",
"selectors 0.17.0",
"selectors 0.18.0",
"serde 0.8.20 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
"servo_atoms 0.0.1",
@ -2336,7 +2336,7 @@ dependencies = [
"profile_traits 0.0.1",
"range 0.0.1",
"script_traits 0.0.1",
"selectors 0.17.0",
"selectors 0.18.0",
"servo_url 0.0.1",
"style 0.0.1",
]
@ -2386,7 +2386,7 @@ dependencies = [
[[package]]
name = "selectors"
version = "0.17.0"
version = "0.18.0"
dependencies = [
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2766,7 +2766,7 @@ dependencies = [
"rayon 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.17.0",
"selectors 0.18.0",
"serde 0.8.20 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)",
"servo_atoms 0.0.1",
@ -2792,7 +2792,7 @@ dependencies = [
"parking_lot 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.17.0",
"selectors 0.18.0",
"servo_atoms 0.0.1",
"servo_config 0.0.1",
"servo_url 0.0.1",
@ -2829,7 +2829,7 @@ dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.17.0",
"selectors 0.18.0",
"servo_url 0.0.1",
"style 0.0.1",
"style_traits 0.0.1",

View file

@ -716,14 +716,14 @@ pub fn process_resolved_style_request<'a, N>(context: &LayoutContext,
// has a mechanism to give us that within a defined scope (after which point
// it's cleared to maintained style system invariants).
let mut tlc = ThreadLocalStyleContext::new(&context.style_context);
let context = StyleContext {
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(&context, element, &ensure, &clear, |_: &_| {
resolve_style(&mut context, element, &ensure, &clear, |_: &_| {
let s = process_resolved_style_request_internal(node, pseudo, property, layout_root);
result = Some(s);
});

View file

@ -76,6 +76,7 @@ use script_layout_interface::reporter::CSSErrorReporter;
use script_layout_interface::rpc::LayoutRPC;
use script_traits::{DocumentActivity, TimerEventId, TimerSource, TouchpadPressurePhase};
use script_traits::{UntrustedNodeAddress, WindowSizeData, WindowSizeType};
use selectors::matching::ElementSelectorFlags;
use serde::{Deserialize, Serialize};
use servo_atoms::Atom;
use servo_url::ServoUrl;
@ -347,6 +348,7 @@ unsafe_no_jsmanaged_fields!(TimeProfilerChan);
unsafe_no_jsmanaged_fields!(MemProfilerChan);
unsafe_no_jsmanaged_fields!(PseudoElement);
unsafe_no_jsmanaged_fields!(Length);
unsafe_no_jsmanaged_fields!(ElementSelectorFlags);
unsafe_no_jsmanaged_fields!(ElementState);
unsafe_no_jsmanaged_fields!(DOMString);
unsafe_no_jsmanaged_fields!(Mime);

View file

@ -83,7 +83,7 @@ use parking_lot::RwLock;
use ref_filter_map::ref_filter_map;
use script_layout_interface::message::ReflowQueryType;
use script_thread::Runnable;
use selectors::matching::{ElementFlags, MatchingReason, matches};
use selectors::matching::{ElementSelectorFlags, matches};
use selectors::matching::{HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS};
use selectors::parser::{AttrSelector, NamespaceConstraint};
use servo_atoms::Atom;
@ -95,7 +95,6 @@ use std::default::Default;
use std::fmt;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use style::attr::{AttrValue, LengthOrPercentageOrAuto};
use style::context::{QuirksMode, ReflowGoal};
use style::element_state::*;
@ -109,6 +108,7 @@ use style::rule_tree::CascadeLevel;
use style::selector_parser::{NonTSPseudoClass, RestyleDamage, SelectorImpl, SelectorParser};
use style::sink::Push;
use style::stylist::ApplicableDeclarationBlock;
use style::thread_state;
use style::values::CSSFloat;
use style::values::specified::{self, CSSColor, CSSRGBA};
use stylesheet_loader::StylesheetOwner;
@ -131,7 +131,12 @@ pub struct Element {
attr_list: MutNullableJS<NamedNodeMap>,
class_list: MutNullableJS<DOMTokenList>,
state: Cell<ElementState>,
atomic_flags: AtomicElementFlags,
/// These flags are set by the style system to indicate the that certain
/// operations may require restyling this element or its descendants. The
/// flags are not atomic, so the style system takes care of only set them
/// when it has exclusive access to the element.
#[ignore_heap_size_of = "bitflags defined in rust-selectors"]
selector_flags: Cell<ElementSelectorFlags>,
}
impl fmt::Debug for Element {
@ -219,7 +224,7 @@ impl Element {
attr_list: Default::default(),
class_list: Default::default(),
state: Cell::new(state),
atomic_flags: AtomicElementFlags::new(),
selector_flags: Cell::new(ElementSelectorFlags::empty()),
}
}
@ -351,7 +356,8 @@ pub trait LayoutElementHelpers {
fn get_checked_state_for_layout(&self) -> bool;
fn get_indeterminate_state_for_layout(&self) -> bool;
fn get_state_for_layout(&self) -> ElementState;
fn insert_atomic_flags(&self, flags: ElementFlags);
fn insert_selector_flags(&self, flags: ElementSelectorFlags);
fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool;
}
impl LayoutElementHelpers for LayoutJS<Element> {
@ -720,9 +726,19 @@ impl LayoutElementHelpers for LayoutJS<Element> {
#[inline]
#[allow(unsafe_code)]
fn insert_atomic_flags(&self, flags: ElementFlags) {
fn insert_selector_flags(&self, flags: ElementSelectorFlags) {
debug_assert!(thread_state::get() == thread_state::LAYOUT);
unsafe {
(*self.unsafe_get()).atomic_flags.insert(flags);
let f = &(*self.unsafe_get()).selector_flags;
f.set(f.get() | flags);
}
}
#[inline]
#[allow(unsafe_code)]
fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
unsafe {
(*self.unsafe_get()).selector_flags.get().contains(flags)
}
}
}
@ -1973,7 +1989,7 @@ impl ElementMethods for Element {
match SelectorParser::parse_author_origin_no_namespace(&selectors) {
Err(()) => Err(Error::Syntax),
Ok(selectors) => {
Ok(matches(&selectors.0, &Root::from_ref(self), None, MatchingReason::Other))
Ok(matches(&selectors.0, &Root::from_ref(self), None))
}
}
}
@ -1991,7 +2007,8 @@ impl ElementMethods for Element {
let root = self.upcast::<Node>();
for element in root.inclusive_ancestors() {
if let Some(element) = Root::downcast::<Element>(element) {
if matches(&selectors.0, &element, None, MatchingReason::Other) {
if matches(&selectors.0, &element, None)
{
return Ok(Some(element));
}
}
@ -2231,7 +2248,7 @@ impl VirtualMethods for Element {
s.children_changed(mutation);
}
let flags = self.atomic_flags.get();
let flags = self.selector_flags.get();
if flags.intersects(HAS_SLOW_SELECTOR) {
// All children of this node need to be restyled when any child changes.
self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
@ -2744,24 +2761,6 @@ impl<'a> AttributeMutation<'a> {
}
}
/// Thread-safe wrapper for ElementFlags set during selector matching
#[derive(JSTraceable, HeapSizeOf)]
struct AtomicElementFlags(AtomicUsize);
impl AtomicElementFlags {
fn new() -> Self {
AtomicElementFlags(AtomicUsize::new(0))
}
fn get(&self) -> ElementFlags {
ElementFlags::from_bits_truncate(self.0.load(Ordering::Relaxed) as u8)
}
fn insert(&self, flags: ElementFlags) {
self.0.fetch_or(flags.bits() as usize, Ordering::Relaxed);
}
}
/// A holder for an element's "tag name", which will be lazily
/// resolved and cached. Should be reset when the document
/// owner changes.

View file

@ -67,7 +67,7 @@ use script_layout_interface::{LayoutElementType, LayoutNodeType, TrustedNodeAddr
use script_layout_interface::message::Msg;
use script_traits::DocumentActivity;
use script_traits::UntrustedNodeAddress;
use selectors::matching::{MatchingReason, matches};
use selectors::matching::matches;
use selectors::parser::SelectorList;
use servo_url::ServoUrl;
use std::borrow::ToOwned;
@ -322,7 +322,7 @@ impl<'a> Iterator for QuerySelectorIterator {
// (instead of passing `None`)? Probably.
self.iterator.by_ref().filter_map(|node| {
if let Some(element) = Root::downcast(node) {
if matches(selectors, &element, None, MatchingReason::Other) {
if matches(selectors, &element, None) {
return Some(Root::upcast(element));
}
}
@ -685,7 +685,7 @@ impl Node {
// Step 3.
Ok(selectors) => {
Ok(self.traverse_preorder().filter_map(Root::downcast).find(|element| {
matches(&selectors.0, element, None, MatchingReason::Other)
matches(&selectors.0, element, None)
}))
}
}

View file

@ -49,7 +49,7 @@ use script_layout_interface::{HTMLCanvasData, LayoutNodeType, SVGSVGData, Truste
use script_layout_interface::{OpaqueStyleAndLayoutData, PartialPersistentLayoutData};
use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, GetLayoutData, LayoutNode};
use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
use selectors::matching::ElementFlags;
use selectors::matching::ElementSelectorFlags;
use selectors::parser::{AttrSelector, NamespaceConstraint};
use servo_atoms::Atom;
use servo_url::ServoUrl;
@ -437,6 +437,14 @@ impl<'le> TElement for ServoLayoutElement<'le> {
fn skip_root_and_item_based_display_fixup(&self) -> bool {
false
}
unsafe fn set_selector_flags(&self, flags: ElementSelectorFlags) {
self.element.insert_selector_flags(flags);
}
fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
self.element.has_selector_flags(flags)
}
}
impl<'le> PartialEq for ServoLayoutElement<'le> {
@ -665,10 +673,6 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
self.element.html_element_in_html_document_for_layout()
}
}
fn insert_flags(&self, flags: ElementFlags) {
self.element.insert_atomic_flags(flags);
}
}
#[derive(Copy, Clone, Debug)]
@ -1009,6 +1013,10 @@ impl<'le> ThreadSafeLayoutElement for ServoThreadSafeLayoutElement<'le> {
self.as_node().type_id()
}
unsafe fn unsafe_get(self) -> ServoLayoutElement<'le> {
self.element
}
fn get_attr<'a>(&'a self, namespace: &Namespace, name: &LocalName) -> Option<&'a str> {
self.element.get_attr(namespace, name)
}

View file

@ -313,6 +313,15 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
/// Returns `None` if this is a pseudo-element; otherwise, returns `Some`.
fn type_id(&self) -> Option<LayoutNodeType>;
/// Returns access to the underlying TElement. This is breaks the abstraction
/// barrier of ThreadSafeLayout wrapper layer, and can lead to races if not used
/// carefully.
///
/// We need this so that the functions defined on this trait can call
/// lazily_compute_pseudo_element_style, which operates on TElement.
unsafe fn unsafe_get(self) ->
<<Self::ConcreteThreadSafeLayoutNode as ThreadSafeLayoutNode>::ConcreteNode as TNode>::ConcreteElement;
#[inline]
fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&str>;
@ -413,7 +422,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
let new_style =
context.stylist
.lazily_compute_pseudo_element_style(
self,
unsafe { &self.unsafe_get() },
&style_pseudo,
&data.styles().primary.values,
&context.default_computed_values);

View file

@ -1,7 +1,7 @@
[package]
name = "selectors"
version = "0.17.0"
version = "0.18.0" # Not yet published
authors = ["Simon Sapin <simon.sapin@exyr.org>", "Alan Jeffrey <ajeffrey@mozilla.com>"]
documentation = "https://docs.rs/selectors/"

View file

@ -7,27 +7,6 @@ use parser::{SimpleSelector, Selector, SelectorImpl};
use std::borrow::Borrow;
use tree::Element;
/// The reason why we're doing selector matching.
///
/// If this is for styling, this will include the flags in the parent element.
///
/// This is done because Servo doesn't need those flags at all when it's not
/// styling (e.g., when you're doing document.querySelector). For example, a
/// slow selector in an API like querySelector doesn't imply that the parent
/// could match it.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum MatchingReason {
ForStyling,
Other,
}
impl MatchingReason {
#[inline]
fn for_styling(&self) -> bool {
*self == MatchingReason::ForStyling
}
}
// The bloom filter for descendant CSS selectors will have a <1% false
// positive rate until it has this many selectors in it, then it will
// rapidly increase.
@ -84,23 +63,20 @@ bitflags! {
}
bitflags! {
/// Set of flags that are set on the parent depending on whether a child
/// could potentially match a selector.
///
/// These setters, in the case of Servo, must be atomic, due to the parallel
/// traversal.
pub flags ElementFlags: u8 {
/// When a child is added or removed from this element, all the children
/// Set of flags that are set on either the element or its parent (depending
/// on the flag) if the element could potentially match a selector.
pub flags ElementSelectorFlags: u8 {
/// When a child is added or removed from the parent, all the children
/// must be restyled, because they may match :nth-last-child,
/// :last-of-type, :nth-last-of-type, or :only-of-type.
const HAS_SLOW_SELECTOR = 1 << 0,
/// When a child is added or removed from this element, any later
/// When a child is added or removed from the parent, any later
/// children must be restyled, because they may match :nth-child,
/// :first-of-type, or :nth-of-type.
const HAS_SLOW_SELECTOR_LATER_SIBLINGS = 1 << 1,
/// When a child is added or removed from this element, the first and
/// When a child is added or removed from the parent, the first and
/// last children must be restyled, because they may match :first-child,
/// :last-child, or :only-child.
const HAS_EDGE_CHILD_SELECTOR = 1 << 2,
@ -111,16 +87,28 @@ bitflags! {
}
}
impl ElementSelectorFlags {
/// Returns the subset of flags that apply to the element.
pub fn for_self(self) -> ElementSelectorFlags {
self & (HAS_EMPTY_SELECTOR)
}
/// Returns the subset of flags that apply to the parent.
pub fn for_parent(self) -> ElementSelectorFlags {
self & (HAS_SLOW_SELECTOR | HAS_SLOW_SELECTOR_LATER_SIBLINGS | HAS_EDGE_CHILD_SELECTOR)
}
}
pub fn matches<E>(selector_list: &[Selector<E::Impl>],
element: &E,
parent_bf: Option<&BloomFilter>,
reason: MatchingReason)
parent_bf: Option<&BloomFilter>)
-> bool
where E: Element
{
selector_list.iter().any(|selector| {
selector.pseudo_element.is_none() &&
matches_complex_selector(&*selector.complex_selector, element, parent_bf, &mut StyleRelations::empty(), reason)
matches_complex_selector(&*selector.complex_selector, element, parent_bf,
&mut StyleRelations::empty(), &mut ElementSelectorFlags::empty())
})
}
@ -134,11 +122,11 @@ pub fn matches_complex_selector<E>(selector: &ComplexSelector<E::Impl>,
element: &E,
parent_bf: Option<&BloomFilter>,
relations: &mut StyleRelations,
reason: MatchingReason)
flags: &mut ElementSelectorFlags)
-> bool
where E: Element
{
match matches_complex_selector_internal(selector, element, parent_bf, relations, reason) {
match matches_complex_selector_internal(selector, element, parent_bf, relations, flags) {
SelectorMatchingResult::Matched => {
match selector.next {
Some((_, Combinator::NextSibling)) |
@ -209,12 +197,12 @@ fn can_fast_reject<E>(mut selector: &ComplexSelector<E::Impl>,
element: &E,
parent_bf: Option<&BloomFilter>,
relations: &mut StyleRelations,
reason: MatchingReason)
flags: &mut ElementSelectorFlags)
-> Option<SelectorMatchingResult>
where E: Element
{
if !selector.compound_selector.iter().all(|simple_selector| {
matches_simple_selector(simple_selector, element, parent_bf, relations, reason) }) {
matches_simple_selector(simple_selector, element, parent_bf, relations, flags) }) {
return Some(SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling);
}
@ -271,11 +259,11 @@ fn matches_complex_selector_internal<E>(selector: &ComplexSelector<E::Impl>,
element: &E,
parent_bf: Option<&BloomFilter>,
relations: &mut StyleRelations,
reason: MatchingReason)
flags: &mut ElementSelectorFlags)
-> SelectorMatchingResult
where E: Element
{
if let Some(result) = can_fast_reject(selector, element, parent_bf, relations, reason) {
if let Some(result) = can_fast_reject(selector, element, parent_bf, relations, flags) {
return result;
}
@ -302,7 +290,7 @@ fn matches_complex_selector_internal<E>(selector: &ComplexSelector<E::Impl>,
&element,
parent_bf,
relations,
reason);
flags);
match (result, combinator) {
// Return the status immediately.
(SelectorMatchingResult::Matched, _) => return result,
@ -346,7 +334,7 @@ fn matches_simple_selector<E>(
element: &E,
parent_bf: Option<&BloomFilter>,
relations: &mut StyleRelations,
reason: MatchingReason)
flags: &mut ElementSelectorFlags)
-> bool
where E: Element
{
@ -429,14 +417,14 @@ fn matches_simple_selector<E>(
AFFECTED_BY_STATE)
}
SimpleSelector::FirstChild => {
relation_if!(matches_first_child(element, reason), AFFECTED_BY_CHILD_INDEX)
relation_if!(matches_first_child(element, flags), AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::LastChild => {
relation_if!(matches_last_child(element, reason), AFFECTED_BY_CHILD_INDEX)
relation_if!(matches_last_child(element, flags), AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::OnlyChild => {
relation_if!(matches_first_child(element, reason) &&
matches_last_child(element, reason), AFFECTED_BY_CHILD_INDEX)
relation_if!(matches_first_child(element, flags) &&
matches_last_child(element, flags), AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::Root => {
// We never share styles with an element with no parent, so no point
@ -444,43 +432,41 @@ fn matches_simple_selector<E>(
element.is_root()
}
SimpleSelector::Empty => {
if reason.for_styling() {
element.insert_flags(HAS_EMPTY_SELECTOR);
}
flags.insert(HAS_EMPTY_SELECTOR);
relation_if!(element.is_empty(), AFFECTED_BY_EMPTY)
}
SimpleSelector::NthChild(a, b) => {
relation_if!(matches_generic_nth_child(element, a, b, false, false, reason),
relation_if!(matches_generic_nth_child(element, a, b, false, false, flags),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::NthLastChild(a, b) => {
relation_if!(matches_generic_nth_child(element, a, b, false, true, reason),
relation_if!(matches_generic_nth_child(element, a, b, false, true, flags),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::NthOfType(a, b) => {
relation_if!(matches_generic_nth_child(element, a, b, true, false, reason),
relation_if!(matches_generic_nth_child(element, a, b, true, false, flags),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::NthLastOfType(a, b) => {
relation_if!(matches_generic_nth_child(element, a, b, true, true, reason),
relation_if!(matches_generic_nth_child(element, a, b, true, true, flags),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::FirstOfType => {
relation_if!(matches_generic_nth_child(element, 0, 1, true, false, reason),
relation_if!(matches_generic_nth_child(element, 0, 1, true, false, flags),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::LastOfType => {
relation_if!(matches_generic_nth_child(element, 0, 1, true, true, reason),
relation_if!(matches_generic_nth_child(element, 0, 1, true, true, flags),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::OnlyOfType => {
relation_if!(matches_generic_nth_child(element, 0, 1, true, false, reason) &&
matches_generic_nth_child(element, 0, 1, true, true, reason),
relation_if!(matches_generic_nth_child(element, 0, 1, true, false, flags) &&
matches_generic_nth_child(element, 0, 1, true, true, flags),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::Negation(ref negated) => {
!negated.iter().all(|s| {
matches_complex_selector(s, element, parent_bf, relations, reason)
matches_complex_selector(s, element, parent_bf, relations, flags)
})
}
}
@ -492,22 +478,15 @@ fn matches_generic_nth_child<E>(element: &E,
b: i32,
is_of_type: bool,
is_from_end: bool,
reason: MatchingReason) -> bool
flags: &mut ElementSelectorFlags)
-> bool
where E: Element
{
// Selectors Level 4 changed from Level 3:
// This can match without a parent element:
// https://drafts.csswg.org/selectors-4/#child-index
if reason.for_styling() {
if let Some(parent) = element.parent_element() {
parent.insert_flags(if is_from_end {
flags.insert(if is_from_end {
HAS_SLOW_SELECTOR
} else {
HAS_SLOW_SELECTOR_LATER_SIBLINGS
});
}
}
let mut index = 1;
let mut next_sibling = if is_from_end {
@ -546,28 +525,15 @@ fn matches_generic_nth_child<E>(element: &E,
}
#[inline]
fn matches_first_child<E>(element: &E, reason: MatchingReason) -> bool where E: Element {
// Selectors Level 4 changed from Level 3:
// This can match without a parent element:
// https://drafts.csswg.org/selectors-4/#child-index
if reason.for_styling() {
if let Some(parent) = element.parent_element() {
parent.insert_flags(HAS_EDGE_CHILD_SELECTOR);
}
}
fn matches_first_child<E>(element: &E, flags: &mut ElementSelectorFlags)
-> bool where E: Element {
flags.insert(HAS_EDGE_CHILD_SELECTOR);
element.prev_sibling_element().is_none()
}
#[inline]
fn matches_last_child<E>(element: &E, reason: MatchingReason) -> bool where E: Element {
// Selectors Level 4 changed from Level 3:
// This can match without a parent element:
// https://drafts.csswg.org/selectors-4/#child-index
if reason.for_styling() {
if let Some(parent) = element.parent_element() {
parent.insert_flags(HAS_EDGE_CHILD_SELECTOR);
}
}
fn matches_last_child<E>(element: &E, flags: &mut ElementSelectorFlags)
-> bool where E: Element {
flags.insert(HAS_EDGE_CHILD_SELECTOR);
element.next_sibling_element().is_none()
}

View file

@ -5,7 +5,6 @@
//! Traits that nodes must implement. Breaks the otherwise-cyclic dependency between layout and
//! style.
use matching::ElementFlags;
use parser::{AttrSelector, SelectorImpl};
use std::ascii::AsciiExt;
@ -162,16 +161,4 @@ pub trait Element: MatchAttr + Sized {
// in the future when we have associated types and/or a more convenient
// JS GC story... --pcwalton
fn each_class<F>(&self, callback: F) where F: FnMut(&<Self::Impl as SelectorImpl>::ClassName);
/// Add flags to the element. See the `ElementFlags` docs for details.
///
/// This may be called while the element *or one of its children* is being
/// matched. Therefore the implementation must be thread-safe if children
/// may be matched in parallel.
fn insert_flags(&self, _flags: ElementFlags) {}
/// Clears the relevant ElementFlags. This is *not* called from
/// rust-selectors, but provided as part of the Element interface since it
/// makes sense.
fn clear_flags(&self) {}
}

View file

@ -9,12 +9,13 @@ use animation::Animation;
use app_units::Au;
use bloom::StyleBloom;
use data::ElementData;
use dom::{OpaqueNode, TNode, TElement};
use dom::{OpaqueNode, TNode, TElement, SendElement};
use error_reporting::ParseErrorReporter;
use euclid::Size2D;
use matching::StyleSharingCandidateCache;
use parking_lot::RwLock;
use properties::ComputedValues;
use selectors::matching::ElementSelectorFlags;
use servo_config::opts;
use std::collections::HashMap;
use std::env;
@ -23,6 +24,7 @@ use std::ops::Add;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::Sender;
use stylist::Stylist;
use thread_state;
use timer::Timer;
/// This structure is used to create a local style context from a shared one.
@ -165,6 +167,34 @@ impl TraversalStatistics {
}
}
/// A task to be run in sequential mode on the parent (non-worker) thread. This
/// is used by the style system to queue up work which is not safe to do during
/// the parallel traversal.
pub enum SequentialTask<E: TElement> {
/// Sets selector flags. This is used when we need to set flags on an
/// element that we don't have exclusive access to (i.e. the parent).
SetSelectorFlags(SendElement<E>, ElementSelectorFlags),
}
impl<E: TElement> SequentialTask<E> {
/// Executes this task.
pub fn execute(self) {
use self::SequentialTask::*;
debug_assert!(thread_state::get() == thread_state::LAYOUT);
match self {
SetSelectorFlags(el, flags) => {
unsafe { el.set_selector_flags(flags) };
}
}
}
/// Creates a task to set the selector flags on an element.
pub fn set_selector_flags(el: E, flags: ElementSelectorFlags) -> Self {
use self::SequentialTask::*;
SetSelectorFlags(unsafe { SendElement::new(el) }, flags)
}
}
/// A thread-local style context.
///
/// This context contains data that needs to be used during restyling, but is
@ -178,6 +208,10 @@ pub struct ThreadLocalStyleContext<E: TElement> {
/// A channel on which new animations that have been triggered by style
/// recalculation can be sent.
pub new_animations_sender: Sender<Animation>,
/// A set of tasks to be run (on the parent thread) in sequential mode after
/// the rest of the styling is complete. This is useful for infrequently-needed
/// non-threadsafe operations.
pub tasks: Vec<SequentialTask<E>>,
/// Statistics about the traversal.
pub statistics: TraversalStatistics,
/// Information related to the current element, non-None during processing.
@ -191,6 +225,7 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
style_sharing_candidate_cache: StyleSharingCandidateCache::new(),
bloom_filter: StyleBloom::new(),
new_animations_sender: shared.local_context_creation_data.lock().unwrap().new_animations_sender.clone(),
tasks: Vec::new(),
statistics: TraversalStatistics::default(),
current_element_info: None,
}
@ -222,10 +257,15 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
}
}
#[cfg(debug_assertions)]
impl<E: TElement> Drop for ThreadLocalStyleContext<E> {
fn drop(&mut self) {
debug_assert!(self.current_element_info.is_none());
// Execute any enqueued sequential tasks.
debug_assert!(thread_state::get() == thread_state::LAYOUT);
for task in self.tasks.drain(..) {
task.execute();
}
}
}

View file

@ -14,6 +14,7 @@ use element_state::ElementState;
use parking_lot::RwLock;
use properties::{ComputedValues, PropertyDeclarationBlock};
use selector_parser::{ElementExt, PreExistingComputedValues, PseudoElement};
use selectors::matching::ElementSelectorFlags;
use sink::Push;
use std::fmt;
use std::fmt::Debug;
@ -321,6 +322,19 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre
/// blockification on this element. (This function exists so that Gecko
/// native anonymous content can opt out of this style fixup.)
fn skip_root_and_item_based_display_fixup(&self) -> bool;
/// Sets selector flags, which indicate what kinds of selectors may have
/// matched on this element and therefore what kind of work may need to
/// be performed when DOM state changes.
///
/// This is unsafe, like all the flag-setting methods, because it's only safe
/// to call with exclusive access to the element. When setting flags on the
/// parent during parallel traversal, we use SequentialTask to queue up the
/// set to run after the threads join.
unsafe fn set_selector_flags(&self, flags: ElementSelectorFlags);
/// Returns true if the element has all the specified selector flags.
fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool;
}
/// TNode and TElement aren't Send because we want to be careful and explicit

View file

@ -46,6 +46,7 @@ use properties::PropertyDeclarationBlock;
use rule_tree::CascadeLevel as ServoCascadeLevel;
use selector_parser::{ElementExt, Snapshot};
use selectors::Element;
use selectors::matching::ElementSelectorFlags;
use selectors::parser::{AttrSelector, NamespaceConstraint};
use servo_url::ServoUrl;
use sink::Push;
@ -379,6 +380,29 @@ lazy_static! {
};
}
/// Converts flags from the layout used by rust-selectors to the layout used
/// by Gecko. We could align these and then do this without conditionals, but
/// it's probably not worth the trouble.
fn selector_flags_to_node_flags(flags: ElementSelectorFlags) -> u32 {
use gecko_bindings::structs::*;
use selectors::matching::*;
let mut gecko_flags = 0u32;
if flags.contains(HAS_SLOW_SELECTOR) {
gecko_flags |= NODE_HAS_SLOW_SELECTOR as u32;
}
if flags.contains(HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
gecko_flags |= NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS as u32;
}
if flags.contains(HAS_EDGE_CHILD_SELECTOR) {
gecko_flags |= NODE_HAS_EDGE_CHILD_SELECTOR as u32;
}
if flags.contains(HAS_EMPTY_SELECTOR) {
gecko_flags |= NODE_HAS_EMPTY_SELECTOR as u32;
}
gecko_flags
}
impl<'le> TElement for GeckoElement<'le> {
type ConcreteNode = GeckoNode<'le>;
@ -476,6 +500,16 @@ impl<'le> TElement for GeckoElement<'le> {
// are NAC handles both cases.
self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) != 0
}
unsafe fn set_selector_flags(&self, flags: ElementSelectorFlags) {
debug_assert!(!flags.is_empty());
self.set_flags(selector_flags_to_node_flags(flags));
}
fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
let node_flags = selector_flags_to_node_flags(flags);
(self.flags() & node_flags) == node_flags
}
}
impl<'le> PartialEq for GeckoElement<'le> {

View file

@ -12,7 +12,7 @@ use animation::{self, Animation, PropertyAnimation};
use atomic_refcell::AtomicRefMut;
use cache::LRUCache;
use cascade_info::CascadeInfo;
use context::{SharedStyleContext, StyleContext};
use context::{SequentialTask, SharedStyleContext, StyleContext};
use data::{ComputedStyle, ElementData, ElementStyles, PseudoRuleNodes, PseudoStyles};
use dom::{SendElement, TElement, TNode};
use properties::{CascadeFlags, ComputedValues, SHAREABLE, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
@ -22,7 +22,8 @@ use rule_tree::{CascadeLevel, StrongRuleNode};
use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
use selectors::MatchAttr;
use selectors::bloom::BloomFilter;
use selectors::matching::{AFFECTED_BY_PSEUDO_ELEMENTS, AFFECTED_BY_STYLE_ATTRIBUTE, MatchingReason, StyleRelations};
use selectors::matching::{AFFECTED_BY_PSEUDO_ELEMENTS, AFFECTED_BY_STYLE_ATTRIBUTE};
use selectors::matching::{ElementSelectorFlags, StyleRelations};
use servo_config::opts;
use sink::ForgetfulSink;
use std::collections::HashMap;
@ -586,6 +587,7 @@ pub trait MatchMethods : TElement {
let stylist = &context.shared.stylist;
let style_attribute = self.style_attribute();
let animation_rules = self.get_animation_rules(None);
let mut flags = ElementSelectorFlags::empty();
// Compute the primary rule node.
let mut primary_relations =
@ -595,7 +597,7 @@ pub trait MatchMethods : TElement {
animation_rules,
None,
&mut applicable_declarations,
MatchingReason::ForStyling);
&mut flags);
let primary_rule_node = compute_rule_node(context, &mut applicable_declarations);
// Compute the pseudo rule nodes.
@ -608,7 +610,7 @@ pub trait MatchMethods : TElement {
None, pseudo_animation_rules,
Some(&pseudo),
&mut applicable_declarations,
MatchingReason::ForStyling);
&mut flags);
if !applicable_declarations.is_empty() {
let rule_node = compute_rule_node(context, &mut applicable_declarations);
@ -621,6 +623,22 @@ pub trait MatchMethods : TElement {
primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
}
// Apply the selector flags.
let self_flags = flags.for_self();
if !self_flags.is_empty() {
unsafe { self.set_selector_flags(self_flags); }
}
let parent_flags = flags.for_parent();
if !parent_flags.is_empty() {
if let Some(p) = self.parent_element() {
// Avoid the overhead of the SequentialTask if the flags are already set.
if !p.has_selector_flags(parent_flags) {
let task = SequentialTask::set_selector_flags(p, parent_flags);
context.thread_local.tasks.push(task);
}
}
}
MatchResults {
primary: primary_rule_node,
relations: primary_relations,

View file

@ -15,7 +15,7 @@ use gecko_bindings::structs::nsRestyleHint;
use heapsize::HeapSizeOf;
use selector_parser::{AttrValue, NonTSPseudoClass, Snapshot, SelectorImpl};
use selectors::{Element, MatchAttr};
use selectors::matching::{MatchingReason, StyleRelations};
use selectors::matching::{ElementSelectorFlags, StyleRelations};
use selectors::matching::matches_complex_selector;
use selectors::parser::{AttrSelector, Combinator, ComplexSelector, SimpleSelector};
use std::clone::Clone;
@ -524,14 +524,16 @@ impl DependencySet {
(attrs_changed && dep.sensitivities.attrs),
"Testing a known ineffective dependency?");
if (attrs_changed || state_changes.intersects(dep.sensitivities.states)) && !hint.intersects(dep.hint) {
// We can ignore the selector flags, since they would have already been set during
// original matching for any element that might change its matching behavior here.
let matched_then =
matches_complex_selector(&dep.selector, snapshot, None,
&mut StyleRelations::empty(),
MatchingReason::Other);
&mut ElementSelectorFlags::empty());
let matches_now =
matches_complex_selector(&dep.selector, element, None,
&mut StyleRelations::empty(),
MatchingReason::Other);
&mut ElementSelectorFlags::empty());
if matched_then != matches_now {
hint.insert(dep.hint);
}

View file

@ -18,12 +18,12 @@ use properties::{self, CascadeFlags, ComputedValues, INHERIT_ALL};
use properties::PropertyDeclarationBlock;
use restyle_hints::{RestyleHint, DependencySet};
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
use selector_parser::{ElementExt, SelectorImpl, PseudoElement, Snapshot};
use selector_parser::{SelectorImpl, PseudoElement, Snapshot};
use selectors::Element;
use selectors::bloom::BloomFilter;
use selectors::matching::{AFFECTED_BY_ANIMATIONS, AFFECTED_BY_TRANSITIONS};
use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS};
use selectors::matching::{MatchingReason, StyleRelations, matches_complex_selector};
use selectors::matching::{ElementSelectorFlags, StyleRelations, matches_complex_selector};
use selectors::parser::{Selector, SimpleSelector, LocalName as LocalNameSelector, ComplexSelector};
use sink::Push;
use smallvec::VecLike;
@ -34,6 +34,7 @@ use std::hash::Hash;
use std::sync::Arc;
use style_traits::viewport::ViewportConstraints;
use stylesheets::{CssRule, Origin, StyleRule, Stylesheet, UserAgentStylesheets};
use thread_state;
use viewport::{self, MaybeNew, ViewportRule};
pub use ::fnv::FnvHashMap;
@ -368,7 +369,7 @@ impl Stylist {
parent: &Arc<ComputedValues>,
default: &Arc<ComputedValues>)
-> Option<ComputedStyle>
where E: ElementExt +
where E: TElement +
fmt::Debug +
PresentationalHintsSynthetizer
{
@ -379,13 +380,14 @@ impl Stylist {
let mut declarations = vec![];
let mut flags = ElementSelectorFlags::empty();
self.push_applicable_declarations(element,
None,
None,
AnimationRules(None, None),
Some(pseudo),
&mut declarations,
MatchingReason::ForStyling);
&mut flags);
let rule_node =
self.rule_tree.insert_ordered_rules(
@ -400,6 +402,28 @@ impl Stylist {
Box::new(StdoutErrorReporter),
CascadeFlags::empty());
// Apply the selector flags. We should be in sequential mode already,
// so we can directly apply the parent flags.
if cfg!(feature = "servo") {
// Servo calls this function from the worker, but only for internal
// pseudos, so we should never generate selector flags here.
debug_assert!(flags.is_empty());
} else {
// Gecko calls this from sequential mode, so we can directly apply
// the flags.
debug_assert!(thread_state::get() == thread_state::LAYOUT);
let self_flags = flags.for_self();
if !self_flags.is_empty() {
unsafe { element.set_selector_flags(self_flags); }
}
let parent_flags = flags.for_parent();
if !parent_flags.is_empty() {
if let Some(p) = element.parent_element() {
unsafe { p.set_selector_flags(parent_flags); }
}
}
}
Some(ComputedStyle::new(rule_node, Arc::new(computed)))
}
@ -495,8 +519,8 @@ impl Stylist {
animation_rules: AnimationRules,
pseudo_element: Option<&PseudoElement>,
applicable_declarations: &mut V,
reason: MatchingReason) -> StyleRelations
where E: ElementExt +
flags: &mut ElementSelectorFlags) -> StyleRelations
where E: TElement +
fmt::Debug +
PresentationalHintsSynthetizer,
V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock>
@ -521,7 +545,7 @@ impl Stylist {
parent_bf,
applicable_declarations,
&mut relations,
reason,
flags,
CascadeLevel::UANormal);
debug!("UA normal: {:?}", relations);
@ -545,14 +569,14 @@ impl Stylist {
parent_bf,
applicable_declarations,
&mut relations,
reason,
flags,
CascadeLevel::UserNormal);
debug!("user normal: {:?}", relations);
map.author.get_all_matching_rules(element,
parent_bf,
applicable_declarations,
&mut relations,
reason,
flags,
CascadeLevel::AuthorNormal);
debug!("author normal: {:?}", relations);
@ -586,7 +610,7 @@ impl Stylist {
parent_bf,
applicable_declarations,
&mut relations,
reason,
flags,
CascadeLevel::AuthorImportant);
debug!("author important: {:?}", relations);
@ -609,7 +633,7 @@ impl Stylist {
parent_bf,
applicable_declarations,
&mut relations,
reason,
flags,
CascadeLevel::UserImportant);
debug!("user important: {:?}", relations);
@ -622,7 +646,7 @@ impl Stylist {
parent_bf,
applicable_declarations,
&mut relations,
reason,
flags,
CascadeLevel::UAImportant);
debug!("UA important: {:?}", relations);
@ -663,7 +687,7 @@ impl Stylist {
pub fn match_same_not_common_style_affecting_attributes_rules<E>(&self,
element: &E,
candidate: &E) -> bool
where E: ElementExt,
where E: TElement,
{
use selectors::matching::StyleRelations;
use selectors::matching::matches_complex_selector;
@ -678,11 +702,11 @@ impl Stylist {
let element_matches =
matches_complex_selector(&selector.complex_selector, element,
None, &mut StyleRelations::empty(),
MatchingReason::Other);
&mut ElementSelectorFlags::empty());
let candidate_matches =
matches_complex_selector(&selector.complex_selector, candidate,
None, &mut StyleRelations::empty(),
MatchingReason::Other);
&mut ElementSelectorFlags::empty());
if element_matches != candidate_matches {
return false;
@ -704,7 +728,7 @@ impl Stylist {
pub fn match_same_sibling_affecting_rules<E>(&self,
element: &E,
candidate: &E) -> bool
where E: ElementExt,
where E: TElement,
{
use selectors::matching::StyleRelations;
use selectors::matching::matches_complex_selector;
@ -717,12 +741,12 @@ impl Stylist {
let element_matches =
matches_complex_selector(&selector.complex_selector, element,
None, &mut StyleRelations::empty(),
MatchingReason::Other);
&mut ElementSelectorFlags::empty());
let candidate_matches =
matches_complex_selector(&selector.complex_selector, candidate,
None, &mut StyleRelations::empty(),
MatchingReason::Other);
&mut ElementSelectorFlags::empty());
if element_matches != candidate_matches {
debug!("match_same_sibling_affecting_rules: Failure due to {:?}",
@ -860,7 +884,7 @@ impl SelectorMap {
parent_bf: Option<&BloomFilter>,
matching_rules_list: &mut V,
relations: &mut StyleRelations,
reason: MatchingReason,
flags: &mut ElementSelectorFlags,
cascade_level: CascadeLevel)
where E: Element<Impl=SelectorImpl>,
V: VecLike<ApplicableDeclarationBlock>
@ -878,7 +902,7 @@ impl SelectorMap {
&id,
matching_rules_list,
relations,
reason,
flags,
cascade_level)
}
@ -889,7 +913,7 @@ impl SelectorMap {
class,
matching_rules_list,
relations,
reason,
flags,
cascade_level);
});
@ -904,7 +928,7 @@ impl SelectorMap {
element.get_local_name(),
matching_rules_list,
relations,
reason,
flags,
cascade_level);
SelectorMap::get_matching_rules(element,
@ -912,7 +936,7 @@ impl SelectorMap {
&self.other_rules,
matching_rules_list,
relations,
reason,
flags,
cascade_level);
// Sort only the rules we just added.
@ -963,7 +987,7 @@ impl SelectorMap {
key: &BorrowedStr,
matching_rules: &mut Vector,
relations: &mut StyleRelations,
reason: MatchingReason,
flags: &mut ElementSelectorFlags,
cascade_level: CascadeLevel)
where E: Element<Impl=SelectorImpl>,
Str: Borrow<BorrowedStr> + Eq + Hash,
@ -976,7 +1000,7 @@ impl SelectorMap {
rules,
matching_rules,
relations,
reason,
flags,
cascade_level)
}
}
@ -987,7 +1011,7 @@ impl SelectorMap {
rules: &[Rule],
matching_rules: &mut V,
relations: &mut StyleRelations,
reason: MatchingReason,
flags: &mut ElementSelectorFlags,
cascade_level: CascadeLevel)
where E: Element<Impl=SelectorImpl>,
V: VecLike<ApplicableDeclarationBlock>
@ -1002,7 +1026,7 @@ impl SelectorMap {
};
if any_declaration_for_importance &&
matches_complex_selector(&*rule.selector, element, parent_bf,
relations, reason) {
relations, flags) {
matching_rules.push(
rule.to_applicable_declaration_block(cascade_level));
}

View file

@ -31,10 +31,10 @@ macro_rules! sizeof_checker (
// Update the sizes here
sizeof_checker!(size_event_target, EventTarget, 40);
sizeof_checker!(size_node, Node, 152);
sizeof_checker!(size_element, Element, 320);
sizeof_checker!(size_htmlelement, HTMLElement, 336);
sizeof_checker!(size_div, HTMLDivElement, 336);
sizeof_checker!(size_span, HTMLSpanElement, 336);
sizeof_checker!(size_element, Element, 312);
sizeof_checker!(size_htmlelement, HTMLElement, 328);
sizeof_checker!(size_div, HTMLDivElement, 328);
sizeof_checker!(size_span, HTMLSpanElement, 328);
sizeof_checker!(size_text, Text, 184);
sizeof_checker!(size_characterdata, CharacterData, 184);
sizeof_checker!(size_servothreadsafelayoutnode, ServoThreadSafeLayoutNode, 16);