Auto merge of #19024 - emilio:document, r=KiChjang

Introduce style::dom::TDocument.

The secret plan is introducing a `get_elements_with_id` for use by `querySelector` / `querySelectorAll`.

But this allows also to make some code look a bit nicer.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/19024)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-10-26 07:51:21 -05:00 committed by GitHub
commit ee4e371c73
6 changed files with 117 additions and 47 deletions

View file

@ -51,7 +51,7 @@ use script_layout_interface::{OpaqueStyleAndLayoutData, StyleData};
use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, GetLayoutData, LayoutNode};
use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint, CaseSensitivity};
use selectors::matching::{ElementSelectorFlags, MatchingContext, RelevantLinkStatus};
use selectors::matching::{ElementSelectorFlags, MatchingContext, QuirksMode, RelevantLinkStatus};
use selectors::matching::VisitedHandlingMode;
use selectors::sink::Push;
use servo_arc::{Arc, ArcBorrow};
@ -69,7 +69,7 @@ use style::computed_values::display;
use style::context::SharedStyleContext;
use style::data::ElementData;
use style::dom::{DomChildren, LayoutIterator, NodeInfo, OpaqueNode};
use style::dom::{TElement, TNode};
use style::dom::{TDocument, TElement, TNode};
use style::element_state::*;
use style::font_metrics::ServoMetricsProvider;
use style::properties::{ComputedValues, PropertyDeclarationBlock};
@ -139,10 +139,6 @@ impl<'ln> ServoLayoutNode<'ln> {
self.node.type_id_for_layout()
}
}
pub fn as_document(&self) -> Option<ServoLayoutDocument<'ln>> {
self.node.downcast().map(ServoLayoutDocument::from_layout_js)
}
}
impl<'ln> NodeInfo for ServoLayoutNode<'ln> {
@ -158,6 +154,7 @@ impl<'ln> NodeInfo for ServoLayoutNode<'ln> {
}
impl<'ln> TNode for ServoLayoutNode<'ln> {
type ConcreteDocument = ServoLayoutDocument<'ln>;
type ConcreteElement = ServoLayoutElement<'ln>;
fn parent_node(&self) -> Option<Self> {
@ -190,6 +187,10 @@ impl<'ln> TNode for ServoLayoutNode<'ln> {
}
}
fn owner_doc(&self) -> Self::ConcreteDocument {
ServoLayoutDocument::from_layout_js(unsafe { self.node.owner_doc_for_layout() })
}
fn traversal_parent(&self) -> Option<ServoLayoutElement<'ln>> {
self.parent_element()
}
@ -206,6 +207,10 @@ impl<'ln> TNode for ServoLayoutNode<'ln> {
as_element(self.node)
}
fn as_document(&self) -> Option<ServoLayoutDocument<'ln>> {
self.node.downcast().map(ServoLayoutDocument::from_layout_js)
}
fn can_be_fragmented(&self) -> bool {
unsafe { self.node.get_flag(CAN_BE_FRAGMENTED) }
}
@ -287,11 +292,23 @@ pub struct ServoLayoutDocument<'ld> {
chain: PhantomData<&'ld ()>,
}
impl<'ld> ServoLayoutDocument<'ld> {
fn as_node(&self) -> ServoLayoutNode<'ld> {
impl<'ld> TDocument for ServoLayoutDocument<'ld> {
type ConcreteNode = ServoLayoutNode<'ld>;
fn as_node(&self) -> Self::ConcreteNode {
ServoLayoutNode::from_layout_js(self.document.upcast())
}
fn quirks_mode(&self) -> QuirksMode {
unsafe { self.document.quirks_mode() }
}
fn is_html_document(&self) -> bool {
unsafe { self.document.is_html_document_for_layout() }
}
}
impl<'ld> ServoLayoutDocument<'ld> {
pub fn root_element(&self) -> Option<ServoLayoutElement<'ld>> {
self.as_node().dom_children().flat_map(|n| n.as_element()).next()
}
@ -773,8 +790,12 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
fn is_html_element_in_html_document(&self) -> bool {
unsafe {
self.element.html_element_in_html_document_for_layout()
if !self.element.is_html_element() {
return false;
}
}
self.as_node().owner_doc().is_html_document()
}
}

View file

@ -446,7 +446,7 @@ pub trait LayoutElementHelpers {
#[allow(unsafe_code)]
unsafe fn get_rowspan(self) -> u32;
#[allow(unsafe_code)]
unsafe fn html_element_in_html_document_for_layout(&self) -> bool;
unsafe fn is_html_element(&self) -> bool;
fn id_attribute(&self) -> *const Option<Atom>;
fn style_attribute(&self) -> *const Option<Arc<Locked<PropertyDeclarationBlock>>>;
fn local_name(&self) -> &LocalName;
@ -781,11 +781,8 @@ impl LayoutElementHelpers for LayoutDom<Element> {
#[inline]
#[allow(unsafe_code)]
unsafe fn html_element_in_html_document_for_layout(&self) -> bool {
if (*self.unsafe_get()).namespace != ns!(html) {
return false;
}
self.upcast::<Node>().owner_doc_for_layout().is_html_document_for_layout()
unsafe fn is_html_element(&self) -> bool {
(*self.unsafe_get()).namespace == ns!(html)
}
#[allow(unsafe_code)]

View file

@ -22,7 +22,7 @@ use properties::{AnimationRules, ComputedValues, PropertyDeclarationBlock};
use rule_tree::CascadeLevel;
use selector_parser::{AttrValue, PseudoClassStringArg, PseudoElement, SelectorImpl};
use selectors::Element as SelectorsElement;
use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode};
use selectors::matching::{ElementSelectorFlags, QuirksMode, VisitedHandlingMode};
use selectors::sink::Push;
use servo_arc::{Arc, ArcBorrow};
use shared_lock::Locked;
@ -133,12 +133,30 @@ where
}
}
/// The `TDocument` trait, to represent a document node.
pub trait TDocument : Sized + Copy + Clone {
/// The concrete `TNode` type.
type ConcreteNode: TNode<ConcreteDocument = Self>;
/// Get this document as a `TNode`.
fn as_node(&self) -> Self::ConcreteNode;
/// Returns whether this document is an HTML document.
fn is_html_document(&self) -> bool;
/// Returns the quirks mode of this document.
fn quirks_mode(&self) -> QuirksMode;
}
/// The `TNode` trait. This is the main generic trait over which the style
/// system can be implemented.
pub trait TNode : Sized + Copy + Clone + Debug + NodeInfo + PartialEq {
/// The concrete `TElement` type.
type ConcreteElement: TElement<ConcreteNode = Self>;
/// The concrete `TDocument` type.
type ConcreteDocument: TDocument<ConcreteNode = Self>;
/// Get this node's parent node.
fn parent_node(&self) -> Option<Self>;
@ -154,6 +172,9 @@ pub trait TNode : Sized + Copy + Clone + Debug + NodeInfo + PartialEq {
/// Get this node's next sibling.
fn next_sibling(&self) -> Option<Self>;
/// Get the owner document of this node.
fn owner_doc(&self) -> Self::ConcreteDocument;
/// Iterate over the DOM children of a node.
fn dom_children(&self) -> DomChildren<Self> {
DomChildren(self.first_child())
@ -211,6 +232,9 @@ pub trait TNode : Sized + Copy + Clone + Debug + NodeInfo + PartialEq {
/// Get this node as an element, if it's one.
fn as_element(&self) -> Option<Self::ConcreteElement>;
/// Get this node as a document, if it's one.
fn as_document(&self) -> Option<Self::ConcreteDocument>;
/// Whether this node can be fragmented. This is used for multicol, and only
/// for Servo.
fn can_be_fragmented(&self) -> bool;

View file

@ -6,7 +6,7 @@
//! and Gecko.
use context::QuirksMode;
use dom::{TElement, TNode};
use dom::{TDocument, TElement, TNode};
use invalidation::element::invalidator::{Invalidation, InvalidationProcessor, InvalidationVector};
use selectors::{Element, NthIndexCache, SelectorList};
use selectors::matching::{self, MatchingContext, MatchingMode};
@ -308,7 +308,6 @@ pub fn query_selector<E, Q>(
root: E::ConcreteNode,
selector_list: &SelectorList<E::Impl>,
results: &mut Q::Output,
quirks_mode: QuirksMode,
)
where
E: TElement,
@ -316,6 +315,7 @@ where
{
use invalidation::element::invalidator::TreeStyleInvalidator;
let quirks_mode = root.owner_doc().quirks_mode();
let fast_result = query_selector_fast::<E, Q>(
root,
selector_list,

View file

@ -20,7 +20,7 @@ use applicable_declarations::ApplicableDeclarationBlock;
use atomic_refcell::{AtomicRefCell, AtomicRef, AtomicRefMut};
use context::{QuirksMode, SharedStyleContext, PostAnimationTasks, UpdateAnimationsTasks};
use data::ElementData;
use dom::{LayoutIterator, NodeInfo, OpaqueNode, TElement, TNode};
use dom::{LayoutIterator, NodeInfo, OpaqueNode, TElement, TDocument, TNode};
use element_state::{ElementState, DocumentState, NS_DOCUMENT_STATE_WINDOW_INACTIVE};
use error_reporting::ParseErrorReporter;
use font_metrics::{FontMetrics, FontMetricsProvider, FontMetricsQueryResult};
@ -92,6 +92,26 @@ use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
use stylesheets::UrlExtraData;
use stylist::Stylist;
/// A simple wrapper over `nsIDocument`.
#[derive(Clone, Copy)]
pub struct GeckoDocument<'ld>(pub &'ld structs::nsIDocument);
impl<'ld> TDocument for GeckoDocument<'ld> {
type ConcreteNode = GeckoNode<'ld>;
fn as_node(&self) -> Self::ConcreteNode {
GeckoNode(&self.0._base)
}
fn is_html_document(&self) -> bool {
self.0.mType == structs::root::nsIDocument_Type::eHTML
}
fn quirks_mode(&self) -> QuirksMode {
self.0.mCompatMode.into()
}
}
/// A simple wrapper over a non-null Gecko node (`nsINode`) pointer.
///
/// Important: We don't currently refcount the DOM, because the wrapper lifetime
@ -124,6 +144,13 @@ impl<'ln> fmt::Debug for GeckoNode<'ln> {
}
impl<'ln> GeckoNode<'ln> {
#[inline]
fn is_document(&self) -> bool {
// This is a DOM constant that isn't going to change.
const DOCUMENT_NODE: u16 = 9;
self.node_info().mInner.mNodeType == DOCUMENT_NODE
}
#[inline]
fn from_content(content: &'ln nsIContent) -> Self {
GeckoNode(&content._base)
@ -153,22 +180,11 @@ impl<'ln> GeckoNode<'ln> {
(self.0).mBoolFlags
}
/// Owner document quirks mode getter.
#[inline]
pub fn owner_document_quirks_mode(&self) -> QuirksMode {
self.owner_doc().mCompatMode.into()
}
#[inline]
fn get_bool_flag(&self, flag: nsINode_BooleanFlag) -> bool {
self.bool_flags() & (1u32 << flag as u32) != 0
}
fn owner_doc(&self) -> &structs::nsIDocument {
debug_assert!(!self.node_info().mDocument.is_null());
unsafe { &*self.node_info().mDocument }
}
/// WARNING: This logic is duplicated in Gecko's FlattenedTreeParentIsParent.
/// Make sure to mirror any modifications in both places.
fn flattened_tree_parent_is_parent(&self) -> bool {
@ -223,6 +239,7 @@ impl<'ln> NodeInfo for GeckoNode<'ln> {
}
impl<'ln> TNode for GeckoNode<'ln> {
type ConcreteDocument = GeckoDocument<'ln>;
type ConcreteElement = GeckoElement<'ln>;
fn parent_node(&self) -> Option<Self> {
@ -249,6 +266,12 @@ impl<'ln> TNode for GeckoNode<'ln> {
unsafe { self.0.mNextSibling.as_ref().map(GeckoNode::from_content) }
}
#[inline]
fn owner_doc(&self) -> Self::ConcreteDocument {
debug_assert!(!self.node_info().mDocument.is_null());
GeckoDocument(unsafe { &*self.node_info().mDocument })
}
fn traversal_parent(&self) -> Option<GeckoElement<'ln>> {
self.flattened_tree_parent().and_then(|n| n.as_element())
}
@ -271,6 +294,15 @@ impl<'ln> TNode for GeckoNode<'ln> {
}
}
#[inline]
fn as_document(&self) -> Option<Self::ConcreteDocument> {
if self.is_document() {
Some(self.owner_doc())
} else {
None
}
}
fn can_be_fragmented(&self) -> bool {
// FIXME(SimonSapin): Servo uses this to implement CSS multicol / fragmentation
// Maybe this isnt useful for Gecko?
@ -596,7 +628,7 @@ impl<'le> GeckoElement<'le> {
fn document_state(&self) -> DocumentState {
let node = self.as_node();
unsafe {
let states = Gecko_DocumentState(node.owner_doc());
let states = Gecko_DocumentState(node.owner_doc().0);
DocumentState::from_bits_truncate(states)
}
}
@ -632,13 +664,7 @@ impl<'le> GeckoElement<'le> {
#[inline]
fn get_document_theme(&self) -> DocumentTheme {
let node = self.as_node();
unsafe { Gecko_GetDocumentLWTheme(node.owner_doc()) }
}
/// Owner document quirks mode getter.
#[inline]
pub fn owner_document_quirks_mode(&self) -> QuirksMode {
self.as_node().owner_document_quirks_mode()
unsafe { Gecko_GetDocumentLWTheme(node.owner_doc().0) }
}
/// Only safe to call on the main thread, with exclusive access to the element and
@ -949,7 +975,7 @@ impl<'le> TElement for GeckoElement<'le> {
}
fn owner_doc_matches_for_testing(&self, device: &Device) -> bool {
self.as_node().owner_doc() as *const structs::nsIDocument ==
self.as_node().owner_doc().0 as *const structs::nsIDocument ==
device.pres_context().mDocument.raw::<structs::nsIDocument>()
}
@ -1584,7 +1610,7 @@ impl<'le> TElement for GeckoElement<'le> {
if self.get_local_name().as_ptr() == atom!("th").as_ptr() {
hints.push(TH_RULE.clone());
} else if self.get_local_name().as_ptr() == atom!("table").as_ptr() &&
self.as_node().owner_doc().mCompatMode == structs::nsCompatibility::eCompatibility_NavQuirks {
self.as_node().owner_doc().quirks_mode() == QuirksMode::Quirks {
hints.push(TABLE_COLOR_RULE.clone());
}
}
@ -2031,7 +2057,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
fn is_html_element_in_html_document(&self) -> bool {
self.is_html_element() &&
self.as_node().owner_doc().mType == structs::root::nsIDocument_Type::eHTML
self.as_node().owner_doc().is_html_document()
}
fn ignores_nth_child_selectors(&self) -> bool {

View file

@ -19,7 +19,7 @@ use style::applicable_declarations::ApplicableDeclarationBlock;
use style::context::{CascadeInputs, QuirksMode, SharedStyleContext, StyleContext};
use style::context::ThreadLocalStyleContext;
use style::data::{ElementStyles, self};
use style::dom::{ShowSubtreeData, TElement, TNode};
use style::dom::{ShowSubtreeData, TDocument, TElement, TNode};
use style::driver;
use style::element_state::{DocumentState, ElementState};
use style::error_reporting::{NullReporter, ParseErrorReporter};
@ -1577,7 +1577,9 @@ pub extern "C" fn Servo_StyleRule_SelectorMatchesElement(rule: RawServoStyleRule
};
let element = GeckoElement(element);
let mut ctx = MatchingContext::new(matching_mode, None, None, element.owner_document_quirks_mode());
let quirks_mode = element.as_node().owner_doc().quirks_mode();
let mut ctx =
MatchingContext::new(matching_mode, None, None, quirks_mode);
matches_selector(selector, 0, None, &element, &mut ctx, &mut |_, _| {})
})
}
@ -1591,9 +1593,10 @@ pub unsafe extern "C" fn Servo_SelectorList_Closest(
use style::dom_apis;
let element = GeckoElement(element);
let quirks_mode = element.as_node().owner_doc().quirks_mode();
let selectors = ::selectors::SelectorList::from_ffi(selectors).borrow();
dom_apis::element_closest(element, &selectors, element.owner_document_quirks_mode())
dom_apis::element_closest(element, &selectors, quirks_mode)
.map_or(ptr::null(), |e| e.0)
}
@ -1606,11 +1609,12 @@ pub unsafe extern "C" fn Servo_SelectorList_Matches(
use style::dom_apis;
let element = GeckoElement(element);
let quirks_mode = element.as_node().owner_doc().quirks_mode();
let selectors = ::selectors::SelectorList::from_ffi(selectors).borrow();
dom_apis::element_matches(
&element,
&selectors,
element.owner_document_quirks_mode(),
quirks_mode,
)
}
@ -1629,7 +1633,6 @@ pub unsafe extern "C" fn Servo_SelectorList_QueryFirst(
node,
&selectors,
&mut result,
node.owner_document_quirks_mode(),
);
result.map_or(ptr::null(), |e| e.0)
@ -1653,7 +1656,6 @@ pub unsafe extern "C" fn Servo_SelectorList_QueryAll(
node,
&selectors,
&mut result,
node.owner_document_quirks_mode(),
);
if !result.is_empty() {