mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
Bug 1336646 - Apply selector flags during traversal. r=emilio
This commit is contained in:
parent
37b8d5231d
commit
9e860df9df
17 changed files with 295 additions and 192 deletions
18
Cargo.lock
generated
18
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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/"
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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) {}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue