diff --git a/components/layout_thread/dom_wrapper.rs b/components/layout_thread/dom_wrapper.rs index 6433b9f163d..ebf7d12cbae 100644 --- a/components/layout_thread/dom_wrapper.rs +++ b/components/layout_thread/dom_wrapper.rs @@ -213,6 +213,10 @@ impl<'ln> TNode for ServoLayoutNode<'ln> { unsafe { self.node.get_flag(NodeFlags::CAN_BE_FRAGMENTED) } } + fn is_in_document(&self) -> bool { + unsafe { self.node.get_flag(NodeFlags::IS_IN_DOC) } + } + unsafe fn set_can_be_fragmented(&self, value: bool) { self.node.set_flag(NodeFlags::CAN_BE_FRAGMENTED, value) } @@ -417,7 +421,7 @@ impl<'le> TElement for ServoLayoutElement<'le> { } unsafe fn set_dirty_descendants(&self) { - debug_assert!(self.as_node().node.get_flag(NodeFlags::IS_IN_DOC)); + debug_assert!(self.as_node().is_in_document()); self.as_node().node.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true) } diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs index c7ca5927e96..3d6eef88696 100644 --- a/components/selectors/matching.rs +++ b/components/selectors/matching.rs @@ -62,6 +62,7 @@ struct LocalMatchingContext<'a, 'b: 'a, Impl: SelectorImpl> { matches_hover_and_active_quirk: bool, } +#[inline(always)] pub fn matches_selector_list( selector_list: &SelectorList, element: &E, @@ -373,6 +374,7 @@ where } /// Matches a complex selector. +#[inline(always)] pub fn matches_complex_selector( mut iter: SelectorIter, element: &E, @@ -385,8 +387,8 @@ where { // If this is the special pseudo-element mode, consume the ::pseudo-element // before proceeding, since the caller has already handled that part. - if context.nesting_level == 0 && - context.matching_mode == MatchingMode::ForStatelessPseudoElement { + if context.matching_mode == MatchingMode::ForStatelessPseudoElement && + context.nesting_level == 0 { // Consume the pseudo. match *iter.next().unwrap() { Component::PseudoElement(ref pseudo) => { @@ -571,12 +573,12 @@ where // Only ancestor combinators are allowed while looking for // relevant links, so switch to not looking. *relevant_link = RelevantLinkStatus::NotLooking; - SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant + SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant } Combinator::Child | Combinator::Descendant | Combinator::PseudoElement => { - SelectorMatchingResult::NotMatchedGlobally + SelectorMatchingResult::NotMatchedGlobally } }; diff --git a/components/style/dom.rs b/components/style/dom.rs index 721625e33a9..b1a12307311 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -122,6 +122,7 @@ where { type Item = N; + #[inline] fn next(&mut self) -> Option { let prev = match self.previous.take() { None => return None, @@ -146,6 +147,18 @@ pub trait TDocument : Sized + Copy + Clone { /// Returns the quirks mode of this document. fn quirks_mode(&self) -> QuirksMode; + + /// Get a list of elements with a given ID in this document, sorted by + /// document position. + /// + /// Can return an error to signal that this list is not available, or also + /// return an empty slice. + fn elements_with_id( + &self, + _id: &Atom, + ) -> Result<&[::ConcreteElement], ()> { + Err(()) + } } /// The `TNode` trait. This is the main generic trait over which the style @@ -180,6 +193,9 @@ pub trait TNode : Sized + Copy + Clone + Debug + NodeInfo + PartialEq { DomChildren(self.first_child()) } + /// Returns whether the node is attached to a document. + fn is_in_document(&self) -> bool; + /// Iterate over the DOM children of a node, in preorder. fn dom_descendants(&self) -> DomDescendants { DomDescendants { @@ -190,6 +206,7 @@ pub trait TNode : Sized + Copy + Clone + Debug + NodeInfo + PartialEq { /// Returns the next children in pre-order, optionally scoped to a subtree /// root. + #[inline] fn next_in_preorder(&self, scoped_to: Option) -> Option { if let Some(c) = self.first_child() { return Some(c); diff --git a/components/style/dom_apis.rs b/components/style/dom_apis.rs index 495cfa95096..8f6ea273aa0 100644 --- a/components/style/dom_apis.rs +++ b/components/style/dom_apis.rs @@ -5,12 +5,16 @@ //! Generic implementations of some DOM APIs so they can be shared between Servo //! and Gecko. +use Atom; use context::QuirksMode; use dom::{TDocument, TElement, TNode}; use invalidation::element::invalidator::{Invalidation, InvalidationProcessor, InvalidationVector}; use selectors::{Element, NthIndexCache, SelectorList}; +use selectors::attr::CaseSensitivity; use selectors::matching::{self, MatchingContext, MatchingMode}; +use selectors::parser::{Combinator, Component, LocalName}; use smallvec::SmallVec; +use std::borrow::Borrow; /// pub fn element_matches( @@ -218,13 +222,103 @@ where } } -/// Fast paths for a given selector query. +/// Returns whether a given element is descendant of a given `root` node. /// -/// FIXME(emilio, nbp): This may very well be a good candidate for code to be -/// replaced by HolyJit :) -fn query_selector_fast( +/// NOTE(emilio): if root == element, this returns false. +fn element_is_descendant_of(element: E, root: E::ConcreteNode) -> bool +where + E: TElement, +{ + if element.as_node().is_in_document() && root == root.owner_doc().as_node() { + return true; + } + + let mut current = element.as_node().parent_node(); + while let Some(n) = current.take() { + if n == root { + return true; + } + + current = n.parent_node(); + } + false +} + +/// Fast path for iterating over every element with a given id in the document +/// that `root` is connected to. +fn fast_connected_elements_with_id<'a, D>( + doc: &'a D, + root: D::ConcreteNode, + id: &Atom, + quirks_mode: QuirksMode, +) -> Result<&'a [::ConcreteElement], ()> +where + D: TDocument, +{ + debug_assert_eq!(root.owner_doc().as_node(), doc.as_node()); + + let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity(); + if case_sensitivity != CaseSensitivity::CaseSensitive { + return Err(()); + } + + if !root.is_in_document() { + return Err(()); + } + + doc.elements_with_id(id) +} + +/// Collects elements with a given id under `root`, that pass `filter`. +fn collect_elements_with_id( root: E::ConcreteNode, - selector_list: &SelectorList, + id: &Atom, + results: &mut Q::Output, + quirks_mode: QuirksMode, + mut filter: F, +) +where + E: TElement, + Q: SelectorQuery, + F: FnMut(E) -> bool, +{ + let doc = root.owner_doc(); + let elements = match fast_connected_elements_with_id(&doc, root, id, quirks_mode) { + Ok(elements) => elements, + Err(()) => { + let case_sensitivity = + quirks_mode.classes_and_ids_case_sensitivity(); + + collect_all_elements::(root, results, |e| { + e.has_id(id, case_sensitivity) && filter(e) + }); + + return; + } + }; + + for element in elements { + // If the element is not an actual descendant of the root, even though + // it's connected, we don't really care about it. + if !element_is_descendant_of(*element, root) { + continue; + } + + if !filter(*element) { + continue; + } + + Q::append_element(results, *element); + if Q::should_stop_after_first_match() { + break; + } + } +} + +/// Fast paths for querySelector with a single simple selector. +fn query_selector_single_query( + root: E::ConcreteNode, + component: &Component, results: &mut Q::Output, quirks_mode: QuirksMode, ) -> Result<(), ()> @@ -232,36 +326,18 @@ where E: TElement, Q: SelectorQuery, { - use selectors::parser::{Component, LocalName}; - use std::borrow::Borrow; - - // We need to return elements in document order, and reordering them - // afterwards is kinda silly. - if selector_list.0.len() > 1 { - return Err(()); - } - - let selector = &selector_list.0[0]; - - // Let's just care about the easy cases for now. - // - // FIXME(emilio): Blink has a fast path for classes in ancestor combinators - // that may be worth stealing. - if selector.len() > 1 { - return Err(()); - } - - let component = selector.iter().next().unwrap(); match *component { Component::ExplicitUniversalType => { collect_all_elements::(root, results, |_| true) } Component::ID(ref id) => { - // TODO(emilio): We may want to reuse Gecko's document ID table. - let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity(); - collect_all_elements::(root, results, |element| { - element.has_id(id, case_sensitivity) - }) + collect_elements_with_id::( + root, + id, + results, + quirks_mode, + |_| true, + ); } Component::Class(ref class) => { let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity(); @@ -287,6 +363,139 @@ where Ok(()) } +/// Fast paths for a given selector query. +/// +/// FIXME(emilio, nbp): This may very well be a good candidate for code to be +/// replaced by HolyJit :) +fn query_selector_fast( + root: E::ConcreteNode, + selector_list: &SelectorList, + results: &mut Q::Output, + matching_context: &mut MatchingContext, +) -> Result<(), ()> +where + E: TElement, + Q: SelectorQuery, +{ + // We need to return elements in document order, and reordering them + // afterwards is kinda silly. + if selector_list.0.len() > 1 { + return Err(()); + } + + let selector = &selector_list.0[0]; + let quirks_mode = matching_context.quirks_mode(); + + // Let's just care about the easy cases for now. + if selector.len() == 1 { + return query_selector_single_query::( + root, + selector.iter().next().unwrap(), + results, + quirks_mode, + ); + } + + let mut iter = selector.iter(); + let mut combinator: Option = None; + + loop { + debug_assert!(combinator.map_or(true, |c| !c.is_sibling())); + + 'component_loop: for component in &mut iter { + match *component { + Component::ID(ref id) => { + if combinator.is_none() { + // In the rightmost compound, just find descendants of + // root that match the selector list with that id. + collect_elements_with_id::( + root, + id, + results, + quirks_mode, + |e| { + matching::matches_selector_list( + selector_list, + &e, + matching_context, + ) + } + ); + + return Ok(()); + } + + let doc = root.owner_doc(); + let elements = + fast_connected_elements_with_id(&doc, root, id, quirks_mode)?; + + if elements.is_empty() { + return Ok(()); + } + + // Results need to be in document order. Let's not bother + // reordering or deduplicating nodes, which we would need to + // do if one element with the given id were a descendant of + // another element with that given id. + if !Q::should_stop_after_first_match() && elements.len() > 1 { + continue; + } + + for element in elements { + // If the element is not a descendant of the root, then + // it may have descendants that match our selector that + // _are_ descendants of the root, and other descendants + // that match our selector that are _not_. + // + // So we can't just walk over the element's descendants + // and match the selector against all of them, nor can + // we skip looking at this element's descendants. + // + // Give up on trying to optimize based on this id and + // keep walking our selector. + if !element_is_descendant_of(*element, root) { + continue 'component_loop; + } + + query_selector_slow::( + element.as_node(), + selector_list, + results, + matching_context, + ); + + if Q::should_stop_after_first_match() && !Q::is_empty(&results) { + break; + } + } + + return Ok(()); + } + _ => {}, + } + } + + loop { + let next_combinator = match iter.next_sequence() { + None => return Err(()), + Some(c) => c, + }; + + // We don't want to scan stuff affected by sibling combinators, + // given we scan the subtree of elements with a given id (and we + // don't want to care about scanning the siblings' subtrees). + if next_combinator.is_sibling() { + // Advance to the next combinator. + for _ in &mut iter {} + continue; + } + + combinator = Some(next_combinator); + break; + } + } +} + // Slow path for a given selector query. fn query_selector_slow( root: E::ConcreteNode, @@ -303,11 +512,21 @@ where }); } +/// Whether the invalidation machinery should be used for this query. +#[derive(PartialEq)] +pub enum MayUseInvalidation { + /// We may use it if we deem it useful. + Yes, + /// Don't use it. + No, +} + /// pub fn query_selector( root: E::ConcreteNode, selector_list: &SelectorList, results: &mut Q::Output, + may_use_invalidation: MayUseInvalidation, ) where E: TElement, @@ -316,21 +535,7 @@ where use invalidation::element::invalidator::TreeStyleInvalidator; let quirks_mode = root.owner_doc().quirks_mode(); - let fast_result = query_selector_fast::( - root, - selector_list, - results, - quirks_mode, - ); - if fast_result.is_ok() { - return; - } - - // Slow path: Use the invalidation machinery if we're a root, and tree - // traversal otherwise. - // - // See the comment in collect_invalidations to see why only if we're a root. let mut nth_index_cache = NthIndexCache::default(); let mut matching_context = MatchingContext::new( MatchingMode::Normal, @@ -342,12 +547,32 @@ where let root_element = root.as_element(); matching_context.scope_element = root_element.map(|e| e.opaque()); + let fast_result = query_selector_fast::( + root, + selector_list, + results, + &mut matching_context, + ); + + if fast_result.is_ok() { + return; + } + + // Slow path: Use the invalidation machinery if we're a root, and tree + // traversal otherwise. + // + // See the comment in collect_invalidations to see why only if we're a root. + // // The invalidation mechanism is only useful in presence of combinators. // // We could do that check properly here, though checking the length of the // selectors is a good heuristic. + // + // A selector with a combinator needs to have a length of at least 3: A + // simple selector, a combinator, and another simple selector. let invalidation_may_be_useful = - selector_list.0.iter().any(|s| s.len() > 1); + may_use_invalidation == MayUseInvalidation::Yes && + selector_list.0.iter().any(|s| s.len() > 2); if root_element.is_some() || !invalidation_may_be_useful { query_selector_slow::( diff --git a/components/style/gecko/generated/bindings.rs b/components/style/gecko/generated/bindings.rs index 995cfad5d54..f9aee3fb7a7 100644 --- a/components/style/gecko/generated/bindings.rs +++ b/components/style/gecko/generated/bindings.rs @@ -30,6 +30,7 @@ use gecko_bindings::structs::RawGeckoComputedTiming; use gecko_bindings::structs::RawGeckoCSSPropertyIDList; use gecko_bindings::structs::RawGeckoDocument; use gecko_bindings::structs::RawGeckoElement; +use gecko_bindings::structs::Element; use gecko_bindings::structs::RawGeckoKeyframeList; use gecko_bindings::structs::RawGeckoPropertyValuePairList; use gecko_bindings::structs::RawGeckoComputedKeyframeValuesList; @@ -1210,9 +1211,9 @@ extern "C" { } extern "C" { pub fn Servo_SelectorList_Closest ( arg1 : RawGeckoElementBorrowed , arg2 : RawServoSelectorListBorrowed , ) -> * const RawGeckoElement ; } extern "C" { - pub fn Servo_SelectorList_QueryFirst ( arg1 : RawGeckoNodeBorrowed , arg2 : RawServoSelectorListBorrowed , ) -> * const RawGeckoElement ; + pub fn Servo_SelectorList_QueryFirst ( arg1 : RawGeckoNodeBorrowed , arg2 : RawServoSelectorListBorrowed , may_use_invalidation : bool , ) -> * const RawGeckoElement ; } extern "C" { - pub fn Servo_SelectorList_QueryAll ( arg1 : RawGeckoNodeBorrowed , arg2 : RawServoSelectorListBorrowed , content_list : * mut nsSimpleContentList , ) ; + pub fn Servo_SelectorList_QueryAll ( arg1 : RawGeckoNodeBorrowed , arg2 : RawServoSelectorListBorrowed , content_list : * mut nsSimpleContentList , may_use_invalidation : bool , ) ; } extern "C" { pub fn Servo_StyleSet_AddSizeOfExcludingThis ( malloc_size_of : MallocSizeOf , malloc_enclosing_size_of : MallocSizeOf , sizes : * mut ServoStyleSetSizes , set : RawServoStyleSetBorrowed , ) ; } extern "C" { @@ -1225,6 +1226,8 @@ extern "C" { pub fn Servo_StyleSet_MightHaveAttributeDependency ( set : RawServoStyleSetBorrowed , element : RawGeckoElementBorrowed , local_name : * mut nsAtom , ) -> bool ; } extern "C" { pub fn Servo_StyleSet_HasStateDependency ( set : RawServoStyleSetBorrowed , element : RawGeckoElementBorrowed , state : u64 , ) -> bool ; +} extern "C" { + pub fn Servo_StyleSet_HasDocumentStateDependency ( set : RawServoStyleSetBorrowed , state : u64 , ) -> bool ; } extern "C" { pub fn Servo_CssRules_ListTypes ( rules : ServoCssRulesBorrowed , result : nsTArrayBorrowed_uintptr_t , ) ; } extern "C" { @@ -1573,4 +1576,6 @@ extern "C" { pub fn Gecko_ReportUnexpectedCSSError ( reporter : * mut ErrorReporter , message : * const :: std :: os :: raw :: c_char , param : * const :: std :: os :: raw :: c_char , paramLen : u32 , prefix : * const :: std :: os :: raw :: c_char , prefixParam : * const :: std :: os :: raw :: c_char , prefixParamLen : u32 , suffix : * const :: std :: os :: raw :: c_char , source : * const :: std :: os :: raw :: c_char , sourceLen : u32 , lineNumber : u32 , colNumber : u32 , ) ; } extern "C" { pub fn Gecko_ContentList_AppendAll ( aContentList : * mut nsSimpleContentList , aElements : * mut * const RawGeckoElement , aLength : usize , ) ; +} extern "C" { + pub fn Gecko_GetElementsWithId ( aDocument : * const nsIDocument , aId : * mut nsAtom , ) -> * const nsTArray < * mut Element > ; } \ No newline at end of file diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 91a9578c912..f25b93b8dec 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -110,6 +110,26 @@ impl<'ld> TDocument for GeckoDocument<'ld> { fn quirks_mode(&self) -> QuirksMode { self.0.mCompatMode.into() } + + fn elements_with_id(&self, id: &Atom) -> Result<&[GeckoElement<'ld>], ()> { + unsafe { + let array = bindings::Gecko_GetElementsWithId(self.0, id.as_ptr()); + if array.is_null() { + return Ok(&[]); + } + + let elements: &[*mut RawGeckoElement] = &**array; + + // NOTE(emilio): We rely on the in-memory representation of + // GeckoElement<'ld> and *mut RawGeckoElement being the same. + #[allow(dead_code)] + unsafe fn static_assert() { + mem::transmute::<*mut RawGeckoElement, GeckoElement<'static>>(0xbadc0de as *mut _); + } + + Ok(mem::transmute(elements)) + } + } } /// A simple wrapper over a non-null Gecko node (`nsINode`) pointer. @@ -242,6 +262,7 @@ impl<'ln> TNode for GeckoNode<'ln> { type ConcreteDocument = GeckoDocument<'ln>; type ConcreteElement = GeckoElement<'ln>; + #[inline] fn parent_node(&self) -> Option { unsafe { self.0.mParent.as_ref().map(GeckoNode) } } @@ -272,6 +293,11 @@ impl<'ln> TNode for GeckoNode<'ln> { GeckoDocument(unsafe { &*self.node_info().mDocument }) } + #[inline] + fn is_in_document(&self) -> bool { + self.get_bool_flag(nsINode_BooleanFlag::IsInDocument) + } + fn traversal_parent(&self) -> Option> { self.flattened_tree_parent().and_then(|n| n.as_element()) } @@ -1742,10 +1768,12 @@ impl<'le> Hash for GeckoElement<'le> { impl<'le> ::selectors::Element for GeckoElement<'le> { type Impl = SelectorImpl; + #[inline] fn opaque(&self) -> OpaqueElement { OpaqueElement::new(self.0) } + #[inline] fn parent_element(&self) -> Option { // FIXME(emilio): This will need to jump across if the parent node is a // shadow root to get the shadow host. @@ -1758,6 +1786,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { self.closest_non_native_anonymous_ancestor() } + #[inline] fn first_child_element(&self) -> Option { let mut child = self.as_node().first_child(); while let Some(child_node) = child { @@ -1769,6 +1798,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { None } + #[inline] fn last_child_element(&self) -> Option { let mut child = self.as_node().last_child(); while let Some(child_node) = child { @@ -1780,6 +1810,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { None } + #[inline] fn prev_sibling_element(&self) -> Option { let mut sibling = self.as_node().prev_sibling(); while let Some(sibling_node) = sibling { @@ -1791,6 +1822,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { None } + #[inline] fn next_sibling_element(&self) -> Option { let mut sibling = self.as_node().next_sibling(); while let Some(sibling_node) = sibling { @@ -1882,12 +1914,14 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { }) } + #[inline] fn get_local_name(&self) -> &WeakAtom { unsafe { WeakAtom::new(self.as_node().node_info().mInner.mName) } } + #[inline] fn get_namespace(&self) -> &WeakNamespace { unsafe { WeakNamespace::new(Gecko_Namespace(self.0)) @@ -2054,6 +2088,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { self.get_state().intersects(NonTSPseudoClass::AnyLink.state_flag()) } + #[inline] fn has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool { if !self.has_id() { return false @@ -2070,6 +2105,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { } } + #[inline] fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { if !self.may_have_class() { return false; @@ -2081,15 +2117,18 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { Gecko_ClassOrClassList) } + #[inline] fn is_html_element_in_html_document(&self) -> bool { self.is_html_element() && self.as_node().owner_doc().is_html_document() } + #[inline] fn ignores_nth_child_selectors(&self) -> bool { self.is_root_of_anonymous_subtree() } + #[inline] fn blocks_ancestor_combinators(&self) -> bool { if !self.is_root_of_anonymous_subtree() { return false diff --git a/components/style/invalidation/element/invalidator.rs b/components/style/invalidation/element/invalidator.rs index 79052a31f9e..07b9787de00 100644 --- a/components/style/invalidation/element/invalidator.rs +++ b/components/style/invalidation/element/invalidator.rs @@ -317,7 +317,7 @@ where fn invalidate_pseudo_element_or_nac( &mut self, child: E, - invalidations: &InvalidationVector<'b>, + invalidations: &[Invalidation<'b>], ) -> bool { let mut sibling_invalidations = InvalidationVector::new(); @@ -342,7 +342,7 @@ where fn invalidate_child( &mut self, child: E, - invalidations: &InvalidationVector<'b>, + invalidations: &[Invalidation<'b>], sibling_invalidations: &mut InvalidationVector<'b>, ) -> bool { let mut invalidations_for_descendants = InvalidationVector::new(); @@ -389,7 +389,7 @@ where fn invalidate_nac( &mut self, - invalidations: &InvalidationVector<'b>, + invalidations: &[Invalidation<'b>], ) -> bool { let mut any_nac_root = false; @@ -407,7 +407,7 @@ where fn invalidate_dom_descendants_of( &mut self, parent: E::ConcreteNode, - invalidations: &InvalidationVector<'b>, + invalidations: &[Invalidation<'b>], ) -> bool { let mut any_descendant = false; @@ -441,7 +441,7 @@ where /// descendants, and invalidate style on them. fn invalidate_descendants( &mut self, - invalidations: &InvalidationVector<'b>, + invalidations: &[Invalidation<'b>], ) -> bool { if invalidations.is_empty() { return false; @@ -546,7 +546,7 @@ where /// Returns whether our style was invalidated as a result. fn process_descendant_invalidations( &mut self, - invalidations: &InvalidationVector<'b>, + invalidations: &[Invalidation<'b>], descendant_invalidations: &mut InvalidationVector<'b>, sibling_invalidations: &mut InvalidationVector<'b>, ) -> bool { diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index cdf98577558..bb443057b6d 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -1693,17 +1693,27 @@ pub unsafe extern "C" fn Servo_SelectorList_Matches( pub unsafe extern "C" fn Servo_SelectorList_QueryFirst( node: RawGeckoNodeBorrowed, selectors: RawServoSelectorListBorrowed, + may_use_invalidation: bool, ) -> *const structs::RawGeckoElement { use std::borrow::Borrow; - use style::dom_apis::{self, QueryFirst}; + use style::dom_apis::{self, MayUseInvalidation, QueryFirst}; let node = GeckoNode(node); let selectors = ::selectors::SelectorList::from_ffi(selectors).borrow(); let mut result = None; + + let may_use_invalidation = + if may_use_invalidation { + MayUseInvalidation::Yes + } else { + MayUseInvalidation::No + }; + dom_apis::query_selector::( node, &selectors, &mut result, + may_use_invalidation, ); result.map_or(ptr::null(), |e| e.0) @@ -1714,19 +1724,28 @@ pub unsafe extern "C" fn Servo_SelectorList_QueryAll( node: RawGeckoNodeBorrowed, selectors: RawServoSelectorListBorrowed, content_list: *mut structs::nsSimpleContentList, + may_use_invalidation: bool, ) { use smallvec::SmallVec; use std::borrow::Borrow; - use style::dom_apis::{self, QueryAll}; + use style::dom_apis::{self, MayUseInvalidation, QueryAll}; let node = GeckoNode(node); let selectors = ::selectors::SelectorList::from_ffi(selectors).borrow(); let mut result = SmallVec::new(); + let may_use_invalidation = + if may_use_invalidation { + MayUseInvalidation::Yes + } else { + MayUseInvalidation::No + }; + dom_apis::query_selector::( node, &selectors, &mut result, + may_use_invalidation, ); if !result.is_empty() {