Bug 1317016 - Basic infrastructure for RestyleHint-driven traversal.

MozReview-Commit-ID: 7wH5XcILVmX
This commit is contained in:
Bobby Holley 2016-11-01 23:11:24 -07:00
parent e1eff691f8
commit 992f7dddf4
35 changed files with 1465 additions and 901 deletions

View file

@ -5,13 +5,12 @@
#![allow(unsafe_code)]
use atomic_refcell::{AtomicRef, AtomicRefCell};
use atomic_refcell::AtomicRefCell;
use data::ElementData;
use dom::{LayoutIterator, NodeInfo, TElement, TNode, UnsafeNode};
use dom::{OpaqueNode, PresentationalHintsSynthetizer};
use element_state::ElementState;
use error_reporting::StdoutErrorReporter;
use gecko::restyle_damage::GeckoRestyleDamage;
use gecko::selector_parser::{SelectorImpl, NonTSPseudoClass, PseudoElement};
use gecko::snapshot_helpers;
use gecko_bindings::bindings;
@ -20,20 +19,19 @@ use gecko_bindings::bindings::{Gecko_ElementState, Gecko_GetLastChild, Gecko_Get
use gecko_bindings::bindings::{Gecko_GetServoDeclarationBlock, Gecko_IsHTMLElementInHTMLDocument};
use gecko_bindings::bindings::{Gecko_IsLink, Gecko_IsRootElement};
use gecko_bindings::bindings::{Gecko_IsUnvisitedLink, Gecko_IsVisitedLink, Gecko_Namespace};
use gecko_bindings::bindings::{Gecko_SetNodeFlags, Gecko_UnsetNodeFlags};
use gecko_bindings::bindings::{RawGeckoElement, RawGeckoNode};
use gecko_bindings::bindings::Gecko_ClassOrClassList;
use gecko_bindings::bindings::Gecko_GetStyleContext;
use gecko_bindings::bindings::Gecko_SetNodeFlags;
use gecko_bindings::bindings::Gecko_StoreStyleDifference;
use gecko_bindings::structs;
use gecko_bindings::structs::{NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO, NODE_IS_DIRTY_FOR_SERVO};
use gecko_bindings::structs::{nsIAtom, nsIContent, nsStyleContext};
use gecko_bindings::structs::NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO;
use gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE;
use parking_lot::RwLock;
use parser::ParserContextExtraData;
use properties::{ComputedValues, parse_style_attribute};
use properties::PropertyDeclarationBlock;
use selector_parser::ElementExt;
use selector_parser::{ElementExt, Snapshot};
use selectors::Element;
use selectors::parser::{AttrSelector, NamespaceConstraint};
use servo_url::ServoUrl;
@ -61,6 +59,22 @@ impl<'ln> GeckoNode<'ln> {
debug_assert!(!self.0.mNodeInfo.mRawPtr.is_null());
unsafe { &*self.0.mNodeInfo.mRawPtr }
}
fn first_child(&self) -> Option<GeckoNode<'ln>> {
unsafe { self.0.mFirstChild.as_ref().map(GeckoNode::from_content) }
}
fn last_child(&self) -> Option<GeckoNode<'ln>> {
unsafe { Gecko_GetLastChild(self.0).map(GeckoNode) }
}
fn prev_sibling(&self) -> Option<GeckoNode<'ln>> {
unsafe { self.0.mPreviousSibling.as_ref().map(GeckoNode::from_content) }
}
fn next_sibling(&self) -> Option<GeckoNode<'ln>> {
unsafe { self.0.mNextSibling.as_ref().map(GeckoNode::from_content) }
}
}
impl<'ln> NodeInfo for GeckoNode<'ln> {
@ -115,14 +129,6 @@ impl<'ln> TNode for GeckoNode<'ln> {
OpaqueNode(ptr)
}
fn layout_parent_element(self, reflow_root: OpaqueNode) -> Option<GeckoElement<'ln>> {
if self.opaque() == reflow_root {
None
} else {
self.parent_node().and_then(|x| x.as_element())
}
}
fn debug_id(self) -> usize {
unimplemented!()
}
@ -146,24 +152,8 @@ impl<'ln> TNode for GeckoNode<'ln> {
// Maybe this isnt useful for Gecko?
}
fn parent_node(&self) -> Option<GeckoNode<'ln>> {
unsafe { self.0.mParent.as_ref().map(GeckoNode) }
}
fn first_child(&self) -> Option<GeckoNode<'ln>> {
unsafe { self.0.mFirstChild.as_ref().map(GeckoNode::from_content) }
}
fn last_child(&self) -> Option<GeckoNode<'ln>> {
unsafe { Gecko_GetLastChild(self.0).map(GeckoNode) }
}
fn prev_sibling(&self) -> Option<GeckoNode<'ln>> {
unsafe { self.0.mPreviousSibling.as_ref().map(GeckoNode::from_content) }
}
fn next_sibling(&self) -> Option<GeckoNode<'ln>> {
unsafe { self.0.mNextSibling.as_ref().map(GeckoNode::from_content) }
fn parent_node(&self) -> Option<Self> {
unsafe { bindings::Gecko_GetParentNode(self.0).map(GeckoNode) }
}
fn needs_dirty_on_viewport_size_changed(&self) -> bool {
@ -221,7 +211,7 @@ impl<'le> fmt::Debug for GeckoElement<'le> {
if let Some(id) = self.get_id() {
try!(write!(f, " id={}", id));
}
write!(f, ">")
write!(f, "> ({:?})", self.0 as *const _)
}
}
@ -251,11 +241,16 @@ impl<'le> GeckoElement<'le> {
unsafe { Gecko_SetNodeFlags(self.as_node().0, flags) }
}
fn unset_flags(&self, flags: u32) {
unsafe { Gecko_UnsetNodeFlags(self.as_node().0, flags) }
}
pub fn clear_data(&self) {
let ptr = self.raw_node().mServoData.get();
let ptr = self.0.mServoData.get();
if !ptr.is_null() {
let data = unsafe { Box::from_raw(self.raw_node().mServoData.get()) };
self.raw_node().mServoData.set(ptr::null_mut());
debug!("Dropping ElementData for {:?}", self);
let data = unsafe { Box::from_raw(self.0.mServoData.get()) };
self.0.mServoData.set(ptr::null_mut());
// Perform a mutable borrow of the data in debug builds. This
// serves as an assertion that there are no outstanding borrows
@ -265,20 +260,31 @@ impl<'le> GeckoElement<'le> {
}
pub fn get_pseudo_style(&self, pseudo: &PseudoElement) -> Option<Arc<ComputedValues>> {
self.borrow_data().and_then(|data| data.current_styles().pseudos
.get(pseudo).map(|c| c.0.clone()))
// NB: Gecko sometimes resolves pseudos after an element has already been
// marked for restyle. We should consider fixing this, but for now just allow
// it with current_or_previous_styles.
self.borrow_data().and_then(|data| data.current_or_previous_styles().pseudos
.get(pseudo).map(|c| c.values.clone()))
}
pub fn ensure_data(&self) -> &AtomicRefCell<ElementData> {
// Only safe to call with exclusive access to the element.
pub unsafe fn ensure_data(&self) -> &AtomicRefCell<ElementData> {
match self.get_data() {
Some(x) => x,
None => {
let ptr = Box::into_raw(Box::new(AtomicRefCell::new(ElementData::new())));
self.raw_node().mServoData.set(ptr);
debug!("Creating ElementData for {:?}", self);
let existing = self.get_styles_from_frame();
let ptr = Box::into_raw(Box::new(AtomicRefCell::new(ElementData::new(existing))));
self.0.mServoData.set(ptr);
unsafe { &* ptr }
},
}
}
/// Creates a blank snapshot for this element.
pub fn create_snapshot(&self) -> Snapshot {
Snapshot::new(*self)
}
}
lazy_static! {
@ -325,14 +331,6 @@ impl<'le> TElement for GeckoElement<'le> {
}
}
fn set_restyle_damage(self, damage: GeckoRestyleDamage) {
// FIXME(bholley): Gecko currently relies on the dirty bit being set to
// drive the post-traversal. This will go away soon.
unsafe { self.set_flags(NODE_IS_DIRTY_FOR_SERVO as u32) }
unsafe { Gecko_StoreStyleDifference(self.as_node().0, damage.as_change_hint()) }
}
fn existing_style_for_restyle_damage<'a>(&'a self,
current_cv: Option<&'a Arc<ComputedValues>>,
pseudo: Option<&PseudoElement>)
@ -350,16 +348,7 @@ impl<'le> TElement for GeckoElement<'le> {
}
}
fn deprecated_dirty_bit_is_set(&self) -> bool {
self.flags() & (NODE_IS_DIRTY_FOR_SERVO as u32) != 0
}
fn has_dirty_descendants(&self) -> bool {
// Return true unconditionally if we're not yet styled. This is a hack
// and should go away soon.
if self.get_data().is_none() {
return true;
}
self.flags() & (NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32) != 0
}
@ -367,6 +356,10 @@ impl<'le> TElement for GeckoElement<'le> {
self.set_flags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32)
}
unsafe fn unset_dirty_descendants(&self) {
self.unset_flags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32)
}
fn store_children_to_process(&self, _: isize) {
// This is only used for bottom-up traversal, and is thus a no-op for Gecko.
}
@ -375,14 +368,9 @@ impl<'le> TElement for GeckoElement<'le> {
panic!("Atomic child count not implemented in Gecko");
}
fn borrow_data(&self) -> Option<AtomicRef<ElementData>> {
self.get_data().map(|x| x.borrow())
}
fn get_data(&self) -> Option<&AtomicRefCell<ElementData>> {
unsafe { self.raw_node().mServoData.get().as_ref() }
unsafe { self.0.mServoData.get().as_ref() }
}
}
impl<'le> PartialEq for GeckoElement<'le> {
@ -401,8 +389,7 @@ impl<'le> PresentationalHintsSynthetizer for GeckoElement<'le> {
impl<'le> ::selectors::Element for GeckoElement<'le> {
fn parent_element(&self) -> Option<Self> {
let parent = self.as_node().parent_node();
parent.and_then(|parent| parent.as_element())
unsafe { bindings::Gecko_GetParentElement(self.0).map(GeckoElement) }
}
fn first_child_element(&self) -> Option<Self> {