Auto merge of #16778 - emilio:snapshots, r=bholley

Take all the snapshots into account in the style system

See [bug 1355343](https://bugzilla.mozilla.org/show_bug.cgi?id=1355343).

The servo part of this patch presumably needs some polishing, let's see.

<!-- 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/16778)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-05-10 15:08:59 -05:00 committed by GitHub
commit c8171ed5d7
22 changed files with 2194 additions and 1137 deletions

12
Cargo.lock generated
View file

@ -164,13 +164,13 @@ dependencies = [
[[package]] [[package]]
name = "bindgen" name = "bindgen"
version = "0.24.0" version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", "aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clang-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "clang-sys 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.20.5 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.20.5 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
@ -357,7 +357,7 @@ dependencies = [
[[package]] [[package]]
name = "clang-sys" name = "clang-sys"
version = "0.16.0" version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2784,7 +2784,7 @@ version = "0.0.1"
dependencies = [ dependencies = [
"app_units 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "app_units 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bindgen 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)", "bindgen 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3383,7 +3383,7 @@ dependencies = [
"checksum base64 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "979d348dc50dfcd050a87df408ec61f01a0a27ee9b4ebdc6085baba8275b2c7f" "checksum base64 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "979d348dc50dfcd050a87df408ec61f01a0a27ee9b4ebdc6085baba8275b2c7f"
"checksum binary-space-partition 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "df65281d9b2b5c332f5bfbd9bb5e5f2e62f627c259cf9dc9cd10fecb758be33d" "checksum binary-space-partition 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "df65281d9b2b5c332f5bfbd9bb5e5f2e62f627c259cf9dc9cd10fecb758be33d"
"checksum bincode 1.0.0-alpha6 (registry+https://github.com/rust-lang/crates.io-index)" = "fb0cdeac1c5d567fdb487ae5853c024e4acf1ea85ba6a6552fe084e0805fea5d" "checksum bincode 1.0.0-alpha6 (registry+https://github.com/rust-lang/crates.io-index)" = "fb0cdeac1c5d567fdb487ae5853c024e4acf1ea85ba6a6552fe084e0805fea5d"
"checksum bindgen 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)" = "21a1de90068c1e58dd31b71daab70e1a1e54212b43cc6c4714e7c8acefb28992" "checksum bindgen 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ccaf8958532d7e570e905266ee2dc1094c3e5c3c3cfc2c299368747a30a5e654"
"checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c" "checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c"
"checksum bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5b97c2c8e8bbb4251754f559df8af22fb264853c7d009084a576cdf12565089d" "checksum bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5b97c2c8e8bbb4251754f559df8af22fb264853c7d009084a576cdf12565089d"
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
@ -3402,7 +3402,7 @@ dependencies = [
"checksum cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393a5f0088efbe41f9d1fcd062f24e83c278608420e62109feb2c8abee07de7d" "checksum cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393a5f0088efbe41f9d1fcd062f24e83c278608420e62109feb2c8abee07de7d"
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c" "checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
"checksum cgl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "86765cb42c2a2c497e142af72517c1b4d7ae5bb2f25dfa77a5c69642f2342d89" "checksum cgl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "86765cb42c2a2c497e142af72517c1b4d7ae5bb2f25dfa77a5c69642f2342d89"
"checksum clang-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5f4f6aa0c4cfa318cd4d2940afae57e48b94d44d3aced603501df24f3c2a414f" "checksum clang-sys 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "33d47b0ea88a529a570490efbb79403e416e89864ce8a96bf23e2a0f23d7e9eb"
"checksum clap 2.20.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7db281b0520e97fbd15cd615dcd8f8bcad0c26f5f7d5effe705f090f39e9a758" "checksum clap 2.20.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7db281b0520e97fbd15cd615dcd8f8bcad0c26f5f7d5effe705f090f39e9a758"
"checksum cmake 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "d18d68987ed4c516dcc3e7913659bfa4076f5182eea4a7e0038bb060953e76ac" "checksum cmake 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "d18d68987ed4c516dcc3e7913659bfa4076f5182eea4a7e0038bb060953e76ac"
"checksum cocoa 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0a5d0bcb4d345adf9b4ada6c5bb3e2b87c8150b79c46f3f26446de5f4d48de4b" "checksum cocoa 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0a5d0bcb4d345adf9b4ada6c5bb3e2b87c8150b79c46f3f26446de5f4d48de4b"

View file

@ -113,6 +113,7 @@ use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TElement, TNode};
use style::error_reporting::{NullReporter, RustLogReporter}; use style::error_reporting::{NullReporter, RustLogReporter};
use style::logical_geometry::LogicalPoint; use style::logical_geometry::LogicalPoint;
use style::media_queries::{Device, MediaList, MediaType}; use style::media_queries::{Device, MediaList, MediaType};
use style::selector_parser::SnapshotMap;
use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, REPOSITION, STORE_OVERFLOW}; use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, REPOSITION, STORE_OVERFLOW};
use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards}; use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards};
use style::stylearc::Arc as StyleArc; use style::stylearc::Arc as StyleArc;
@ -509,7 +510,8 @@ impl LayoutThread {
fn build_layout_context<'a>(&self, fn build_layout_context<'a>(&self,
guards: StylesheetGuards<'a>, guards: StylesheetGuards<'a>,
rw_data: &LayoutThreadData, rw_data: &LayoutThreadData,
request_images: bool) request_images: bool,
snapshot_map: &'a SnapshotMap)
-> LayoutContext<'a> { -> LayoutContext<'a> {
let thread_local_style_context_creation_data = let thread_local_style_context_creation_data =
ThreadLocalStyleContextCreationInfo::new(self.new_animations_sender.clone()); ThreadLocalStyleContextCreationInfo::new(self.new_animations_sender.clone());
@ -527,6 +529,7 @@ impl LayoutThread {
timer: self.timer.clone(), timer: self.timer.clone(),
quirks_mode: self.quirks_mode.unwrap(), quirks_mode: self.quirks_mode.unwrap(),
traversal_flags: TraversalFlags::empty(), traversal_flags: TraversalFlags::empty(),
snapshot_map: snapshot_map,
}, },
image_cache: self.image_cache.clone(), image_cache: self.image_cache.clone(),
font_cache_thread: Mutex::new(self.font_cache_thread.clone()), font_cache_thread: Mutex::new(self.font_cache_thread.clone()),
@ -1111,37 +1114,51 @@ impl LayoutThread {
} }
let restyles = document.drain_pending_restyles(); let restyles = document.drain_pending_restyles();
debug!("Draining restyles: {} (needs dirtying? {:?})", restyles.len(), needs_dirtying); debug!("Draining restyles: {} (needs dirtying? {:?})",
if !needs_dirtying { restyles.len(), needs_dirtying);
for (el, restyle) in restyles { let mut map = SnapshotMap::new();
// Propagate the descendant bit up the ancestors. Do this before let elements_with_snapshot: Vec<_> =
// the restyle calculation so that we can also do it for new restyles
// unstyled nodes, which the descendants bit helps us find. .iter()
if let Some(parent) = el.parent_element() { .filter(|r| r.1.snapshot.is_some())
unsafe { parent.note_dirty_descendant() }; .map(|r| r.0)
} .collect();
// If we haven't styled this node yet, we don't need to track a restyle. for (el, restyle) in restyles {
let mut data = match el.mutate_layout_data() { // Propagate the descendant bit up the ancestors. Do this before
Some(d) => d, // the restyle calculation so that we can also do it for new
None => continue, // unstyled nodes, which the descendants bit helps us find.
}; if let Some(parent) = el.parent_element() {
let mut style_data = &mut data.base.style_data; unsafe { parent.note_dirty_descendant() };
debug_assert!(style_data.has_current_styles());
let mut restyle_data = style_data.ensure_restyle();
// Stash the data on the element for processing by the style system.
restyle_data.hint = restyle.hint.into();
restyle_data.damage = restyle.damage;
if let Some(s) = restyle.snapshot {
restyle_data.snapshot.ensure(move || s);
}
debug!("Noting restyle for {:?}: {:?}", el, restyle_data);
} }
// If we haven't styled this node yet, we don't need to track a
// restyle.
let mut data = match el.mutate_layout_data() {
Some(d) => d,
None => {
unsafe { el.unset_snapshot_flags() };
continue;
}
};
if let Some(s) = restyle.snapshot {
unsafe { el.set_has_snapshot() };
map.insert(el.as_node().opaque(), s);
}
let mut style_data = &mut data.base.style_data;
let mut restyle_data = style_data.ensure_restyle();
// Stash the data on the element for processing by the style system.
restyle_data.hint.insert(&restyle.hint.into());
restyle_data.damage = restyle.damage;
debug!("Noting restyle for {:?}: {:?}", el, restyle_data);
} }
// Create a layout context for use throughout the following passes. // Create a layout context for use throughout the following passes.
let mut layout_context = self.build_layout_context(guards.clone(), &*rw_data, true); let mut layout_context =
self.build_layout_context(guards.clone(), &*rw_data, true, &map);
// NB: Type inference falls apart here for some reason, so we need to be very verbose. :-( // NB: Type inference falls apart here for some reason, so we need to be very verbose. :-(
let traversal_driver = if self.parallel_flag && self.parallel_traversal.is_some() { let traversal_driver = if self.parallel_flag && self.parallel_traversal.is_some() {
@ -1152,10 +1169,12 @@ impl LayoutThread {
let traversal = RecalcStyleAndConstructFlows::new(layout_context, traversal_driver); let traversal = RecalcStyleAndConstructFlows::new(layout_context, traversal_driver);
let token = { let token = {
let stylist = &<RecalcStyleAndConstructFlows as let context = <RecalcStyleAndConstructFlows as
DomTraversal<ServoLayoutElement>>::shared_context(&traversal).stylist; DomTraversal<ServoLayoutElement>>::shared_context(&traversal);
<RecalcStyleAndConstructFlows as <RecalcStyleAndConstructFlows as
DomTraversal<ServoLayoutElement>>::pre_traverse(element, stylist, TraversalFlags::empty()) DomTraversal<ServoLayoutElement>>::pre_traverse(element,
context,
TraversalFlags::empty())
}; };
if token.should_traverse() { if token.should_traverse() {
@ -1192,6 +1211,10 @@ impl LayoutThread {
self.root_flow = self.try_get_layout_root(element.as_node()); self.root_flow = self.try_get_layout_root(element.as_node());
} }
for element in elements_with_snapshot {
unsafe { element.unset_snapshot_flags() }
}
layout_context = traversal.destroy(); layout_context = traversal.destroy();
if opts::get().dump_style_tree { if opts::get().dump_style_tree {
@ -1382,7 +1405,11 @@ impl LayoutThread {
author: &author_guard, author: &author_guard,
ua_or_user: &ua_or_user_guard, ua_or_user: &ua_or_user_guard,
}; };
let mut layout_context = self.build_layout_context(guards, &*rw_data, false); let snapshots = SnapshotMap::new();
let mut layout_context = self.build_layout_context(guards,
&*rw_data,
false,
&snapshots);
{ {
// Perform an abbreviated style recalc that operates without access to the DOM. // Perform an abbreviated style recalc that operates without access to the DOM.

View file

@ -144,29 +144,40 @@ pub struct Node {
bitflags! { bitflags! {
#[doc = "Flags for node items."] #[doc = "Flags for node items."]
#[derive(JSTraceable, HeapSizeOf)] #[derive(JSTraceable, HeapSizeOf)]
pub flags NodeFlags: u8 { pub flags NodeFlags: u16 {
#[doc = "Specifies whether this node is in a document."] #[doc = "Specifies whether this node is in a document."]
const IS_IN_DOC = 0x01, const IS_IN_DOC = 1 << 0,
#[doc = "Specifies whether this node needs style recalc on next reflow."] #[doc = "Specifies whether this node needs style recalc on next reflow."]
const HAS_DIRTY_DESCENDANTS = 0x08, const HAS_DIRTY_DESCENDANTS = 1 << 1,
// TODO: find a better place to keep this (#4105) // TODO: find a better place to keep this (#4105)
// https://critic.hoppipolla.co.uk/showcomment?chain=8873 // https://critic.hoppipolla.co.uk/showcomment?chain=8873
// Perhaps using a Set in Document? // Perhaps using a Set in Document?
#[doc = "Specifies whether or not there is an authentic click in progress on \ #[doc = "Specifies whether or not there is an authentic click in progress on \
this element."] this element."]
const CLICK_IN_PROGRESS = 0x10, const CLICK_IN_PROGRESS = 1 << 2,
#[doc = "Specifies whether this node is focusable and whether it is supposed \ #[doc = "Specifies whether this node is focusable and whether it is supposed \
to be reachable with using sequential focus navigation."] to be reachable with using sequential focus navigation."]
const SEQUENTIALLY_FOCUSABLE = 0x20, const SEQUENTIALLY_FOCUSABLE = 1 << 3,
/// Whether any ancestor is a fragmentation container /// Whether any ancestor is a fragmentation container
const CAN_BE_FRAGMENTED = 0x40, const CAN_BE_FRAGMENTED = 1 << 4,
#[doc = "Specifies whether this node needs to be dirted when viewport size changed."] #[doc = "Specifies whether this node needs to be dirted when viewport size changed."]
const DIRTY_ON_VIEWPORT_SIZE_CHANGE = 0x80, const DIRTY_ON_VIEWPORT_SIZE_CHANGE = 1 << 5,
#[doc = "Specifies whether the parser has set an associated form owner for \ #[doc = "Specifies whether the parser has set an associated form owner for \
this element. Only applicable for form-associatable elements."] this element. Only applicable for form-associatable elements."]
const PARSER_ASSOCIATED_FORM_OWNER = 0x90, const PARSER_ASSOCIATED_FORM_OWNER = 1 << 6,
/// Whether this element has a snapshot stored due to a style or
/// attribute change.
///
/// See the `style::restyle_hints` module.
const HAS_SNAPSHOT = 1 << 7,
/// Whether this element has already handled the stored snapshot.
const HANDLED_SNAPSHOT = 1 << 8,
} }
} }
@ -289,7 +300,9 @@ impl Node {
for node in child.traverse_preorder() { for node in child.traverse_preorder() {
// Out-of-document elements never have the descendants flag set. // Out-of-document elements never have the descendants flag set.
node.set_flag(IS_IN_DOC | HAS_DIRTY_DESCENDANTS, false); node.set_flag(IS_IN_DOC | HAS_DIRTY_DESCENDANTS |
HAS_SNAPSHOT | HANDLED_SNAPSHOT,
false);
} }
for node in child.traverse_preorder() { for node in child.traverse_preorder() {
// This needs to be in its own loop, because unbind_from_tree may // This needs to be in its own loop, because unbind_from_tree may

View file

@ -39,6 +39,7 @@ use dom::characterdata::LayoutCharacterDataHelpers;
use dom::document::{Document, LayoutDocumentHelpers, PendingRestyle}; use dom::document::{Document, LayoutDocumentHelpers, PendingRestyle};
use dom::element::{Element, LayoutElementHelpers, RawLayoutElementHelpers}; use dom::element::{Element, LayoutElementHelpers, RawLayoutElementHelpers};
use dom::node::{CAN_BE_FRAGMENTED, DIRTY_ON_VIEWPORT_SIZE_CHANGE, HAS_DIRTY_DESCENDANTS, IS_IN_DOC}; use dom::node::{CAN_BE_FRAGMENTED, DIRTY_ON_VIEWPORT_SIZE_CHANGE, HAS_DIRTY_DESCENDANTS, IS_IN_DOC};
use dom::node::{HANDLED_SNAPSHOT, HAS_SNAPSHOT};
use dom::node::{LayoutNodeHelpers, Node}; use dom::node::{LayoutNodeHelpers, Node};
use dom::text::Text; use dom::text::Text;
use gfx_traits::ByteIndex; use gfx_traits::ByteIndex;
@ -413,6 +414,18 @@ impl<'le> TElement for ServoLayoutElement<'le> {
unsafe { self.as_node().node.get_flag(HAS_DIRTY_DESCENDANTS) } unsafe { self.as_node().node.get_flag(HAS_DIRTY_DESCENDANTS) }
} }
fn has_snapshot(&self) -> bool {
unsafe { self.as_node().node.get_flag(HAS_SNAPSHOT) }
}
fn handled_snapshot(&self) -> bool {
unsafe { self.as_node().node.get_flag(HANDLED_SNAPSHOT) }
}
unsafe fn set_handled_snapshot(&self) {
self.as_node().node.set_flag(HANDLED_SNAPSHOT, true);
}
unsafe fn note_descendants<B: DescendantsBit<Self>>(&self) { unsafe fn note_descendants<B: DescendantsBit<Self>>(&self) {
debug_assert!(self.get_data().is_some()); debug_assert!(self.get_data().is_some());
style::dom::raw_note_descendants::<Self, B>(*self); style::dom::raw_note_descendants::<Self, B>(*self);
@ -509,6 +522,14 @@ impl<'le> ServoLayoutElement<'le> {
} }
} }
pub unsafe fn unset_snapshot_flags(&self) {
self.as_node().node.set_flag(HAS_SNAPSHOT | HANDLED_SNAPSHOT, false);
}
pub unsafe fn set_has_snapshot(&self) {
self.as_node().node.set_flag(HAS_SNAPSHOT, true);
}
// FIXME(bholley): This should be merged with TElement::note_descendants, // FIXME(bholley): This should be merged with TElement::note_descendants,
// but that requires re-testing and possibly fixing the broken callers given // but that requires re-testing and possibly fixing the broken callers given
// the FIXME below, which I don't have time to do right now. // the FIXME below, which I don't have time to do right now.

View file

@ -65,6 +65,6 @@ kernel32-sys = "0.2"
[build-dependencies] [build-dependencies]
lazy_static = "0.2" lazy_static = "0.2"
log = "0.3" log = "0.3"
bindgen = { version = "0.24", optional = true } bindgen = { version = "0.25", optional = true }
regex = {version = "0.2", optional = true} regex = {version = "0.2", optional = true}
walkdir = "1.0" walkdir = "1.0"

View file

@ -305,6 +305,7 @@ mod bindings {
.include(add_include("mozilla/ComputedTimingFunction.h")) .include(add_include("mozilla/ComputedTimingFunction.h"))
.include(add_include("mozilla/Keyframe.h")) .include(add_include("mozilla/Keyframe.h"))
.include(add_include("mozilla/ServoElementSnapshot.h")) .include(add_include("mozilla/ServoElementSnapshot.h"))
.include(add_include("mozilla/ServoElementSnapshotTable.h"))
.include(add_include("mozilla/dom/Element.h")) .include(add_include("mozilla/dom/Element.h"))
.include(add_include("mozilla/dom/NameSpaceConstants.h")) .include(add_include("mozilla/dom/NameSpaceConstants.h"))
.include(add_include("mozilla/LookAndFeel.h")) .include(add_include("mozilla/LookAndFeel.h"))
@ -329,6 +330,7 @@ mod bindings {
"NS_AUTHOR_SPECIFIED_.*", "NS_AUTHOR_SPECIFIED_.*",
"NS_THEME_.*", "NS_THEME_.*",
"NODE_.*", "NODE_.*",
"ELEMENT_.*",
"NS_FONT_.*", "NS_FONT_.*",
"NS_STYLE_.*", "NS_STYLE_.*",
"NS_MATHML_.*", "NS_MATHML_.*",
@ -670,6 +672,7 @@ mod bindings {
"Keyframe", "Keyframe",
"ServoBundledURI", "ServoBundledURI",
"ServoElementSnapshot", "ServoElementSnapshot",
"ServoElementSnapshotTable",
"SheetParsingMode", "SheetParsingMode",
"StyleBasicShape", "StyleBasicShape",
"StyleBasicShapeType", "StyleBasicShapeType",

View file

@ -3,7 +3,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! The context within which style is calculated. //! The context within which style is calculated.
#![deny(missing_docs)]
use animation::{Animation, PropertyAnimation}; use animation::{Animation, PropertyAnimation};
use app_units::Au; use app_units::Au;
@ -20,6 +19,7 @@ use font_metrics::FontMetricsProvider;
use matching::StyleSharingCandidateCache; use matching::StyleSharingCandidateCache;
use parking_lot::RwLock; use parking_lot::RwLock;
#[cfg(feature = "gecko")] use properties::ComputedValues; #[cfg(feature = "gecko")] use properties::ComputedValues;
use selector_parser::SnapshotMap;
use selectors::matching::ElementSelectorFlags; use selectors::matching::ElementSelectorFlags;
#[cfg(feature = "servo")] use servo_config::opts; #[cfg(feature = "servo")] use servo_config::opts;
use shared_lock::StylesheetGuards; use shared_lock::StylesheetGuards;
@ -135,6 +135,9 @@ pub struct SharedStyleContext<'a> {
/// Flags controlling how we traverse the tree. /// Flags controlling how we traverse the tree.
pub traversal_flags: TraversalFlags, pub traversal_flags: TraversalFlags,
/// A map with our snapshots in order to handle restyle hints.
pub snapshot_map: &'a SnapshotMap,
} }
impl<'a> SharedStyleContext<'a> { impl<'a> SharedStyleContext<'a> {

View file

@ -6,19 +6,17 @@
#![deny(missing_docs)] #![deny(missing_docs)]
use context::SharedStyleContext;
use dom::TElement; use dom::TElement;
use properties::ComputedValues; use properties::ComputedValues;
use properties::longhands::display::computed_value as display; use properties::longhands::display::computed_value as display;
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint}; use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint};
use rule_tree::StrongRuleNode; use rule_tree::StrongRuleNode;
use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage, Snapshot}; use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage};
#[cfg(feature = "servo")] use std::collections::HashMap; #[cfg(feature = "servo")] use std::collections::HashMap;
use std::fmt; use std::fmt;
#[cfg(feature = "servo")] use std::hash::BuildHasherDefault; #[cfg(feature = "servo")] use std::hash::BuildHasherDefault;
use std::ops::Deref;
use stylearc::Arc; use stylearc::Arc;
use stylist::Stylist;
use thread_state;
use traversal::TraversalFlags; use traversal::TraversalFlags;
/// The structure that represents the result of style computation. This is /// The structure that represents the result of style computation. This is
@ -199,9 +197,7 @@ impl StoredRestyleHint {
// In the middle of an animation only restyle, we don't need to // In the middle of an animation only restyle, we don't need to
// propagate any restyle hints, and we need to remove ourselves. // propagate any restyle hints, and we need to remove ourselves.
if traversal_flags.for_animation_only() { if traversal_flags.for_animation_only() {
if self.0.intersects(RestyleHint::for_animations()) { self.0.remove(RestyleHint::for_animations());
self.0.remove(RestyleHint::for_animations());
}
return Self::empty(); return Self::empty();
} }
@ -275,55 +271,6 @@ impl From<RestyleHint> for StoredRestyleHint {
} }
} }
static NO_SNAPSHOT: Option<Snapshot> = None;
/// We really want to store an Option<Snapshot> here, but we can't drop Gecko
/// Snapshots off-main-thread. So we make a convenient little wrapper to provide
/// the semantics of Option<Snapshot>, while deferring the actual drop.
#[derive(Debug, Default)]
pub struct SnapshotOption {
snapshot: Option<Snapshot>,
destroyed: bool,
}
impl SnapshotOption {
/// An empty snapshot.
pub fn empty() -> Self {
SnapshotOption {
snapshot: None,
destroyed: false,
}
}
/// Destroy this snapshot.
pub fn destroy(&mut self) {
self.destroyed = true;
debug_assert!(self.is_none());
}
/// Ensure a snapshot is available and return a mutable reference to it.
pub fn ensure<F: FnOnce() -> Snapshot>(&mut self, create: F) -> &mut Snapshot {
debug_assert!(thread_state::get().is_layout());
if self.is_none() {
self.snapshot = Some(create());
self.destroyed = false;
}
self.snapshot.as_mut().unwrap()
}
}
impl Deref for SnapshotOption {
type Target = Option<Snapshot>;
fn deref(&self) -> &Option<Snapshot> {
if self.destroyed {
&NO_SNAPSHOT
} else {
&self.snapshot
}
}
}
/// Transient data used by the restyle algorithm. This structure is instantiated /// Transient data used by the restyle algorithm. This structure is instantiated
/// either before or during restyle traversal, and is cleared at the end of node /// either before or during restyle traversal, and is cleared at the end of node
/// processing. /// processing.
@ -350,54 +297,17 @@ pub struct RestyleData {
/// for Servo for now. /// for Servo for now.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub damage_handled: RestyleDamage, pub damage_handled: RestyleDamage,
/// An optional snapshot of the original state and attributes of the element,
/// from which we may compute additional restyle hints at traversal time.
pub snapshot: SnapshotOption,
} }
impl RestyleData { impl RestyleData {
/// Computes the final restyle hint for this element.
///
/// This expands the snapshot (if any) into a restyle hint, and handles
/// explicit sibling restyle hints from the stored restyle hint.
///
/// Returns true if later siblings must be restyled.
pub fn compute_final_hint<E: TElement>(&mut self,
element: E,
stylist: &Stylist)
-> bool {
let mut hint = self.hint.0;
if let Some(snapshot) = self.snapshot.as_ref() {
hint |= stylist.compute_restyle_hint(&element, snapshot);
}
// If the hint includes a directive for later siblings, strip it out and
// notify the caller to modify the base hint for future siblings.
let later_siblings = hint.contains(RESTYLE_LATER_SIBLINGS);
hint.remove(RESTYLE_LATER_SIBLINGS);
// Insert the hint, overriding the previous hint. This effectively takes
// care of removing the later siblings restyle hint.
self.hint = hint.into();
// Destroy the snapshot.
self.snapshot.destroy();
later_siblings
}
/// Returns true if this RestyleData might invalidate the current style. /// Returns true if this RestyleData might invalidate the current style.
pub fn has_invalidations(&self) -> bool { pub fn has_invalidations(&self) -> bool {
self.hint.has_self_invalidations() || self.hint.has_self_invalidations() || self.recascade
self.recascade ||
self.snapshot.is_some()
} }
/// Returns true if this RestyleData might invalidate sibling styles. /// Returns true if this RestyleData might invalidate sibling styles.
pub fn has_sibling_invalidations(&self) -> bool { pub fn has_sibling_invalidations(&self) -> bool {
self.hint.has_sibling_invalidations() || self.snapshot.is_some() self.hint.has_sibling_invalidations()
} }
/// Returns damage handled. /// Returns damage handled.
@ -452,6 +362,51 @@ pub enum RestyleKind {
} }
impl ElementData { impl ElementData {
/// Computes the final restyle hint for this element, potentially allocating
/// a `RestyleData` if we need to.
///
/// This expands the snapshot (if any) into a restyle hint, and handles
/// explicit sibling restyle hints from the stored restyle hint.
///
/// Returns true if later siblings must be restyled.
pub fn compute_final_hint<E: TElement>(
&mut self,
element: E,
context: &SharedStyleContext)
-> bool
{
debug!("compute_final_hint: {:?}, {:?}",
element,
context.traversal_flags);
let mut hint = match self.get_restyle() {
Some(r) => r.hint.0,
None => RestyleHint::empty(),
};
if element.has_snapshot() && !element.handled_snapshot() {
hint |= context.stylist.compute_restyle_hint(&element, context.snapshot_map);
unsafe { element.set_handled_snapshot() }
debug_assert!(element.handled_snapshot());
}
let empty_hint = hint.is_empty();
// If the hint includes a directive for later siblings, strip it out and
// notify the caller to modify the base hint for future siblings.
let later_siblings = hint.contains(RESTYLE_LATER_SIBLINGS);
hint.remove(RESTYLE_LATER_SIBLINGS);
// Insert the hint, overriding the previous hint. This effectively takes
// care of removing the later siblings restyle hint.
if !empty_hint {
self.ensure_restyle().hint = hint.into();
}
later_siblings
}
/// Trivially construct an ElementData. /// Trivially construct an ElementData.
pub fn new(existing: Option<ElementStyles>) -> Self { pub fn new(existing: Option<ElementStyles>) -> Self {
ElementData { ElementData {
@ -460,22 +415,21 @@ impl ElementData {
} }
} }
/// Returns true if this element has a computed styled. /// Returns true if this element has a computed style.
pub fn has_styles(&self) -> bool { pub fn has_styles(&self) -> bool {
self.styles.is_some() self.styles.is_some()
} }
/// Returns true if this element's style is up-to-date and has no potential /// Returns whether we have any outstanding style invalidation.
/// invalidation. pub fn has_invalidations(&self) -> bool {
pub fn has_current_styles(&self) -> bool { self.restyle.as_ref().map_or(false, |r| r.has_invalidations())
self.has_styles() &&
self.restyle.as_ref().map_or(true, |r| !r.has_invalidations())
} }
/// Returns the kind of restyling that we're going to need to do on this /// Returns the kind of restyling that we're going to need to do on this
/// element, based of the stored restyle hint. /// element, based of the stored restyle hint.
pub fn restyle_kind(&self) -> RestyleKind { pub fn restyle_kind(&self) -> RestyleKind {
debug_assert!(!self.has_current_styles(), "Should've stopped earlier"); debug_assert!(!self.has_styles() || self.has_invalidations(),
"Should've stopped earlier");
if !self.has_styles() { if !self.has_styles() {
return RestyleKind::MatchAndCascade; return RestyleKind::MatchAndCascade;
} }
@ -527,8 +481,6 @@ impl ElementData {
/// Sets the computed element styles. /// Sets the computed element styles.
pub fn set_styles(&mut self, styles: ElementStyles) { pub fn set_styles(&mut self, styles: ElementStyles) {
debug_assert!(self.get_restyle().map_or(true, |r| r.snapshot.is_none()),
"Traversal should have expanded snapshots");
self.styles = Some(styles); self.styles = Some(styles);
} }

View file

@ -385,6 +385,26 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
/// the actual restyle traversal. /// the actual restyle traversal.
fn has_dirty_descendants(&self) -> bool; fn has_dirty_descendants(&self) -> bool;
/// Returns whether state or attributes that may change style have changed
/// on the element, and thus whether the element has been snapshotted to do
/// restyle hint computation.
fn has_snapshot(&self) -> bool;
/// Returns whether the current snapshot if present has been handled.
fn handled_snapshot(&self) -> bool;
/// Flags this element as having handled already its snapshot.
unsafe fn set_handled_snapshot(&self);
/// Returns whether the element's styles are up-to-date.
fn has_current_styles(&self, data: &ElementData) -> bool {
if self.has_snapshot() && !self.handled_snapshot() {
return false;
}
data.has_styles() && !data.has_invalidations()
}
/// Flags an element and its ancestors with a given `DescendantsBit`. /// Flags an element and its ancestors with a given `DescendantsBit`.
/// ///
/// TODO(emilio): We call this conservatively from restyle_element_internal /// TODO(emilio): We call this conservatively from restyle_element_internal

View file

@ -37,6 +37,7 @@ use gecko_bindings::structs::GeckoFontMetrics;
use gecko_bindings::structs::Keyframe; use gecko_bindings::structs::Keyframe;
use gecko_bindings::structs::ServoBundledURI; use gecko_bindings::structs::ServoBundledURI;
use gecko_bindings::structs::ServoElementSnapshot; use gecko_bindings::structs::ServoElementSnapshot;
use gecko_bindings::structs::ServoElementSnapshotTable;
use gecko_bindings::structs::SheetParsingMode; use gecko_bindings::structs::SheetParsingMode;
use gecko_bindings::structs::StyleBasicShape; use gecko_bindings::structs::StyleBasicShape;
use gecko_bindings::structs::StyleBasicShapeType; use gecko_bindings::structs::StyleBasicShapeType;
@ -902,8 +903,9 @@ extern "C" {
-> nsChangeHint; -> nsChangeHint;
} }
extern "C" { extern "C" {
pub fn Gecko_CreateElementSnapshot(element: RawGeckoElementBorrowed) pub fn Gecko_GetElementSnapshot(table: *const ServoElementSnapshotTable,
-> ServoElementSnapshotOwned; element: RawGeckoElementBorrowed)
-> *const ServoElementSnapshot;
} }
extern "C" { extern "C" {
pub fn Gecko_DropElementSnapshot(snapshot: ServoElementSnapshotOwned); pub fn Gecko_DropElementSnapshot(snapshot: ServoElementSnapshotOwned);
@ -2171,10 +2173,6 @@ extern "C" {
extern "C" { extern "C" {
pub fn Servo_Shutdown(); pub fn Servo_Shutdown();
} }
extern "C" {
pub fn Servo_Element_GetSnapshot(element: RawGeckoElementBorrowed)
-> *mut ServoElementSnapshot;
}
extern "C" { extern "C" {
pub fn Servo_Element_GetStyleRuleList(element: RawGeckoElementBorrowed, pub fn Servo_Element_GetStyleRuleList(element: RawGeckoElementBorrowed,
rules: rules:
@ -2215,12 +2213,15 @@ extern "C" {
extern "C" { extern "C" {
pub fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed, pub fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
pseudo_tag: *mut nsIAtom, pseudo_tag: *mut nsIAtom,
snapshots:
*const ServoElementSnapshotTable,
set: RawServoStyleSetBorrowed) set: RawServoStyleSetBorrowed)
-> ServoComputedValuesStrong; -> ServoComputedValuesStrong;
} }
extern "C" { extern "C" {
pub fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed, pub fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed,
set: RawServoStyleSetBorrowed, set: RawServoStyleSetBorrowed,
snapshots: *const ServoElementSnapshotTable,
root_behavior: TraversalRootBehavior, root_behavior: TraversalRootBehavior,
restyle_behavior: TraversalRestyleBehavior) restyle_behavior: TraversalRestyleBehavior)
-> bool; -> bool;
@ -2233,6 +2234,8 @@ extern "C" {
RawServoStyleSetBorrowed, RawServoStyleSetBorrowed,
element: element:
RawGeckoElementBorrowed, RawGeckoElementBorrowed,
snapshots:
*const ServoElementSnapshotTable,
pseudo_tag: pseudo_tag:
*mut nsIAtom) *mut nsIAtom)
-> ServoComputedValuesStrong; -> ServoComputedValuesStrong;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -16,6 +16,8 @@ use std::fmt;
use std::ptr; use std::ptr;
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
pub use gecko::snapshot::SnapshotMap;
/// A representation of a CSS pseudo-element. /// A representation of a CSS pseudo-element.
/// ///
/// In Gecko, we represent pseudo-elements as plain `Atom`s. /// In Gecko, we represent pseudo-elements as plain `Atom`s.

View file

@ -5,61 +5,60 @@
//! A gecko snapshot, that stores the element attributes and state before they //! A gecko snapshot, that stores the element attributes and state before they
//! change in order to properly calculate restyle hints. //! change in order to properly calculate restyle hints.
use dom::TElement;
use element_state::ElementState; use element_state::ElementState;
use gecko::snapshot_helpers; use gecko::snapshot_helpers;
use gecko::wrapper::{AttrSelectorHelpers, GeckoElement}; use gecko::wrapper::{AttrSelectorHelpers, GeckoElement};
use gecko_bindings::bindings; use gecko_bindings::bindings;
use gecko_bindings::structs::ServoElementSnapshot; use gecko_bindings::structs::ServoElementSnapshot;
use gecko_bindings::structs::ServoElementSnapshotFlags as Flags; use gecko_bindings::structs::ServoElementSnapshotFlags as Flags;
use gecko_bindings::structs::ServoElementSnapshotTable;
use restyle_hints::ElementSnapshot; use restyle_hints::ElementSnapshot;
use selector_parser::SelectorImpl; use selector_parser::SelectorImpl;
use selectors::parser::AttrSelector; use selectors::parser::AttrSelector;
use std::ptr;
use string_cache::Atom; use string_cache::Atom;
/// A snapshot of a Gecko element. /// A snapshot of a Gecko element.
/// pub type GeckoElementSnapshot = ServoElementSnapshot;
/// This is really a Gecko type (see `ServoElementSnapshot.h` in Gecko) we wrap
/// here.
#[derive(Debug)]
pub struct GeckoElementSnapshot(bindings::ServoElementSnapshotOwned);
// FIXME(bholley): Add support for *OwnedConst type, and then we get Sync /// A map from elements to snapshots for Gecko's style back-end.
// automatically. pub type SnapshotMap = ServoElementSnapshotTable;
unsafe impl Sync for GeckoElementSnapshot {}
impl SnapshotMap {
/// Gets the snapshot for this element, if any.
///
/// FIXME(emilio): The transmute() business we do here is kind of nasty, but
/// it's a consequence of the map being a OpaqueNode -> Snapshot table in
/// Servo and an Element -> Snapshot table in Gecko.
///
/// We should be able to make this a more type-safe with type annotations by
/// making SnapshotMap a trait and moving the implementations outside, but
/// that's a pain because it implies parameterizing SharedStyleContext.
pub fn get<E: TElement>(&self, element: &E) -> Option<&GeckoElementSnapshot> {
debug_assert!(element.has_snapshot());
impl Drop for GeckoElementSnapshot {
fn drop(&mut self) {
unsafe { unsafe {
bindings::Gecko_DropElementSnapshot(ptr::read(&self.0 as *const _)); let element =
unsafe { ::std::mem::transmute::<&E, &GeckoElement>(element) };
bindings::Gecko_GetElementSnapshot(self, element.0).as_ref()
} }
} }
} }
impl GeckoElementSnapshot { impl GeckoElementSnapshot {
/// Create a new snapshot of the given element.
pub fn new<'le>(el: GeckoElement<'le>) -> Self {
unsafe { GeckoElementSnapshot(bindings::Gecko_CreateElementSnapshot(el.0)) }
}
/// Get a mutable reference to the snapshot.
pub fn borrow_mut_raw(&mut self) -> bindings::ServoElementSnapshotBorrowedMut {
&mut *self.0
}
/// Get the pointer to the actual snapshot.
pub fn ptr(&self) -> *const ServoElementSnapshot {
&*self.0
}
#[inline] #[inline]
fn is_html_element_in_html_document(&self) -> bool { fn is_html_element_in_html_document(&self) -> bool {
unsafe { (*self.0).mIsHTMLElementInHTMLDocument } self.mIsHTMLElementInHTMLDocument
} }
#[inline] #[inline]
fn has_any(&self, flags: Flags) -> bool { fn has_any(&self, flags: Flags) -> bool {
unsafe { ((*self.0).mContains as u8 & flags as u8) != 0 } (self.mContains as u8 & flags as u8) != 0
}
fn as_ptr(&self) -> *const Self {
self
} }
} }
@ -68,7 +67,7 @@ impl ::selectors::MatchAttr for GeckoElementSnapshot {
fn match_attr_has(&self, attr: &AttrSelector<SelectorImpl>) -> bool { fn match_attr_has(&self, attr: &AttrSelector<SelectorImpl>) -> bool {
unsafe { unsafe {
bindings::Gecko_SnapshotHasAttr(self.ptr(), bindings::Gecko_SnapshotHasAttr(self,
attr.ns_or_null(), attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document())) attr.select_name(self.is_html_element_in_html_document()))
} }
@ -76,7 +75,7 @@ impl ::selectors::MatchAttr for GeckoElementSnapshot {
fn match_attr_equals(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool { fn match_attr_equals(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool {
unsafe { unsafe {
bindings::Gecko_SnapshotAttrEquals(self.ptr(), bindings::Gecko_SnapshotAttrEquals(self,
attr.ns_or_null(), attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()), attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr(), value.as_ptr(),
@ -86,7 +85,7 @@ impl ::selectors::MatchAttr for GeckoElementSnapshot {
fn match_attr_equals_ignore_ascii_case(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool { fn match_attr_equals_ignore_ascii_case(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool {
unsafe { unsafe {
bindings::Gecko_SnapshotAttrEquals(self.ptr(), bindings::Gecko_SnapshotAttrEquals(self,
attr.ns_or_null(), attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()), attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr(), value.as_ptr(),
@ -95,7 +94,7 @@ impl ::selectors::MatchAttr for GeckoElementSnapshot {
} }
fn match_attr_includes(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool { fn match_attr_includes(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool {
unsafe { unsafe {
bindings::Gecko_SnapshotAttrIncludes(self.ptr(), bindings::Gecko_SnapshotAttrIncludes(self,
attr.ns_or_null(), attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()), attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr()) value.as_ptr())
@ -103,7 +102,7 @@ impl ::selectors::MatchAttr for GeckoElementSnapshot {
} }
fn match_attr_dash(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool { fn match_attr_dash(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool {
unsafe { unsafe {
bindings::Gecko_SnapshotAttrDashEquals(self.ptr(), bindings::Gecko_SnapshotAttrDashEquals(self,
attr.ns_or_null(), attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()), attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr()) value.as_ptr())
@ -111,7 +110,7 @@ impl ::selectors::MatchAttr for GeckoElementSnapshot {
} }
fn match_attr_prefix(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool { fn match_attr_prefix(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool {
unsafe { unsafe {
bindings::Gecko_SnapshotAttrHasPrefix(self.ptr(), bindings::Gecko_SnapshotAttrHasPrefix(self,
attr.ns_or_null(), attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()), attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr()) value.as_ptr())
@ -119,7 +118,7 @@ impl ::selectors::MatchAttr for GeckoElementSnapshot {
} }
fn match_attr_substring(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool { fn match_attr_substring(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool {
unsafe { unsafe {
bindings::Gecko_SnapshotAttrHasSubstring(self.ptr(), bindings::Gecko_SnapshotAttrHasSubstring(self,
attr.ns_or_null(), attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()), attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr()) value.as_ptr())
@ -127,7 +126,7 @@ impl ::selectors::MatchAttr for GeckoElementSnapshot {
} }
fn match_attr_suffix(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool { fn match_attr_suffix(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool {
unsafe { unsafe {
bindings::Gecko_SnapshotAttrHasSuffix(self.ptr(), bindings::Gecko_SnapshotAttrHasSuffix(self,
attr.ns_or_null(), attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()), attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr()) value.as_ptr())
@ -138,7 +137,7 @@ impl ::selectors::MatchAttr for GeckoElementSnapshot {
impl ElementSnapshot for GeckoElementSnapshot { impl ElementSnapshot for GeckoElementSnapshot {
fn state(&self) -> Option<ElementState> { fn state(&self) -> Option<ElementState> {
if self.has_any(Flags::State) { if self.has_any(Flags::State) {
Some(ElementState::from_bits_truncate(unsafe { (*self.0).mState })) Some(ElementState::from_bits_truncate(self.mState))
} else { } else {
None None
} }
@ -151,7 +150,7 @@ impl ElementSnapshot for GeckoElementSnapshot {
fn id_attr(&self) -> Option<Atom> { fn id_attr(&self) -> Option<Atom> {
let ptr = unsafe { let ptr = unsafe {
bindings::Gecko_SnapshotAtomAttrValue(self.ptr(), bindings::Gecko_SnapshotAtomAttrValue(self,
atom!("id").as_ptr()) atom!("id").as_ptr())
}; };
@ -163,7 +162,7 @@ impl ElementSnapshot for GeckoElementSnapshot {
} }
fn has_class(&self, name: &Atom) -> bool { fn has_class(&self, name: &Atom) -> bool {
snapshot_helpers::has_class(self.ptr(), snapshot_helpers::has_class(self.as_ptr(),
name, name,
bindings::Gecko_SnapshotClassOrClassList) bindings::Gecko_SnapshotClassOrClassList)
} }
@ -171,7 +170,7 @@ impl ElementSnapshot for GeckoElementSnapshot {
fn each_class<F>(&self, callback: F) fn each_class<F>(&self, callback: F)
where F: FnMut(&Atom) where F: FnMut(&Atom)
{ {
snapshot_helpers::each_class(self.ptr(), snapshot_helpers::each_class(self.as_ptr(),
callback, callback,
bindings::Gecko_SnapshotClassOrClassList) bindings::Gecko_SnapshotClassOrClassList)
} }

View file

@ -47,9 +47,11 @@ use gecko_bindings::bindings::Gecko_UpdateAnimations;
use gecko_bindings::structs; use gecko_bindings::structs;
use gecko_bindings::structs::{RawGeckoElement, RawGeckoNode}; use gecko_bindings::structs::{RawGeckoElement, RawGeckoNode};
use gecko_bindings::structs::{nsIAtom, nsIContent, nsStyleContext}; use gecko_bindings::structs::{nsIAtom, nsIContent, nsStyleContext};
use gecko_bindings::structs::ELEMENT_HANDLED_SNAPSHOT;
use gecko_bindings::structs::ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO;
use gecko_bindings::structs::ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO;
use gecko_bindings::structs::ELEMENT_HAS_SNAPSHOT;
use gecko_bindings::structs::EffectCompositor_CascadeLevel as CascadeLevel; use gecko_bindings::structs::EffectCompositor_CascadeLevel as CascadeLevel;
use gecko_bindings::structs::NODE_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO;
use gecko_bindings::structs::NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO;
use gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE; use gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE;
use gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS; use gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS;
use gecko_bindings::sugar::ownership::HasArcFFI; use gecko_bindings::sugar::ownership::HasArcFFI;
@ -60,7 +62,7 @@ use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock};
use properties::animated_properties::{AnimationValue, AnimationValueMap, TransitionProperty}; use properties::animated_properties::{AnimationValue, AnimationValueMap, TransitionProperty};
use properties::style_structs::Font; use properties::style_structs::Font;
use rule_tree::CascadeLevel as ServoCascadeLevel; use rule_tree::CascadeLevel as ServoCascadeLevel;
use selector_parser::{ElementExt, Snapshot}; use selector_parser::ElementExt;
use selectors::Element; use selectors::Element;
use selectors::matching::{ElementSelectorFlags, StyleRelations}; use selectors::matching::{ElementSelectorFlags, StyleRelations};
use selectors::parser::{AttrSelector, NamespaceConstraint}; use selectors::parser::{AttrSelector, NamespaceConstraint};
@ -361,6 +363,10 @@ impl<'le> GeckoElement<'le> {
/// Clear the element data for a given element. /// Clear the element data for a given element.
pub fn clear_data(&self) { pub fn clear_data(&self) {
let ptr = self.0.mServoData.get(); let ptr = self.0.mServoData.get();
unsafe {
self.unset_flags(ELEMENT_HAS_SNAPSHOT as u32 |
ELEMENT_HANDLED_SNAPSHOT as u32);
}
if !ptr.is_null() { if !ptr.is_null() {
debug!("Dropping ElementData for {:?}", self); debug!("Dropping ElementData for {:?}", self);
let data = unsafe { Box::from_raw(self.0.mServoData.get()) }; let data = unsafe { Box::from_raw(self.0.mServoData.get()) };
@ -389,11 +395,6 @@ impl<'le> GeckoElement<'le> {
}, },
} }
} }
/// Creates a blank snapshot for this element.
pub fn create_snapshot(&self) -> Snapshot {
Snapshot::new(*self)
}
} }
/// Converts flags from the layout used by rust-selectors to the layout used /// Converts flags from the layout used by rust-selectors to the layout used
@ -589,14 +590,27 @@ impl<'le> TElement for GeckoElement<'le> {
} }
} }
fn has_snapshot(&self) -> bool {
self.flags() & (ELEMENT_HAS_SNAPSHOT as u32) != 0
}
fn handled_snapshot(&self) -> bool {
self.flags() & (ELEMENT_HANDLED_SNAPSHOT as u32) != 0
}
unsafe fn set_handled_snapshot(&self) {
debug_assert!(self.get_data().is_some());
self.set_flags(ELEMENT_HANDLED_SNAPSHOT as u32)
}
fn has_dirty_descendants(&self) -> bool { fn has_dirty_descendants(&self) -> bool {
self.flags() & (NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32) != 0 self.flags() & (ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32) != 0
} }
unsafe fn set_dirty_descendants(&self) { unsafe fn set_dirty_descendants(&self) {
debug_assert!(self.get_data().is_some()); debug_assert!(self.get_data().is_some());
debug!("Setting dirty descendants: {:?}", self); debug!("Setting dirty descendants: {:?}", self);
self.set_flags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32) self.set_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32)
} }
unsafe fn note_descendants<B: DescendantsBit<Self>>(&self) { unsafe fn note_descendants<B: DescendantsBit<Self>>(&self) {
@ -617,19 +631,19 @@ impl<'le> TElement for GeckoElement<'le> {
} }
unsafe fn unset_dirty_descendants(&self) { unsafe fn unset_dirty_descendants(&self) {
self.unset_flags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32) self.unset_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32)
} }
fn has_animation_only_dirty_descendants(&self) -> bool { fn has_animation_only_dirty_descendants(&self) -> bool {
self.flags() & (NODE_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32) != 0 self.flags() & (ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32) != 0
} }
unsafe fn set_animation_only_dirty_descendants(&self) { unsafe fn set_animation_only_dirty_descendants(&self) {
self.set_flags(NODE_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32) self.set_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32)
} }
unsafe fn unset_animation_only_dirty_descendants(&self) { unsafe fn unset_animation_only_dirty_descendants(&self) {
self.unset_flags(NODE_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32) self.unset_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32)
} }
fn is_native_anonymous(&self) -> bool { fn is_native_anonymous(&self) -> bool {

View file

@ -85,6 +85,7 @@ impl<T: RefCounted> RefPtr<T> {
pub fn forget(self) -> structs::RefPtr<T> { pub fn forget(self) -> structs::RefPtr<T> {
let ret = structs::RefPtr { let ret = structs::RefPtr {
mRawPtr: self.ptr, mRawPtr: self.ptr,
_phantom_0: PhantomData,
}; };
mem::forget(self); mem::forget(self);
ret ret

View file

@ -196,7 +196,7 @@ fn element_matches_candidate<E: TElement>(element: &E,
} }
let data = candidate_element.borrow_data().unwrap(); let data = candidate_element.borrow_data().unwrap();
debug_assert!(data.has_current_styles()); debug_assert!(element.has_current_styles(&data));
let current_styles = data.styles(); let current_styles = data.styles();
debug!("Sharing style between {:?} and {:?}", element, candidate_element); debug!("Sharing style between {:?} and {:?}", element, candidate_element);
@ -432,7 +432,8 @@ trait PrivateMatchMethods: TElement {
// construct a frame for some small piece of newly-added // construct a frame for some small piece of newly-added
// content in order to do something specific with that frame, // content in order to do something specific with that frame,
// but not wanting to flush all of layout). // but not wanting to flush all of layout).
debug_assert!(cfg!(feature = "gecko") || d.has_current_styles()); debug_assert!(cfg!(feature = "gecko") ||
parent_el.unwrap().has_current_styles(d));
d.styles().primary.values() d.styles().primary.values()
}); });

View file

@ -13,7 +13,7 @@ use element_state::*;
use gecko_bindings::structs::nsRestyleHint; use gecko_bindings::structs::nsRestyleHint;
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
use selector_parser::{AttrValue, NonTSPseudoClass, Snapshot, SelectorImpl}; use selector_parser::{AttrValue, NonTSPseudoClass, SelectorImpl, Snapshot, SnapshotMap};
use selectors::{Element, MatchAttr}; use selectors::{Element, MatchAttr};
use selectors::matching::{ElementSelectorFlags, StyleRelations}; use selectors::matching::{ElementSelectorFlags, StyleRelations};
use selectors::matching::matches_selector; use selectors::matching::matches_selector;
@ -22,6 +22,7 @@ use selectors::parser::{SelectorInner, SelectorMethods};
use selectors::visitor::SelectorVisitor; use selectors::visitor::SelectorVisitor;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::cell::Cell;
use std::clone::Clone; use std::clone::Clone;
use stylist::SelectorMap; use stylist::SelectorMap;
@ -126,6 +127,7 @@ impl RestyleHint {
impl From<nsRestyleHint> for RestyleHint { impl From<nsRestyleHint> for RestyleHint {
fn from(raw: nsRestyleHint) -> Self { fn from(raw: nsRestyleHint) -> Self {
let raw_bits: u32 = raw.0; let raw_bits: u32 = raw.0;
// FIXME(bholley): Finish aligning the binary representations here and // FIXME(bholley): Finish aligning the binary representations here and
// then .expect() the result of the checked version. // then .expect() the result of the checked version.
if Self::from_bits(raw_bits).is_none() { if Self::from_bits(raw_bits).is_none() {
@ -193,20 +195,38 @@ struct ElementWrapper<'a, E>
where E: TElement, where E: TElement,
{ {
element: E, element: E,
snapshot: Option<&'a Snapshot>, cached_snapshot: Cell<Option<&'a Snapshot>>,
snapshot_map: &'a SnapshotMap,
} }
impl<'a, E> ElementWrapper<'a, E> impl<'a, E> ElementWrapper<'a, E>
where E: TElement, where E: TElement,
{ {
/// Trivially constructs an `ElementWrapper` without a snapshot. /// Trivially constructs an `ElementWrapper`.
pub fn new(el: E) -> ElementWrapper<'a, E> { fn new(el: E, snapshot_map: &'a SnapshotMap) -> Self {
ElementWrapper { element: el, snapshot: None } ElementWrapper {
element: el,
cached_snapshot: Cell::new(None),
snapshot_map: snapshot_map,
}
} }
/// Trivially constructs an `ElementWrapper` with a snapshot. /// Gets the snapshot associated with this element, if any.
pub fn new_with_snapshot(el: E, snapshot: &'a Snapshot) -> ElementWrapper<'a, E> { fn snapshot(&self) -> Option<&'a Snapshot> {
ElementWrapper { element: el, snapshot: Some(snapshot) } if !self.element.has_snapshot() {
return None;
}
if let Some(s) = self.cached_snapshot.get() {
return Some(s);
}
let snapshot = self.snapshot_map.get(&self.element);
debug_assert!(snapshot.is_some(), "has_snapshot lied!");
self.cached_snapshot.set(snapshot);
snapshot
} }
} }
@ -216,7 +236,7 @@ impl<'a, E> MatchAttr for ElementWrapper<'a, E>
type Impl = SelectorImpl; type Impl = SelectorImpl;
fn match_attr_has(&self, attr: &AttrSelector<SelectorImpl>) -> bool { fn match_attr_has(&self, attr: &AttrSelector<SelectorImpl>) -> bool {
match self.snapshot { match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() Some(snapshot) if snapshot.has_attrs()
=> snapshot.match_attr_has(attr), => snapshot.match_attr_has(attr),
_ => self.element.match_attr_has(attr) _ => self.element.match_attr_has(attr)
@ -226,7 +246,7 @@ impl<'a, E> MatchAttr for ElementWrapper<'a, E>
fn match_attr_equals(&self, fn match_attr_equals(&self,
attr: &AttrSelector<SelectorImpl>, attr: &AttrSelector<SelectorImpl>,
value: &AttrValue) -> bool { value: &AttrValue) -> bool {
match self.snapshot { match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() Some(snapshot) if snapshot.has_attrs()
=> snapshot.match_attr_equals(attr, value), => snapshot.match_attr_equals(attr, value),
_ => self.element.match_attr_equals(attr, value) _ => self.element.match_attr_equals(attr, value)
@ -236,7 +256,7 @@ impl<'a, E> MatchAttr for ElementWrapper<'a, E>
fn match_attr_equals_ignore_ascii_case(&self, fn match_attr_equals_ignore_ascii_case(&self,
attr: &AttrSelector<SelectorImpl>, attr: &AttrSelector<SelectorImpl>,
value: &AttrValue) -> bool { value: &AttrValue) -> bool {
match self.snapshot { match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() Some(snapshot) if snapshot.has_attrs()
=> snapshot.match_attr_equals_ignore_ascii_case(attr, value), => snapshot.match_attr_equals_ignore_ascii_case(attr, value),
_ => self.element.match_attr_equals_ignore_ascii_case(attr, value) _ => self.element.match_attr_equals_ignore_ascii_case(attr, value)
@ -246,7 +266,7 @@ impl<'a, E> MatchAttr for ElementWrapper<'a, E>
fn match_attr_includes(&self, fn match_attr_includes(&self,
attr: &AttrSelector<SelectorImpl>, attr: &AttrSelector<SelectorImpl>,
value: &AttrValue) -> bool { value: &AttrValue) -> bool {
match self.snapshot { match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() Some(snapshot) if snapshot.has_attrs()
=> snapshot.match_attr_includes(attr, value), => snapshot.match_attr_includes(attr, value),
_ => self.element.match_attr_includes(attr, value) _ => self.element.match_attr_includes(attr, value)
@ -256,7 +276,7 @@ impl<'a, E> MatchAttr for ElementWrapper<'a, E>
fn match_attr_dash(&self, fn match_attr_dash(&self,
attr: &AttrSelector<SelectorImpl>, attr: &AttrSelector<SelectorImpl>,
value: &AttrValue) -> bool { value: &AttrValue) -> bool {
match self.snapshot { match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() Some(snapshot) if snapshot.has_attrs()
=> snapshot.match_attr_dash(attr, value), => snapshot.match_attr_dash(attr, value),
_ => self.element.match_attr_dash(attr, value) _ => self.element.match_attr_dash(attr, value)
@ -266,7 +286,7 @@ impl<'a, E> MatchAttr for ElementWrapper<'a, E>
fn match_attr_prefix(&self, fn match_attr_prefix(&self,
attr: &AttrSelector<SelectorImpl>, attr: &AttrSelector<SelectorImpl>,
value: &AttrValue) -> bool { value: &AttrValue) -> bool {
match self.snapshot { match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() Some(snapshot) if snapshot.has_attrs()
=> snapshot.match_attr_prefix(attr, value), => snapshot.match_attr_prefix(attr, value),
_ => self.element.match_attr_prefix(attr, value) _ => self.element.match_attr_prefix(attr, value)
@ -276,7 +296,7 @@ impl<'a, E> MatchAttr for ElementWrapper<'a, E>
fn match_attr_substring(&self, fn match_attr_substring(&self,
attr: &AttrSelector<SelectorImpl>, attr: &AttrSelector<SelectorImpl>,
value: &AttrValue) -> bool { value: &AttrValue) -> bool {
match self.snapshot { match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() Some(snapshot) if snapshot.has_attrs()
=> snapshot.match_attr_substring(attr, value), => snapshot.match_attr_substring(attr, value),
_ => self.element.match_attr_substring(attr, value) _ => self.element.match_attr_substring(attr, value)
@ -286,7 +306,7 @@ impl<'a, E> MatchAttr for ElementWrapper<'a, E>
fn match_attr_suffix(&self, fn match_attr_suffix(&self,
attr: &AttrSelector<SelectorImpl>, attr: &AttrSelector<SelectorImpl>,
value: &AttrValue) -> bool { value: &AttrValue) -> bool {
match self.snapshot { match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() Some(snapshot) if snapshot.has_attrs()
=> snapshot.match_attr_suffix(attr, value), => snapshot.match_attr_suffix(attr, value),
_ => self.element.match_attr_suffix(attr, value) _ => self.element.match_attr_suffix(attr, value)
@ -322,7 +342,7 @@ impl<'a, E> Element for ElementWrapper<'a, E>
relations, relations,
&mut |_, _| {}) &mut |_, _| {})
} }
match self.snapshot.and_then(|s| s.state()) { match self.snapshot().and_then(|s| s.state()) {
Some(snapshot_state) => snapshot_state.contains(flag), Some(snapshot_state) => snapshot_state.contains(flag),
None => { None => {
self.element.match_non_ts_pseudo_class(pseudo_class, self.element.match_non_ts_pseudo_class(pseudo_class,
@ -333,23 +353,28 @@ impl<'a, E> Element for ElementWrapper<'a, E>
} }
fn parent_element(&self) -> Option<Self> { fn parent_element(&self) -> Option<Self> {
self.element.parent_element().map(ElementWrapper::new) self.element.parent_element()
.map(|e| ElementWrapper::new(e, self.snapshot_map))
} }
fn first_child_element(&self) -> Option<Self> { fn first_child_element(&self) -> Option<Self> {
self.element.first_child_element().map(ElementWrapper::new) self.element.first_child_element()
.map(|e| ElementWrapper::new(e, self.snapshot_map))
} }
fn last_child_element(&self) -> Option<Self> { fn last_child_element(&self) -> Option<Self> {
self.element.last_child_element().map(ElementWrapper::new) self.element.last_child_element()
.map(|e| ElementWrapper::new(e, self.snapshot_map))
} }
fn prev_sibling_element(&self) -> Option<Self> { fn prev_sibling_element(&self) -> Option<Self> {
self.element.prev_sibling_element().map(ElementWrapper::new) self.element.prev_sibling_element()
.map(|e| ElementWrapper::new(e, self.snapshot_map))
} }
fn next_sibling_element(&self) -> Option<Self> { fn next_sibling_element(&self) -> Option<Self> {
self.element.next_sibling_element().map(ElementWrapper::new) self.element.next_sibling_element()
.map(|e| ElementWrapper::new(e, self.snapshot_map))
} }
fn is_html_element_in_html_document(&self) -> bool { fn is_html_element_in_html_document(&self) -> bool {
@ -365,7 +390,7 @@ impl<'a, E> Element for ElementWrapper<'a, E>
} }
fn get_id(&self) -> Option<Atom> { fn get_id(&self) -> Option<Atom> {
match self.snapshot { match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() Some(snapshot) if snapshot.has_attrs()
=> snapshot.id_attr(), => snapshot.id_attr(),
_ => self.element.get_id() _ => self.element.get_id()
@ -373,7 +398,7 @@ impl<'a, E> Element for ElementWrapper<'a, E>
} }
fn has_class(&self, name: &Atom) -> bool { fn has_class(&self, name: &Atom) -> bool {
match self.snapshot { match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() Some(snapshot) if snapshot.has_attrs()
=> snapshot.has_class(name), => snapshot.has_class(name),
_ => self.element.has_class(name) _ => self.element.has_class(name)
@ -390,7 +415,7 @@ impl<'a, E> Element for ElementWrapper<'a, E>
fn each_class<F>(&self, callback: F) fn each_class<F>(&self, callback: F)
where F: FnMut(&Atom) { where F: FnMut(&Atom) {
match self.snapshot { match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() Some(snapshot) if snapshot.has_attrs()
=> snapshot.each_class(callback), => snapshot.each_class(callback),
_ => self.element.each_class(callback) _ => self.element.each_class(callback)
@ -606,13 +631,21 @@ impl DependencySet {
/// explained in the rest of the documentation. /// explained in the rest of the documentation.
pub fn compute_hint<E>(&self, pub fn compute_hint<E>(&self,
el: &E, el: &E,
snapshot: &Snapshot) snapshots: &SnapshotMap)
-> RestyleHint -> RestyleHint
where E: TElement + Clone, where E: TElement + Clone,
{ {
debug_assert!(el.has_snapshot(), "Shouldn't be here!");
let snapshot_el = ElementWrapper::new(el.clone(), snapshots);
let snapshot =
snapshot_el.snapshot().expect("has_snapshot lied so badly");
let current_state = el.get_state(); let current_state = el.get_state();
let state_changes = snapshot.state() let state_changes =
.map_or_else(ElementState::empty, |old_state| current_state ^ old_state); snapshot.state()
.map_or_else(ElementState::empty,
|old_state| current_state ^ old_state);
let attrs_changed = snapshot.has_attrs(); let attrs_changed = snapshot.has_attrs();
if state_changes.is_empty() && !attrs_changed { if state_changes.is_empty() && !attrs_changed {
@ -620,7 +653,6 @@ impl DependencySet {
} }
let mut hint = RestyleHint::empty(); let mut hint = RestyleHint::empty();
let snapshot_el = ElementWrapper::new_with_snapshot(el.clone(), snapshot);
// Compute whether the snapshot has any different id or class attributes // Compute whether the snapshot has any different id or class attributes
// from the element. If it does, we need to pass those to the lookup, so // from the element. If it does, we need to pass those to the lookup, so
@ -637,12 +669,14 @@ impl DependencySet {
} }
self.0.lookup_with_additional(*el, additional_id, &additional_classes, &mut |dep| { self.0.lookup_with_additional(*el, additional_id, &additional_classes, &mut |dep| {
if !dep.sensitivities.sensitive_to(attrs_changed, state_changes) || hint.contains(dep.hint) { if !dep.sensitivities.sensitive_to(attrs_changed, state_changes) ||
hint.contains(dep.hint) {
return true; return true;
} }
// We can ignore the selector flags, since they would have already been set during // We can ignore the selector flags, since they would have already
// original matching for any element that might change its matching behavior here. // been set during original matching for any element that might
// change its matching behavior here.
let matched_then = let matched_then =
matches_selector(&dep.selector, &snapshot_el, None, matches_selector(&dep.selector, &snapshot_el, None,
&mut StyleRelations::empty(), &mut StyleRelations::empty(),
@ -658,8 +692,8 @@ impl DependencySet {
!hint.is_all() !hint.is_all()
}); });
debug!("Calculated restyle hint: {:?}. (Element={:?}, State={:?}, Snapshot={:?}, {} Deps)", debug!("Calculated restyle hint: {:?} for {:?}. (State={:?}, {} Deps)",
hint, el, current_state, snapshot, self.len()); hint, el, current_state, self.len());
trace!("Deps: {:?}", self); trace!("Deps: {:?}", self);
hint hint

View file

@ -9,7 +9,9 @@
use {Atom, Prefix, Namespace, LocalName}; use {Atom, Prefix, Namespace, LocalName};
use attr::{AttrIdentifier, AttrValue}; use attr::{AttrIdentifier, AttrValue};
use cssparser::{Parser as CssParser, ToCss, serialize_identifier}; use cssparser::{Parser as CssParser, ToCss, serialize_identifier};
use dom::{OpaqueNode, TElement, TNode};
use element_state::ElementState; use element_state::ElementState;
use fnv::FnvHashMap;
use restyle_hints::ElementSnapshot; use restyle_hints::ElementSnapshot;
use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser}; use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser};
use selectors::{Element, MatchAttrGeneric}; use selectors::{Element, MatchAttrGeneric};
@ -20,6 +22,7 @@ use std::borrow::Cow;
use std::fmt; use std::fmt;
use std::fmt::Debug; use std::fmt::Debug;
use std::mem; use std::mem;
use std::ops::{Deref, DerefMut};
/// A pseudo-element, both public and private. /// A pseudo-element, both public and private.
/// ///
@ -448,6 +451,36 @@ impl SelectorImpl {
} }
} }
/// A map from elements to snapshots for the Servo style back-end.
#[derive(Debug)]
pub struct SnapshotMap(FnvHashMap<OpaqueNode, ServoElementSnapshot>);
impl SnapshotMap {
/// Create a new empty `SnapshotMap`.
pub fn new() -> Self {
SnapshotMap(FnvHashMap::default())
}
/// Get a snapshot given an element.
pub fn get<T: TElement>(&self, el: &T) -> Option<&ServoElementSnapshot> {
self.0.get(&el.as_node().opaque())
}
}
impl Deref for SnapshotMap {
type Target = FnvHashMap<OpaqueNode, ServoElementSnapshot>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for SnapshotMap {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
/// Servo's version of an element snapshot. /// Servo's version of an element snapshot.
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]

View file

@ -22,7 +22,7 @@ use properties::INHERIT_ALL;
use properties::PropertyDeclarationBlock; use properties::PropertyDeclarationBlock;
use restyle_hints::{RestyleHint, DependencySet}; use restyle_hints::{RestyleHint, DependencySet};
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource}; use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
use selector_parser::{SelectorImpl, PseudoElement, Snapshot}; use selector_parser::{SelectorImpl, PseudoElement, SnapshotMap};
use selectors::Element; use selectors::Element;
use selectors::bloom::BloomFilter; use selectors::bloom::BloomFilter;
use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS}; use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS};
@ -894,16 +894,16 @@ impl Stylist {
results results
} }
/// Given an element, and a snapshot that represents a previous state of the /// Given an element, and a snapshot table that represents a previous state
/// element, compute the appropriate restyle hint, that is, the kind of /// of the tree, compute the appropriate restyle hint, that is, the kind of
/// restyle we need to do. /// restyle we need to do.
pub fn compute_restyle_hint<E>(&self, pub fn compute_restyle_hint<E>(&self,
element: &E, element: &E,
snapshot: &Snapshot) snapshots: &SnapshotMap)
-> RestyleHint -> RestyleHint
where E: TElement, where E: TElement,
{ {
self.dependencies.compute_hint(element, snapshot) self.dependencies.compute_hint(element, snapshots)
} }
/// Computes styles for a given declaration with parent_style. /// Computes styles for a given declaration with parent_style.

View file

@ -13,7 +13,6 @@ use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_SELF};
use selector_parser::RestyleDamage; use selector_parser::RestyleDamage;
#[cfg(feature = "servo")] use servo_config::opts; #[cfg(feature = "servo")] use servo_config::opts;
use std::borrow::BorrowMut; use std::borrow::BorrowMut;
use stylist::Stylist;
/// A per-traversal-level chunk of data. This is sent down by the traversal, and /// A per-traversal-level chunk of data. This is sent down by the traversal, and
/// currently only holds the dom depth for the bloom filter. /// currently only holds the dom depth for the bloom filter.
@ -200,7 +199,9 @@ pub trait DomTraversal<E: TElement> : Sync {
/// appended children without restyling the parent. /// appended children without restyling the parent.
/// If traversal_flag::ANIMATION_ONLY is specified, style only elements for /// If traversal_flag::ANIMATION_ONLY is specified, style only elements for
/// animations. /// animations.
fn pre_traverse(root: E, stylist: &Stylist, traversal_flags: TraversalFlags) fn pre_traverse(root: E,
shared_context: &SharedStyleContext,
traversal_flags: TraversalFlags)
-> PreTraverseToken -> PreTraverseToken
{ {
debug_assert!(!(traversal_flags.for_reconstruct() && debug_assert!(!(traversal_flags.for_reconstruct() &&
@ -226,14 +227,12 @@ pub trait DomTraversal<E: TElement> : Sync {
// Expanding snapshots here may create a LATER_SIBLINGS restyle hint, which // Expanding snapshots here may create a LATER_SIBLINGS restyle hint, which
// we propagate to the next sibling element. // we propagate to the next sibling element.
if let Some(mut data) = root.mutate_data() { if let Some(mut data) = root.mutate_data() {
if let Some(r) = data.get_restyle_mut() { let later_siblings = data.compute_final_hint(root, shared_context);
let later_siblings = r.compute_final_hint(root, stylist); if later_siblings {
if later_siblings { if let Some(next) = root.next_sibling_element() {
if let Some(next) = root.next_sibling_element() { if let Some(mut next_data) = next.mutate_data() {
if let Some(mut next_data) = next.mutate_data() { let hint = StoredRestyleHint::subtree_and_later_siblings();
let hint = StoredRestyleHint::subtree_and_later_siblings(); next_data.ensure_restyle().hint.insert(&hint);
next_data.ensure_restyle().hint.insert(&hint);
}
} }
} }
} }
@ -375,6 +374,7 @@ pub trait DomTraversal<E: TElement> : Sync {
return true; return true;
} }
trace!("{:?} doesn't need traversal", el);
false false
} }
@ -390,7 +390,7 @@ pub trait DomTraversal<E: TElement> : Sync {
log: LogBehavior) -> bool log: LogBehavior) -> bool
{ {
// See the comment on `cascade_node` for why we allow this on Gecko. // See the comment on `cascade_node` for why we allow this on Gecko.
debug_assert!(cfg!(feature = "gecko") || parent_data.has_current_styles()); debug_assert!(cfg!(feature = "gecko") || parent.has_current_styles(parent_data));
// If the parent computed display:none, we don't style the subtree. // If the parent computed display:none, we don't style the subtree.
if parent_data.styles().is_display_none() { if parent_data.styles().is_display_none() {
@ -597,11 +597,13 @@ pub fn recalc_style_at<E, D>(traversal: &D,
{ {
context.thread_local.begin_element(element, &data); context.thread_local.begin_element(element, &data);
context.thread_local.statistics.elements_traversed += 1; context.thread_local.statistics.elements_traversed += 1;
debug_assert!(!element.has_snapshot() || element.handled_snapshot(),
"Should've handled snapshots here already");
debug_assert!(data.get_restyle().map_or(true, |r| { debug_assert!(data.get_restyle().map_or(true, |r| {
r.snapshot.is_none() && !r.has_sibling_invalidations() !r.has_sibling_invalidations()
}), "Should've computed the final hint and handled later_siblings already"); }), "Should've computed the final hint and handled later_siblings already");
let compute_self = !data.has_current_styles(); let compute_self = !element.has_current_styles(data);
let mut inherited_style_changed = false; let mut inherited_style_changed = false;
debug!("recalc_style_at: {:?} (compute_self={:?}, dirty_descendants={:?}, data={:?})", debug!("recalc_style_at: {:?} (compute_self={:?}, dirty_descendants={:?}, data={:?})",
@ -613,9 +615,9 @@ pub fn recalc_style_at<E, D>(traversal: &D,
// If we're restyling this element to display:none, throw away all style // If we're restyling this element to display:none, throw away all style
// data in the subtree, notify the caller to early-return. // data in the subtree, notify the caller to early-return.
let display_none = data.styles().is_display_none(); if data.styles().is_display_none() {
if display_none { debug!("{:?} style is display:none - clearing data from descendants.",
debug!("New element style is display:none - clearing data from descendants."); element);
clear_descendant_data(element, &|e| unsafe { D::clear_element_data(&e) }); clear_descendant_data(element, &|e| unsafe { D::clear_element_data(&e) });
} }
@ -636,15 +638,15 @@ pub fn recalc_style_at<E, D>(traversal: &D,
r.hint.propagate(&context.shared.traversal_flags) r.hint.propagate(&context.shared.traversal_flags)
}, },
}; };
debug_assert!(data.has_current_styles() ||
context.shared.traversal_flags.for_animation_only(),
"Should have computed style or haven't yet valid computed \
style in case of animation-only restyle");
trace!("propagated_hint={:?}, inherited_style_changed={:?}, \ trace!("propagated_hint={:?}, inherited_style_changed={:?}, \
is_display_none={:?}, implementing_pseudo={:?}", is_display_none={:?}, implementing_pseudo={:?}",
propagated_hint, inherited_style_changed, propagated_hint, inherited_style_changed,
data.styles().is_display_none(), data.styles().is_display_none(),
element.implemented_pseudo_element()); element.implemented_pseudo_element());
debug_assert!(element.has_current_styles(data) ||
context.shared.traversal_flags.for_animation_only(),
"Should have computed style or haven't yet valid computed \
style in case of animation-only restyle");
let has_dirty_descendants_for_this_restyle = let has_dirty_descendants_for_this_restyle =
if context.shared.traversal_flags.for_animation_only() { if context.shared.traversal_flags.for_animation_only() {
@ -779,6 +781,18 @@ fn preprocess_children<E, D>(traversal: &D,
continue; continue;
} }
// Handle element snapshots and sibling restyle hints.
//
// NB: This will be a no-op if there's no restyle data and no snapshot.
let later_siblings =
child_data.compute_final_hint(child, traversal.shared_context());
trace!(" > {:?} -> {:?} + {:?}, later_siblings: {:?}",
child,
child_data.get_restyle().map(|r| &r.hint),
propagated_hint,
later_siblings);
// If the child doesn't have pre-existing RestyleData and we don't have // If the child doesn't have pre-existing RestyleData and we don't have
// any reason to create one, avoid the useless allocation and move on to // any reason to create one, avoid the useless allocation and move on to
// the next child. // the next child.
@ -786,6 +800,7 @@ fn preprocess_children<E, D>(traversal: &D,
damage_handled.is_empty() && !child_data.has_restyle() { damage_handled.is_empty() && !child_data.has_restyle() {
continue; continue;
} }
let mut restyle_data = child_data.ensure_restyle(); let mut restyle_data = child_data.ensure_restyle();
// Propagate the parent and sibling restyle hint. // Propagate the parent and sibling restyle hint.
@ -793,11 +808,6 @@ fn preprocess_children<E, D>(traversal: &D,
restyle_data.hint.insert(&propagated_hint); restyle_data.hint.insert(&propagated_hint);
} }
// Handle element snapshots and sibling restyle hints.
let stylist = &traversal.shared_context().stylist;
let later_siblings = restyle_data.compute_final_hint(child, stylist);
trace!(" > {:?} -> {:?}, later_siblings: {:?}",
child, restyle_data.hint, later_siblings);
if later_siblings { if later_siblings {
propagated_hint.insert(&(RESTYLE_SELF | RESTYLE_DESCENDANTS).into()); propagated_hint.insert(&(RESTYLE_SELF | RESTYLE_DESCENDANTS).into());
} }
@ -805,9 +815,11 @@ fn preprocess_children<E, D>(traversal: &D,
// Store the damage already handled by ancestors. // Store the damage already handled by ancestors.
restyle_data.set_damage_handled(damage_handled); restyle_data.set_damage_handled(damage_handled);
// If properties that we inherited from the parent changed, we need to recascade. // If properties that we inherited from the parent changed, we need to
// recascade.
// //
// FIXME(bholley): Need to handle explicitly-inherited reset properties somewhere. // FIXME(bholley): Need to handle explicitly-inherited reset properties
// somewhere.
if parent_inherited_style_changed { if parent_inherited_style_changed {
restyle_data.recascade = true; restyle_data.recascade = true;
} }

View file

@ -61,6 +61,7 @@ use style::gecko_bindings::structs::{SheetParsingMode, nsIAtom, nsCSSPropertyID}
use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, nsCSSFontFaceRule}; use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, nsCSSFontFaceRule};
use style::gecko_bindings::structs::Loader; use style::gecko_bindings::structs::Loader;
use style::gecko_bindings::structs::RawGeckoPresContextOwned; use style::gecko_bindings::structs::RawGeckoPresContextOwned;
use style::gecko_bindings::structs::ServoElementSnapshotTable;
use style::gecko_bindings::structs::URLExtraData; use style::gecko_bindings::structs::URLExtraData;
use style::gecko_bindings::structs::nsCSSValueSharedList; use style::gecko_bindings::structs::nsCSSValueSharedList;
use style::gecko_bindings::structs::nsresult; use style::gecko_bindings::structs::nsresult;
@ -149,7 +150,9 @@ unsafe fn dummy_url_data() -> &'static RefPtr<URLExtraData> {
fn create_shared_context<'a>(global_style_data: &GlobalStyleData, fn create_shared_context<'a>(global_style_data: &GlobalStyleData,
guard: &'a SharedRwLockReadGuard, guard: &'a SharedRwLockReadGuard,
per_doc_data: &PerDocumentStyleDataImpl, per_doc_data: &PerDocumentStyleDataImpl,
traversal_flags: TraversalFlags) -> SharedStyleContext<'a> { traversal_flags: TraversalFlags,
snapshot_map: &'a ServoElementSnapshotTable)
-> SharedStyleContext<'a> {
let local_context_data = let local_context_data =
ThreadLocalStyleContextCreationInfo::new(per_doc_data.new_animations_sender.clone()); ThreadLocalStyleContextCreationInfo::new(per_doc_data.new_animations_sender.clone());
@ -166,12 +169,14 @@ fn create_shared_context<'a>(global_style_data: &GlobalStyleData,
// FIXME Find the real QuirksMode information for this document // FIXME Find the real QuirksMode information for this document
quirks_mode: QuirksMode::NoQuirks, quirks_mode: QuirksMode::NoQuirks,
traversal_flags: traversal_flags, traversal_flags: traversal_flags,
snapshot_map: snapshot_map,
} }
} }
fn traverse_subtree(element: GeckoElement, fn traverse_subtree(element: GeckoElement,
raw_data: RawServoStyleSetBorrowed, raw_data: RawServoStyleSetBorrowed,
traversal_flags: TraversalFlags) { traversal_flags: TraversalFlags,
snapshots: &ServoElementSnapshotTable) {
// When new content is inserted in a display:none subtree, we will call into // When new content is inserted in a display:none subtree, we will call into
// servo to try to style it. Detect that here and bail out. // servo to try to style it. Detect that here and bail out.
if let Some(parent) = element.parent_element() { if let Some(parent) = element.parent_element() {
@ -184,7 +189,18 @@ fn traverse_subtree(element: GeckoElement,
let per_doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow(); let per_doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow();
debug_assert!(!per_doc_data.stylesheets.has_changed()); debug_assert!(!per_doc_data.stylesheets.has_changed());
let token = RecalcStyleOnly::pre_traverse(element, &per_doc_data.stylist, traversal_flags); let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let shared_style_context = create_shared_context(&global_style_data,
&guard,
&per_doc_data,
traversal_flags,
snapshots);
let token = RecalcStyleOnly::pre_traverse(element,
&shared_style_context,
traversal_flags);
if !token.should_traverse() { if !token.should_traverse() {
return; return;
} }
@ -192,13 +208,6 @@ fn traverse_subtree(element: GeckoElement,
debug!("Traversing subtree:"); debug!("Traversing subtree:");
debug!("{:?}", ShowSubtreeData(element.as_node())); debug!("{:?}", ShowSubtreeData(element.as_node()));
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let shared_style_context = create_shared_context(&global_style_data,
&guard,
&per_doc_data,
traversal_flags);
let traversal_driver = if global_style_data.style_thread_pool.is_none() { let traversal_driver = if global_style_data.style_thread_pool.is_none() {
TraversalDriver::Sequential TraversalDriver::Sequential
} else { } else {
@ -214,17 +223,20 @@ fn traverse_subtree(element: GeckoElement,
} }
} }
/// Traverses the subtree rooted at `root` for restyling. Returns whether a /// Traverses the subtree rooted at `root` for restyling.
/// Gecko post-traversal (to perform lazy frame construction, or consume any ///
/// RestyleData, or drop any ElementData) is required. /// Returns whether a Gecko post-traversal (to perform lazy frame construction,
/// or consume any RestyleData, or drop any ElementData) is required.
#[no_mangle] #[no_mangle]
pub extern "C" fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed, pub extern "C" fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed,
raw_data: RawServoStyleSetBorrowed, raw_data: RawServoStyleSetBorrowed,
snapshots: *const ServoElementSnapshotTable,
root_behavior: structs::TraversalRootBehavior, root_behavior: structs::TraversalRootBehavior,
restyle_behavior: structs::TraversalRestyleBehavior) restyle_behavior: structs::TraversalRestyleBehavior)
-> bool { -> bool {
use self::structs::TraversalRestyleBehavior as Restyle; use self::structs::TraversalRestyleBehavior as Restyle;
use self::structs::TraversalRootBehavior as Root; use self::structs::TraversalRootBehavior as Root;
debug_assert!(!snapshots.is_null());
let element = GeckoElement(root); let element = GeckoElement(root);
debug!("Servo_TraverseSubtree: {:?}", element); debug!("Servo_TraverseSubtree: {:?}", element);
@ -238,12 +250,18 @@ pub extern "C" fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed,
if element.has_animation_only_dirty_descendants() || if element.has_animation_only_dirty_descendants() ||
element.has_animation_restyle_hints() { element.has_animation_restyle_hints() {
traverse_subtree(element, raw_data, traversal_flags | ANIMATION_ONLY); traverse_subtree(element,
raw_data,
traversal_flags | ANIMATION_ONLY,
unsafe { &*snapshots });
} }
traverse_subtree(element, raw_data, traversal_flags); traverse_subtree(element,
raw_data,
traversal_flags,
unsafe { &*snapshots });
element.has_dirty_descendants() || element.mutate_data().unwrap().has_restyle() element.has_dirty_descendants() || element.borrow_data().unwrap().has_restyle()
} }
#[no_mangle] #[no_mangle]
@ -436,18 +454,21 @@ pub extern "C" fn Servo_AnimationValue_Uncompute(value: RawServoAnimationValueBo
#[no_mangle] #[no_mangle]
pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(raw_data: RawServoStyleSetBorrowed, pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(raw_data: RawServoStyleSetBorrowed,
element: RawGeckoElementBorrowed, element: RawGeckoElementBorrowed,
snapshots: *const ServoElementSnapshotTable,
pseudo_tag: *mut nsIAtom) pseudo_tag: *mut nsIAtom)
-> ServoComputedValuesStrong -> ServoComputedValuesStrong
{ {
use style::matching::MatchMethods; use style::matching::MatchMethods;
debug_assert!(!snapshots.is_null());
let doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow(); let doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow();
let global_style_data = &*GLOBAL_STYLE_DATA; let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read(); let guard = global_style_data.shared_lock.read();
let shared_context = &create_shared_context(&global_style_data, let shared_context = create_shared_context(&global_style_data,
&guard, &guard,
&doc_data, &doc_data,
TraversalFlags::empty()); TraversalFlags::empty(),
unsafe { &*snapshots });
let element = GeckoElement(element); let element = GeckoElement(element);
let element_data = element.borrow_data().unwrap(); let element_data = element.borrow_data().unwrap();
let styles = element_data.styles(); let styles = element_data.styles();
@ -469,7 +490,10 @@ pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(raw_data: RawSe
}; };
let provider = get_metrics_provider_for_product(); let provider = get_metrics_provider_for_product();
element.get_base_style(shared_context, &provider, &styles.primary, pseudo_style) element.get_base_style(&shared_context,
&provider,
&styles.primary,
pseudo_style)
.into_strong() .into_strong()
} }
@ -1867,10 +1891,12 @@ unsafe fn maybe_restyle<'a>(data: &'a mut AtomicRefMut<ElementData>,
} }
// Propagate the bit up the chain. // Propagate the bit up the chain.
if animation_only { if let Some(p) = element.parent_element() {
element.parent_element().map(|p| p.note_descendants::<AnimationOnlyDirtyDescendants>()); if animation_only {
} else { p.note_descendants::<AnimationOnlyDirtyDescendants>();
element.parent_element().map(|p| p.note_descendants::<DirtyDescendants>()); } else {
p.note_descendants::<DirtyDescendants>();
}
}; };
bindings::Gecko_SetOwnerDocumentNeedsStyleFlush(element.0); bindings::Gecko_SetOwnerDocumentNeedsStyleFlush(element.0);
@ -1879,25 +1905,6 @@ unsafe fn maybe_restyle<'a>(data: &'a mut AtomicRefMut<ElementData>,
Some(data.ensure_restyle()) Some(data.ensure_restyle())
} }
#[no_mangle]
pub extern "C" fn Servo_Element_GetSnapshot(element: RawGeckoElementBorrowed) -> *mut structs::ServoElementSnapshot
{
let element = GeckoElement(element);
let snapshot = match element.mutate_data() {
None => ptr::null_mut(),
Some(mut data) => {
if let Some(restyle_data) = unsafe { maybe_restyle(&mut data, element, false) } {
restyle_data.snapshot.ensure(|| element.create_snapshot()).borrow_mut_raw()
} else {
ptr::null_mut()
}
},
};
debug!("Servo_Element_GetSnapshot: {:?}: {:?}", element, snapshot);
snapshot
}
#[no_mangle] #[no_mangle]
pub extern "C" fn Servo_Element_GetStyleRuleList(element: RawGeckoElementBorrowed, pub extern "C" fn Servo_Element_GetStyleRuleList(element: RawGeckoElementBorrowed,
rules: RawGeckoServoStyleRuleListBorrowedMut) { rules: RawGeckoServoStyleRuleListBorrowedMut) {
@ -1982,12 +1989,12 @@ pub extern "C" fn Servo_ResolveStyle(element: RawGeckoElementBorrowed,
{ {
let element = GeckoElement(element); let element = GeckoElement(element);
debug!("Servo_ResolveStyle: {:?}", element); debug!("Servo_ResolveStyle: {:?}", element);
let data = unsafe { element.ensure_data() }.borrow_mut(); let data = unsafe { element.ensure_data() }.borrow();
let valid_styles = if allow_stale { let valid_styles = if allow_stale {
data.has_styles() data.has_styles()
} else { } else {
data.has_current_styles() element.has_current_styles(&*data)
}; };
if !valid_styles { if !valid_styles {
@ -2003,9 +2010,11 @@ pub extern "C" fn Servo_ResolveStyle(element: RawGeckoElementBorrowed,
#[no_mangle] #[no_mangle]
pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed, pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
pseudo_tag: *mut nsIAtom, pseudo_tag: *mut nsIAtom,
snapshots: *const ServoElementSnapshotTable,
raw_data: RawServoStyleSetBorrowed) raw_data: RawServoStyleSetBorrowed)
-> ServoComputedValuesStrong -> ServoComputedValuesStrong
{ {
debug_assert!(!snapshots.is_null());
let global_style_data = &*GLOBAL_STYLE_DATA; let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read(); let guard = global_style_data.shared_lock.read();
let element = GeckoElement(element); let element = GeckoElement(element);
@ -2031,7 +2040,8 @@ pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
let shared = create_shared_context(&global_style_data, let shared = create_shared_context(&global_style_data,
&guard, &guard,
&mut doc_data.borrow_mut(), &mut doc_data.borrow_mut(),
TraversalFlags::empty()); TraversalFlags::empty(),
unsafe { &*snapshots });
let mut tlc = ThreadLocalStyleContext::new(&shared); let mut tlc = ThreadLocalStyleContext::new(&shared);
let mut context = StyleContext { let mut context = StyleContext {
shared: &shared, shared: &shared,