mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
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:
commit
c8171ed5d7
22 changed files with 2194 additions and 1137 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -164,13 +164,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.24.0"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"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)",
|
||||
"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)",
|
||||
"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)",
|
||||
|
@ -357,7 +357,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "0.16.0"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2784,7 +2784,7 @@ version = "0.0.1"
|
|||
dependencies = [
|
||||
"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)",
|
||||
"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)",
|
||||
"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)",
|
||||
|
@ -3383,7 +3383,7 @@ dependencies = [
|
|||
"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 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-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"
|
||||
|
@ -3402,7 +3402,7 @@ dependencies = [
|
|||
"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 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 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"
|
||||
|
|
|
@ -113,6 +113,7 @@ use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TElement, TNode};
|
|||
use style::error_reporting::{NullReporter, RustLogReporter};
|
||||
use style::logical_geometry::LogicalPoint;
|
||||
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::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards};
|
||||
use style::stylearc::Arc as StyleArc;
|
||||
|
@ -509,7 +510,8 @@ impl LayoutThread {
|
|||
fn build_layout_context<'a>(&self,
|
||||
guards: StylesheetGuards<'a>,
|
||||
rw_data: &LayoutThreadData,
|
||||
request_images: bool)
|
||||
request_images: bool,
|
||||
snapshot_map: &'a SnapshotMap)
|
||||
-> LayoutContext<'a> {
|
||||
let thread_local_style_context_creation_data =
|
||||
ThreadLocalStyleContextCreationInfo::new(self.new_animations_sender.clone());
|
||||
|
@ -527,6 +529,7 @@ impl LayoutThread {
|
|||
timer: self.timer.clone(),
|
||||
quirks_mode: self.quirks_mode.unwrap(),
|
||||
traversal_flags: TraversalFlags::empty(),
|
||||
snapshot_map: snapshot_map,
|
||||
},
|
||||
image_cache: self.image_cache.clone(),
|
||||
font_cache_thread: Mutex::new(self.font_cache_thread.clone()),
|
||||
|
@ -1111,37 +1114,51 @@ impl LayoutThread {
|
|||
}
|
||||
|
||||
let restyles = document.drain_pending_restyles();
|
||||
debug!("Draining restyles: {} (needs dirtying? {:?})", restyles.len(), needs_dirtying);
|
||||
if !needs_dirtying {
|
||||
for (el, restyle) in restyles {
|
||||
// Propagate the descendant bit up the ancestors. Do this before
|
||||
// the restyle calculation so that we can also do it for new
|
||||
// unstyled nodes, which the descendants bit helps us find.
|
||||
if let Some(parent) = el.parent_element() {
|
||||
unsafe { parent.note_dirty_descendant() };
|
||||
}
|
||||
debug!("Draining restyles: {} (needs dirtying? {:?})",
|
||||
restyles.len(), needs_dirtying);
|
||||
let mut map = SnapshotMap::new();
|
||||
let elements_with_snapshot: Vec<_> =
|
||||
restyles
|
||||
.iter()
|
||||
.filter(|r| r.1.snapshot.is_some())
|
||||
.map(|r| r.0)
|
||||
.collect();
|
||||
|
||||
// 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 => continue,
|
||||
};
|
||||
let mut style_data = &mut data.base.style_data;
|
||||
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);
|
||||
for (el, restyle) in restyles {
|
||||
// Propagate the descendant bit up the ancestors. Do this before
|
||||
// the restyle calculation so that we can also do it for new
|
||||
// unstyled nodes, which the descendants bit helps us find.
|
||||
if let Some(parent) = el.parent_element() {
|
||||
unsafe { parent.note_dirty_descendant() };
|
||||
}
|
||||
|
||||
// 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.
|
||||
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. :-(
|
||||
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 token = {
|
||||
let stylist = &<RecalcStyleAndConstructFlows as
|
||||
DomTraversal<ServoLayoutElement>>::shared_context(&traversal).stylist;
|
||||
let context = <RecalcStyleAndConstructFlows as
|
||||
DomTraversal<ServoLayoutElement>>::shared_context(&traversal);
|
||||
<RecalcStyleAndConstructFlows as
|
||||
DomTraversal<ServoLayoutElement>>::pre_traverse(element, stylist, TraversalFlags::empty())
|
||||
DomTraversal<ServoLayoutElement>>::pre_traverse(element,
|
||||
context,
|
||||
TraversalFlags::empty())
|
||||
};
|
||||
|
||||
if token.should_traverse() {
|
||||
|
@ -1192,6 +1211,10 @@ impl LayoutThread {
|
|||
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();
|
||||
|
||||
if opts::get().dump_style_tree {
|
||||
|
@ -1382,7 +1405,11 @@ impl LayoutThread {
|
|||
author: &author_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.
|
||||
|
|
|
@ -144,29 +144,40 @@ pub struct Node {
|
|||
bitflags! {
|
||||
#[doc = "Flags for node items."]
|
||||
#[derive(JSTraceable, HeapSizeOf)]
|
||||
pub flags NodeFlags: u8 {
|
||||
pub flags NodeFlags: u16 {
|
||||
#[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."]
|
||||
const HAS_DIRTY_DESCENDANTS = 0x08,
|
||||
const HAS_DIRTY_DESCENDANTS = 1 << 1,
|
||||
// TODO: find a better place to keep this (#4105)
|
||||
// https://critic.hoppipolla.co.uk/showcomment?chain=8873
|
||||
// Perhaps using a Set in Document?
|
||||
#[doc = "Specifies whether or not there is an authentic click in progress on \
|
||||
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 \
|
||||
to be reachable with using sequential focus navigation."]
|
||||
const SEQUENTIALLY_FOCUSABLE = 0x20,
|
||||
const SEQUENTIALLY_FOCUSABLE = 1 << 3,
|
||||
|
||||
/// 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."]
|
||||
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 \
|
||||
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() {
|
||||
// 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() {
|
||||
// This needs to be in its own loop, because unbind_from_tree may
|
||||
|
|
|
@ -39,6 +39,7 @@ use dom::characterdata::LayoutCharacterDataHelpers;
|
|||
use dom::document::{Document, LayoutDocumentHelpers, PendingRestyle};
|
||||
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::{HANDLED_SNAPSHOT, HAS_SNAPSHOT};
|
||||
use dom::node::{LayoutNodeHelpers, Node};
|
||||
use dom::text::Text;
|
||||
use gfx_traits::ByteIndex;
|
||||
|
@ -413,6 +414,18 @@ impl<'le> TElement for ServoLayoutElement<'le> {
|
|||
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) {
|
||||
debug_assert!(self.get_data().is_some());
|
||||
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,
|
||||
// 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.
|
||||
|
|
|
@ -65,6 +65,6 @@ kernel32-sys = "0.2"
|
|||
[build-dependencies]
|
||||
lazy_static = "0.2"
|
||||
log = "0.3"
|
||||
bindgen = { version = "0.24", optional = true }
|
||||
bindgen = { version = "0.25", optional = true }
|
||||
regex = {version = "0.2", optional = true}
|
||||
walkdir = "1.0"
|
||||
|
|
|
@ -305,6 +305,7 @@ mod bindings {
|
|||
.include(add_include("mozilla/ComputedTimingFunction.h"))
|
||||
.include(add_include("mozilla/Keyframe.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/NameSpaceConstants.h"))
|
||||
.include(add_include("mozilla/LookAndFeel.h"))
|
||||
|
@ -329,6 +330,7 @@ mod bindings {
|
|||
"NS_AUTHOR_SPECIFIED_.*",
|
||||
"NS_THEME_.*",
|
||||
"NODE_.*",
|
||||
"ELEMENT_.*",
|
||||
"NS_FONT_.*",
|
||||
"NS_STYLE_.*",
|
||||
"NS_MATHML_.*",
|
||||
|
@ -670,6 +672,7 @@ mod bindings {
|
|||
"Keyframe",
|
||||
"ServoBundledURI",
|
||||
"ServoElementSnapshot",
|
||||
"ServoElementSnapshotTable",
|
||||
"SheetParsingMode",
|
||||
"StyleBasicShape",
|
||||
"StyleBasicShapeType",
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! The context within which style is calculated.
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use animation::{Animation, PropertyAnimation};
|
||||
use app_units::Au;
|
||||
|
@ -20,6 +19,7 @@ use font_metrics::FontMetricsProvider;
|
|||
use matching::StyleSharingCandidateCache;
|
||||
use parking_lot::RwLock;
|
||||
#[cfg(feature = "gecko")] use properties::ComputedValues;
|
||||
use selector_parser::SnapshotMap;
|
||||
use selectors::matching::ElementSelectorFlags;
|
||||
#[cfg(feature = "servo")] use servo_config::opts;
|
||||
use shared_lock::StylesheetGuards;
|
||||
|
@ -135,6 +135,9 @@ pub struct SharedStyleContext<'a> {
|
|||
|
||||
/// Flags controlling how we traverse the tree.
|
||||
pub traversal_flags: TraversalFlags,
|
||||
|
||||
/// A map with our snapshots in order to handle restyle hints.
|
||||
pub snapshot_map: &'a SnapshotMap,
|
||||
}
|
||||
|
||||
impl<'a> SharedStyleContext<'a> {
|
||||
|
|
|
@ -6,19 +6,17 @@
|
|||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use context::SharedStyleContext;
|
||||
use dom::TElement;
|
||||
use properties::ComputedValues;
|
||||
use properties::longhands::display::computed_value as display;
|
||||
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint};
|
||||
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;
|
||||
use std::fmt;
|
||||
#[cfg(feature = "servo")] use std::hash::BuildHasherDefault;
|
||||
use std::ops::Deref;
|
||||
use stylearc::Arc;
|
||||
use stylist::Stylist;
|
||||
use thread_state;
|
||||
use traversal::TraversalFlags;
|
||||
|
||||
/// 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
|
||||
// propagate any restyle hints, and we need to remove ourselves.
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
/// either before or during restyle traversal, and is cleared at the end of node
|
||||
/// processing.
|
||||
|
@ -350,54 +297,17 @@ pub struct RestyleData {
|
|||
/// for Servo for now.
|
||||
#[cfg(feature = "gecko")]
|
||||
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 {
|
||||
/// 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.
|
||||
pub fn has_invalidations(&self) -> bool {
|
||||
self.hint.has_self_invalidations() ||
|
||||
self.recascade ||
|
||||
self.snapshot.is_some()
|
||||
self.hint.has_self_invalidations() || self.recascade
|
||||
}
|
||||
|
||||
/// Returns true if this RestyleData might invalidate sibling styles.
|
||||
pub fn has_sibling_invalidations(&self) -> bool {
|
||||
self.hint.has_sibling_invalidations() || self.snapshot.is_some()
|
||||
self.hint.has_sibling_invalidations()
|
||||
}
|
||||
|
||||
/// Returns damage handled.
|
||||
|
@ -452,6 +362,51 @@ pub enum RestyleKind {
|
|||
}
|
||||
|
||||
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.
|
||||
pub fn new(existing: Option<ElementStyles>) -> Self {
|
||||
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 {
|
||||
self.styles.is_some()
|
||||
}
|
||||
|
||||
/// Returns true if this element's style is up-to-date and has no potential
|
||||
/// invalidation.
|
||||
pub fn has_current_styles(&self) -> bool {
|
||||
self.has_styles() &&
|
||||
self.restyle.as_ref().map_or(true, |r| !r.has_invalidations())
|
||||
/// Returns whether we have any outstanding style invalidation.
|
||||
pub fn has_invalidations(&self) -> bool {
|
||||
self.restyle.as_ref().map_or(false, |r| r.has_invalidations())
|
||||
}
|
||||
|
||||
/// Returns the kind of restyling that we're going to need to do on this
|
||||
/// element, based of the stored restyle hint.
|
||||
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() {
|
||||
return RestyleKind::MatchAndCascade;
|
||||
}
|
||||
|
@ -527,8 +481,6 @@ impl ElementData {
|
|||
|
||||
/// Sets the computed element styles.
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -385,6 +385,26 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
|
|||
/// the actual restyle traversal.
|
||||
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`.
|
||||
///
|
||||
/// TODO(emilio): We call this conservatively from restyle_element_internal
|
||||
|
|
|
@ -37,6 +37,7 @@ use gecko_bindings::structs::GeckoFontMetrics;
|
|||
use gecko_bindings::structs::Keyframe;
|
||||
use gecko_bindings::structs::ServoBundledURI;
|
||||
use gecko_bindings::structs::ServoElementSnapshot;
|
||||
use gecko_bindings::structs::ServoElementSnapshotTable;
|
||||
use gecko_bindings::structs::SheetParsingMode;
|
||||
use gecko_bindings::structs::StyleBasicShape;
|
||||
use gecko_bindings::structs::StyleBasicShapeType;
|
||||
|
@ -902,8 +903,9 @@ extern "C" {
|
|||
-> nsChangeHint;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Gecko_CreateElementSnapshot(element: RawGeckoElementBorrowed)
|
||||
-> ServoElementSnapshotOwned;
|
||||
pub fn Gecko_GetElementSnapshot(table: *const ServoElementSnapshotTable,
|
||||
element: RawGeckoElementBorrowed)
|
||||
-> *const ServoElementSnapshot;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Gecko_DropElementSnapshot(snapshot: ServoElementSnapshotOwned);
|
||||
|
@ -2171,10 +2173,6 @@ extern "C" {
|
|||
extern "C" {
|
||||
pub fn Servo_Shutdown();
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Servo_Element_GetSnapshot(element: RawGeckoElementBorrowed)
|
||||
-> *mut ServoElementSnapshot;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Servo_Element_GetStyleRuleList(element: RawGeckoElementBorrowed,
|
||||
rules:
|
||||
|
@ -2215,12 +2213,15 @@ extern "C" {
|
|||
extern "C" {
|
||||
pub fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
|
||||
pseudo_tag: *mut nsIAtom,
|
||||
snapshots:
|
||||
*const ServoElementSnapshotTable,
|
||||
set: RawServoStyleSetBorrowed)
|
||||
-> ServoComputedValuesStrong;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed,
|
||||
set: RawServoStyleSetBorrowed,
|
||||
snapshots: *const ServoElementSnapshotTable,
|
||||
root_behavior: TraversalRootBehavior,
|
||||
restyle_behavior: TraversalRestyleBehavior)
|
||||
-> bool;
|
||||
|
@ -2233,6 +2234,8 @@ extern "C" {
|
|||
RawServoStyleSetBorrowed,
|
||||
element:
|
||||
RawGeckoElementBorrowed,
|
||||
snapshots:
|
||||
*const ServoElementSnapshotTable,
|
||||
pseudo_tag:
|
||||
*mut nsIAtom)
|
||||
-> ServoComputedValuesStrong;
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -16,6 +16,8 @@ use std::fmt;
|
|||
use std::ptr;
|
||||
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
|
||||
|
||||
pub use gecko::snapshot::SnapshotMap;
|
||||
|
||||
/// A representation of a CSS pseudo-element.
|
||||
///
|
||||
/// In Gecko, we represent pseudo-elements as plain `Atom`s.
|
||||
|
|
|
@ -5,61 +5,60 @@
|
|||
//! A gecko snapshot, that stores the element attributes and state before they
|
||||
//! change in order to properly calculate restyle hints.
|
||||
|
||||
use dom::TElement;
|
||||
use element_state::ElementState;
|
||||
use gecko::snapshot_helpers;
|
||||
use gecko::wrapper::{AttrSelectorHelpers, GeckoElement};
|
||||
use gecko_bindings::bindings;
|
||||
use gecko_bindings::structs::ServoElementSnapshot;
|
||||
use gecko_bindings::structs::ServoElementSnapshotFlags as Flags;
|
||||
use gecko_bindings::structs::ServoElementSnapshotTable;
|
||||
use restyle_hints::ElementSnapshot;
|
||||
use selector_parser::SelectorImpl;
|
||||
use selectors::parser::AttrSelector;
|
||||
use std::ptr;
|
||||
use string_cache::Atom;
|
||||
|
||||
/// A snapshot of a Gecko element.
|
||||
///
|
||||
/// This is really a Gecko type (see `ServoElementSnapshot.h` in Gecko) we wrap
|
||||
/// here.
|
||||
#[derive(Debug)]
|
||||
pub struct GeckoElementSnapshot(bindings::ServoElementSnapshotOwned);
|
||||
pub type GeckoElementSnapshot = ServoElementSnapshot;
|
||||
|
||||
// FIXME(bholley): Add support for *OwnedConst type, and then we get Sync
|
||||
// automatically.
|
||||
unsafe impl Sync for GeckoElementSnapshot {}
|
||||
/// A map from elements to snapshots for Gecko's style back-end.
|
||||
pub type SnapshotMap = ServoElementSnapshotTable;
|
||||
|
||||
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 {
|
||||
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 {
|
||||
/// 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]
|
||||
fn is_html_element_in_html_document(&self) -> bool {
|
||||
unsafe { (*self.0).mIsHTMLElementInHTMLDocument }
|
||||
self.mIsHTMLElementInHTMLDocument
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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 {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotHasAttr(self.ptr(),
|
||||
bindings::Gecko_SnapshotHasAttr(self,
|
||||
attr.ns_or_null(),
|
||||
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 {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrEquals(self.ptr(),
|
||||
bindings::Gecko_SnapshotAttrEquals(self,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
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 {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrEquals(self.ptr(),
|
||||
bindings::Gecko_SnapshotAttrEquals(self,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr(),
|
||||
|
@ -95,7 +94,7 @@ impl ::selectors::MatchAttr for GeckoElementSnapshot {
|
|||
}
|
||||
fn match_attr_includes(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrIncludes(self.ptr(),
|
||||
bindings::Gecko_SnapshotAttrIncludes(self,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
|
@ -103,7 +102,7 @@ impl ::selectors::MatchAttr for GeckoElementSnapshot {
|
|||
}
|
||||
fn match_attr_dash(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrDashEquals(self.ptr(),
|
||||
bindings::Gecko_SnapshotAttrDashEquals(self,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
|
@ -111,7 +110,7 @@ impl ::selectors::MatchAttr for GeckoElementSnapshot {
|
|||
}
|
||||
fn match_attr_prefix(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrHasPrefix(self.ptr(),
|
||||
bindings::Gecko_SnapshotAttrHasPrefix(self,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
|
@ -119,7 +118,7 @@ impl ::selectors::MatchAttr for GeckoElementSnapshot {
|
|||
}
|
||||
fn match_attr_substring(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrHasSubstring(self.ptr(),
|
||||
bindings::Gecko_SnapshotAttrHasSubstring(self,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
|
@ -127,7 +126,7 @@ impl ::selectors::MatchAttr for GeckoElementSnapshot {
|
|||
}
|
||||
fn match_attr_suffix(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrHasSuffix(self.ptr(),
|
||||
bindings::Gecko_SnapshotAttrHasSuffix(self,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
|
@ -138,7 +137,7 @@ impl ::selectors::MatchAttr for GeckoElementSnapshot {
|
|||
impl ElementSnapshot for GeckoElementSnapshot {
|
||||
fn state(&self) -> Option<ElementState> {
|
||||
if self.has_any(Flags::State) {
|
||||
Some(ElementState::from_bits_truncate(unsafe { (*self.0).mState }))
|
||||
Some(ElementState::from_bits_truncate(self.mState))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -151,7 +150,7 @@ impl ElementSnapshot for GeckoElementSnapshot {
|
|||
|
||||
fn id_attr(&self) -> Option<Atom> {
|
||||
let ptr = unsafe {
|
||||
bindings::Gecko_SnapshotAtomAttrValue(self.ptr(),
|
||||
bindings::Gecko_SnapshotAtomAttrValue(self,
|
||||
atom!("id").as_ptr())
|
||||
};
|
||||
|
||||
|
@ -163,7 +162,7 @@ impl ElementSnapshot for GeckoElementSnapshot {
|
|||
}
|
||||
|
||||
fn has_class(&self, name: &Atom) -> bool {
|
||||
snapshot_helpers::has_class(self.ptr(),
|
||||
snapshot_helpers::has_class(self.as_ptr(),
|
||||
name,
|
||||
bindings::Gecko_SnapshotClassOrClassList)
|
||||
}
|
||||
|
@ -171,7 +170,7 @@ impl ElementSnapshot for GeckoElementSnapshot {
|
|||
fn each_class<F>(&self, callback: F)
|
||||
where F: FnMut(&Atom)
|
||||
{
|
||||
snapshot_helpers::each_class(self.ptr(),
|
||||
snapshot_helpers::each_class(self.as_ptr(),
|
||||
callback,
|
||||
bindings::Gecko_SnapshotClassOrClassList)
|
||||
}
|
||||
|
|
|
@ -47,9 +47,11 @@ use gecko_bindings::bindings::Gecko_UpdateAnimations;
|
|||
use gecko_bindings::structs;
|
||||
use gecko_bindings::structs::{RawGeckoElement, RawGeckoNode};
|
||||
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::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_NATIVE_ANONYMOUS;
|
||||
use gecko_bindings::sugar::ownership::HasArcFFI;
|
||||
|
@ -60,7 +62,7 @@ use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock};
|
|||
use properties::animated_properties::{AnimationValue, AnimationValueMap, TransitionProperty};
|
||||
use properties::style_structs::Font;
|
||||
use rule_tree::CascadeLevel as ServoCascadeLevel;
|
||||
use selector_parser::{ElementExt, Snapshot};
|
||||
use selector_parser::ElementExt;
|
||||
use selectors::Element;
|
||||
use selectors::matching::{ElementSelectorFlags, StyleRelations};
|
||||
use selectors::parser::{AttrSelector, NamespaceConstraint};
|
||||
|
@ -361,6 +363,10 @@ impl<'le> GeckoElement<'le> {
|
|||
/// Clear the element data for a given element.
|
||||
pub fn clear_data(&self) {
|
||||
let ptr = self.0.mServoData.get();
|
||||
unsafe {
|
||||
self.unset_flags(ELEMENT_HAS_SNAPSHOT as u32 |
|
||||
ELEMENT_HANDLED_SNAPSHOT as u32);
|
||||
}
|
||||
if !ptr.is_null() {
|
||||
debug!("Dropping ElementData for {:?}", self);
|
||||
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
|
||||
|
@ -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 {
|
||||
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) {
|
||||
debug_assert!(self.get_data().is_some());
|
||||
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) {
|
||||
|
@ -617,19 +631,19 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
}
|
||||
|
||||
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 {
|
||||
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) {
|
||||
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) {
|
||||
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 {
|
||||
|
|
|
@ -85,6 +85,7 @@ impl<T: RefCounted> RefPtr<T> {
|
|||
pub fn forget(self) -> structs::RefPtr<T> {
|
||||
let ret = structs::RefPtr {
|
||||
mRawPtr: self.ptr,
|
||||
_phantom_0: PhantomData,
|
||||
};
|
||||
mem::forget(self);
|
||||
ret
|
||||
|
|
|
@ -196,7 +196,7 @@ fn element_matches_candidate<E: TElement>(element: &E,
|
|||
}
|
||||
|
||||
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();
|
||||
|
||||
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
|
||||
// content in order to do something specific with that frame,
|
||||
// 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()
|
||||
});
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ use element_state::*;
|
|||
use gecko_bindings::structs::nsRestyleHint;
|
||||
#[cfg(feature = "servo")]
|
||||
use heapsize::HeapSizeOf;
|
||||
use selector_parser::{AttrValue, NonTSPseudoClass, Snapshot, SelectorImpl};
|
||||
use selector_parser::{AttrValue, NonTSPseudoClass, SelectorImpl, Snapshot, SnapshotMap};
|
||||
use selectors::{Element, MatchAttr};
|
||||
use selectors::matching::{ElementSelectorFlags, StyleRelations};
|
||||
use selectors::matching::matches_selector;
|
||||
|
@ -22,6 +22,7 @@ use selectors::parser::{SelectorInner, SelectorMethods};
|
|||
use selectors::visitor::SelectorVisitor;
|
||||
use smallvec::SmallVec;
|
||||
use std::borrow::Borrow;
|
||||
use std::cell::Cell;
|
||||
use std::clone::Clone;
|
||||
use stylist::SelectorMap;
|
||||
|
||||
|
@ -126,6 +127,7 @@ impl RestyleHint {
|
|||
impl From<nsRestyleHint> for RestyleHint {
|
||||
fn from(raw: nsRestyleHint) -> Self {
|
||||
let raw_bits: u32 = raw.0;
|
||||
|
||||
// FIXME(bholley): Finish aligning the binary representations here and
|
||||
// then .expect() the result of the checked version.
|
||||
if Self::from_bits(raw_bits).is_none() {
|
||||
|
@ -193,20 +195,38 @@ struct ElementWrapper<'a, E>
|
|||
where E: TElement,
|
||||
{
|
||||
element: E,
|
||||
snapshot: Option<&'a Snapshot>,
|
||||
cached_snapshot: Cell<Option<&'a Snapshot>>,
|
||||
snapshot_map: &'a SnapshotMap,
|
||||
}
|
||||
|
||||
impl<'a, E> ElementWrapper<'a, E>
|
||||
where E: TElement,
|
||||
{
|
||||
/// Trivially constructs an `ElementWrapper` without a snapshot.
|
||||
pub fn new(el: E) -> ElementWrapper<'a, E> {
|
||||
ElementWrapper { element: el, snapshot: None }
|
||||
/// Trivially constructs an `ElementWrapper`.
|
||||
fn new(el: E, snapshot_map: &'a SnapshotMap) -> Self {
|
||||
ElementWrapper {
|
||||
element: el,
|
||||
cached_snapshot: Cell::new(None),
|
||||
snapshot_map: snapshot_map,
|
||||
}
|
||||
}
|
||||
|
||||
/// Trivially constructs an `ElementWrapper` with a snapshot.
|
||||
pub fn new_with_snapshot(el: E, snapshot: &'a Snapshot) -> ElementWrapper<'a, E> {
|
||||
ElementWrapper { element: el, snapshot: Some(snapshot) }
|
||||
/// Gets the snapshot associated with this element, if any.
|
||||
fn snapshot(&self) -> Option<&'a 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;
|
||||
|
||||
fn match_attr_has(&self, attr: &AttrSelector<SelectorImpl>) -> bool {
|
||||
match self.snapshot {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.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,
|
||||
attr: &AttrSelector<SelectorImpl>,
|
||||
value: &AttrValue) -> bool {
|
||||
match self.snapshot {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.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,
|
||||
attr: &AttrSelector<SelectorImpl>,
|
||||
value: &AttrValue) -> bool {
|
||||
match self.snapshot {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.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,
|
||||
attr: &AttrSelector<SelectorImpl>,
|
||||
value: &AttrValue) -> bool {
|
||||
match self.snapshot {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.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,
|
||||
attr: &AttrSelector<SelectorImpl>,
|
||||
value: &AttrValue) -> bool {
|
||||
match self.snapshot {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.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,
|
||||
attr: &AttrSelector<SelectorImpl>,
|
||||
value: &AttrValue) -> bool {
|
||||
match self.snapshot {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.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,
|
||||
attr: &AttrSelector<SelectorImpl>,
|
||||
value: &AttrValue) -> bool {
|
||||
match self.snapshot {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.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,
|
||||
attr: &AttrSelector<SelectorImpl>,
|
||||
value: &AttrValue) -> bool {
|
||||
match self.snapshot {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.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,
|
||||
&mut |_, _| {})
|
||||
}
|
||||
match self.snapshot.and_then(|s| s.state()) {
|
||||
match self.snapshot().and_then(|s| s.state()) {
|
||||
Some(snapshot_state) => snapshot_state.contains(flag),
|
||||
None => {
|
||||
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> {
|
||||
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> {
|
||||
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> {
|
||||
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> {
|
||||
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> {
|
||||
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 {
|
||||
|
@ -365,7 +390,7 @@ impl<'a, E> Element for ElementWrapper<'a, E>
|
|||
}
|
||||
|
||||
fn get_id(&self) -> Option<Atom> {
|
||||
match self.snapshot {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.id_attr(),
|
||||
_ => self.element.get_id()
|
||||
|
@ -373,7 +398,7 @@ impl<'a, E> Element for ElementWrapper<'a, E>
|
|||
}
|
||||
|
||||
fn has_class(&self, name: &Atom) -> bool {
|
||||
match self.snapshot {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.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)
|
||||
where F: FnMut(&Atom) {
|
||||
match self.snapshot {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.each_class(callback),
|
||||
_ => self.element.each_class(callback)
|
||||
|
@ -606,13 +631,21 @@ impl DependencySet {
|
|||
/// explained in the rest of the documentation.
|
||||
pub fn compute_hint<E>(&self,
|
||||
el: &E,
|
||||
snapshot: &Snapshot)
|
||||
snapshots: &SnapshotMap)
|
||||
-> RestyleHint
|
||||
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 state_changes = snapshot.state()
|
||||
.map_or_else(ElementState::empty, |old_state| current_state ^ old_state);
|
||||
let state_changes =
|
||||
snapshot.state()
|
||||
.map_or_else(ElementState::empty,
|
||||
|old_state| current_state ^ old_state);
|
||||
let attrs_changed = snapshot.has_attrs();
|
||||
|
||||
if state_changes.is_empty() && !attrs_changed {
|
||||
|
@ -620,7 +653,6 @@ impl DependencySet {
|
|||
}
|
||||
|
||||
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
|
||||
// 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| {
|
||||
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;
|
||||
}
|
||||
|
||||
// We can ignore the selector flags, since they would have already been set during
|
||||
// original matching for any element that might change its matching behavior here.
|
||||
// We can ignore the selector flags, since they would have already
|
||||
// been set during original matching for any element that might
|
||||
// change its matching behavior here.
|
||||
let matched_then =
|
||||
matches_selector(&dep.selector, &snapshot_el, None,
|
||||
&mut StyleRelations::empty(),
|
||||
|
@ -658,8 +692,8 @@ impl DependencySet {
|
|||
!hint.is_all()
|
||||
});
|
||||
|
||||
debug!("Calculated restyle hint: {:?}. (Element={:?}, State={:?}, Snapshot={:?}, {} Deps)",
|
||||
hint, el, current_state, snapshot, self.len());
|
||||
debug!("Calculated restyle hint: {:?} for {:?}. (State={:?}, {} Deps)",
|
||||
hint, el, current_state, self.len());
|
||||
trace!("Deps: {:?}", self);
|
||||
|
||||
hint
|
||||
|
|
|
@ -9,7 +9,9 @@
|
|||
use {Atom, Prefix, Namespace, LocalName};
|
||||
use attr::{AttrIdentifier, AttrValue};
|
||||
use cssparser::{Parser as CssParser, ToCss, serialize_identifier};
|
||||
use dom::{OpaqueNode, TElement, TNode};
|
||||
use element_state::ElementState;
|
||||
use fnv::FnvHashMap;
|
||||
use restyle_hints::ElementSnapshot;
|
||||
use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser};
|
||||
use selectors::{Element, MatchAttrGeneric};
|
||||
|
@ -20,6 +22,7 @@ use std::borrow::Cow;
|
|||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
/// 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.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
|
|
|
@ -22,7 +22,7 @@ use properties::INHERIT_ALL;
|
|||
use properties::PropertyDeclarationBlock;
|
||||
use restyle_hints::{RestyleHint, DependencySet};
|
||||
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
|
||||
use selector_parser::{SelectorImpl, PseudoElement, Snapshot};
|
||||
use selector_parser::{SelectorImpl, PseudoElement, SnapshotMap};
|
||||
use selectors::Element;
|
||||
use selectors::bloom::BloomFilter;
|
||||
use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS};
|
||||
|
@ -894,16 +894,16 @@ impl Stylist {
|
|||
results
|
||||
}
|
||||
|
||||
/// Given an element, and a snapshot that represents a previous state of the
|
||||
/// element, compute the appropriate restyle hint, that is, the kind of
|
||||
/// Given an element, and a snapshot table that represents a previous state
|
||||
/// of the tree, compute the appropriate restyle hint, that is, the kind of
|
||||
/// restyle we need to do.
|
||||
pub fn compute_restyle_hint<E>(&self,
|
||||
element: &E,
|
||||
snapshot: &Snapshot)
|
||||
snapshots: &SnapshotMap)
|
||||
-> RestyleHint
|
||||
where E: TElement,
|
||||
{
|
||||
self.dependencies.compute_hint(element, snapshot)
|
||||
self.dependencies.compute_hint(element, snapshots)
|
||||
}
|
||||
|
||||
/// Computes styles for a given declaration with parent_style.
|
||||
|
|
|
@ -13,7 +13,6 @@ use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_SELF};
|
|||
use selector_parser::RestyleDamage;
|
||||
#[cfg(feature = "servo")] use servo_config::opts;
|
||||
use std::borrow::BorrowMut;
|
||||
use stylist::Stylist;
|
||||
|
||||
/// 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.
|
||||
|
@ -200,7 +199,9 @@ pub trait DomTraversal<E: TElement> : Sync {
|
|||
/// appended children without restyling the parent.
|
||||
/// If traversal_flag::ANIMATION_ONLY is specified, style only elements for
|
||||
/// animations.
|
||||
fn pre_traverse(root: E, stylist: &Stylist, traversal_flags: TraversalFlags)
|
||||
fn pre_traverse(root: E,
|
||||
shared_context: &SharedStyleContext,
|
||||
traversal_flags: TraversalFlags)
|
||||
-> PreTraverseToken
|
||||
{
|
||||
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
|
||||
// we propagate to the next sibling element.
|
||||
if let Some(mut data) = root.mutate_data() {
|
||||
if let Some(r) = data.get_restyle_mut() {
|
||||
let later_siblings = r.compute_final_hint(root, stylist);
|
||||
if later_siblings {
|
||||
if let Some(next) = root.next_sibling_element() {
|
||||
if let Some(mut next_data) = next.mutate_data() {
|
||||
let hint = StoredRestyleHint::subtree_and_later_siblings();
|
||||
next_data.ensure_restyle().hint.insert(&hint);
|
||||
}
|
||||
let later_siblings = data.compute_final_hint(root, shared_context);
|
||||
if later_siblings {
|
||||
if let Some(next) = root.next_sibling_element() {
|
||||
if let Some(mut next_data) = next.mutate_data() {
|
||||
let hint = StoredRestyleHint::subtree_and_later_siblings();
|
||||
next_data.ensure_restyle().hint.insert(&hint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -375,6 +374,7 @@ pub trait DomTraversal<E: TElement> : Sync {
|
|||
return true;
|
||||
}
|
||||
|
||||
trace!("{:?} doesn't need traversal", el);
|
||||
false
|
||||
}
|
||||
|
||||
|
@ -390,7 +390,7 @@ pub trait DomTraversal<E: TElement> : Sync {
|
|||
log: LogBehavior) -> bool
|
||||
{
|
||||
// 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 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.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| {
|
||||
r.snapshot.is_none() && !r.has_sibling_invalidations()
|
||||
!r.has_sibling_invalidations()
|
||||
}), "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;
|
||||
|
||||
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
|
||||
// data in the subtree, notify the caller to early-return.
|
||||
let display_none = data.styles().is_display_none();
|
||||
if display_none {
|
||||
debug!("New element style is display:none - clearing data from descendants.");
|
||||
if data.styles().is_display_none() {
|
||||
debug!("{:?} style is display:none - clearing data from descendants.",
|
||||
element);
|
||||
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)
|
||||
},
|
||||
};
|
||||
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={:?}, \
|
||||
is_display_none={:?}, implementing_pseudo={:?}",
|
||||
propagated_hint, inherited_style_changed,
|
||||
data.styles().is_display_none(),
|
||||
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 =
|
||||
if context.shared.traversal_flags.for_animation_only() {
|
||||
|
@ -779,6 +781,18 @@ fn preprocess_children<E, D>(traversal: &D,
|
|||
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
|
||||
// any reason to create one, avoid the useless allocation and move on to
|
||||
// the next child.
|
||||
|
@ -786,6 +800,7 @@ fn preprocess_children<E, D>(traversal: &D,
|
|||
damage_handled.is_empty() && !child_data.has_restyle() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut restyle_data = child_data.ensure_restyle();
|
||||
|
||||
// Propagate the parent and sibling restyle hint.
|
||||
|
@ -793,11 +808,6 @@ fn preprocess_children<E, D>(traversal: &D,
|
|||
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 {
|
||||
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.
|
||||
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 {
|
||||
restyle_data.recascade = true;
|
||||
}
|
||||
|
|
|
@ -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::Loader;
|
||||
use style::gecko_bindings::structs::RawGeckoPresContextOwned;
|
||||
use style::gecko_bindings::structs::ServoElementSnapshotTable;
|
||||
use style::gecko_bindings::structs::URLExtraData;
|
||||
use style::gecko_bindings::structs::nsCSSValueSharedList;
|
||||
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,
|
||||
guard: &'a SharedRwLockReadGuard,
|
||||
per_doc_data: &PerDocumentStyleDataImpl,
|
||||
traversal_flags: TraversalFlags) -> SharedStyleContext<'a> {
|
||||
traversal_flags: TraversalFlags,
|
||||
snapshot_map: &'a ServoElementSnapshotTable)
|
||||
-> SharedStyleContext<'a> {
|
||||
let local_context_data =
|
||||
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
|
||||
quirks_mode: QuirksMode::NoQuirks,
|
||||
traversal_flags: traversal_flags,
|
||||
snapshot_map: snapshot_map,
|
||||
}
|
||||
}
|
||||
|
||||
fn traverse_subtree(element: GeckoElement,
|
||||
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
|
||||
// servo to try to style it. Detect that here and bail out.
|
||||
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();
|
||||
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() {
|
||||
return;
|
||||
}
|
||||
|
@ -192,13 +208,6 @@ fn traverse_subtree(element: GeckoElement,
|
|||
debug!("Traversing subtree:");
|
||||
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() {
|
||||
TraversalDriver::Sequential
|
||||
} else {
|
||||
|
@ -214,17 +223,20 @@ fn traverse_subtree(element: GeckoElement,
|
|||
}
|
||||
}
|
||||
|
||||
/// Traverses the subtree rooted at `root` for restyling. Returns whether a
|
||||
/// Gecko post-traversal (to perform lazy frame construction, or consume any
|
||||
/// RestyleData, or drop any ElementData) is required.
|
||||
/// Traverses the subtree rooted at `root` for restyling.
|
||||
///
|
||||
/// Returns whether a Gecko post-traversal (to perform lazy frame construction,
|
||||
/// or consume any RestyleData, or drop any ElementData) is required.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed,
|
||||
raw_data: RawServoStyleSetBorrowed,
|
||||
snapshots: *const ServoElementSnapshotTable,
|
||||
root_behavior: structs::TraversalRootBehavior,
|
||||
restyle_behavior: structs::TraversalRestyleBehavior)
|
||||
-> bool {
|
||||
use self::structs::TraversalRestyleBehavior as Restyle;
|
||||
use self::structs::TraversalRootBehavior as Root;
|
||||
debug_assert!(!snapshots.is_null());
|
||||
|
||||
let element = GeckoElement(root);
|
||||
debug!("Servo_TraverseSubtree: {:?}", element);
|
||||
|
@ -238,12 +250,18 @@ pub extern "C" fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed,
|
|||
|
||||
if element.has_animation_only_dirty_descendants() ||
|
||||
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]
|
||||
|
@ -436,18 +454,21 @@ pub extern "C" fn Servo_AnimationValue_Uncompute(value: RawServoAnimationValueBo
|
|||
#[no_mangle]
|
||||
pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(raw_data: RawServoStyleSetBorrowed,
|
||||
element: RawGeckoElementBorrowed,
|
||||
snapshots: *const ServoElementSnapshotTable,
|
||||
pseudo_tag: *mut nsIAtom)
|
||||
-> ServoComputedValuesStrong
|
||||
{
|
||||
use style::matching::MatchMethods;
|
||||
debug_assert!(!snapshots.is_null());
|
||||
|
||||
let doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow();
|
||||
let global_style_data = &*GLOBAL_STYLE_DATA;
|
||||
let guard = global_style_data.shared_lock.read();
|
||||
let shared_context = &create_shared_context(&global_style_data,
|
||||
&guard,
|
||||
&doc_data,
|
||||
TraversalFlags::empty());
|
||||
let shared_context = create_shared_context(&global_style_data,
|
||||
&guard,
|
||||
&doc_data,
|
||||
TraversalFlags::empty(),
|
||||
unsafe { &*snapshots });
|
||||
let element = GeckoElement(element);
|
||||
let element_data = element.borrow_data().unwrap();
|
||||
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();
|
||||
element.get_base_style(shared_context, &provider, &styles.primary, pseudo_style)
|
||||
element.get_base_style(&shared_context,
|
||||
&provider,
|
||||
&styles.primary,
|
||||
pseudo_style)
|
||||
.into_strong()
|
||||
}
|
||||
|
||||
|
@ -1867,10 +1891,12 @@ unsafe fn maybe_restyle<'a>(data: &'a mut AtomicRefMut<ElementData>,
|
|||
}
|
||||
|
||||
// Propagate the bit up the chain.
|
||||
if animation_only {
|
||||
element.parent_element().map(|p| p.note_descendants::<AnimationOnlyDirtyDescendants>());
|
||||
} else {
|
||||
element.parent_element().map(|p| p.note_descendants::<DirtyDescendants>());
|
||||
if let Some(p) = element.parent_element() {
|
||||
if animation_only {
|
||||
p.note_descendants::<AnimationOnlyDirtyDescendants>();
|
||||
} else {
|
||||
p.note_descendants::<DirtyDescendants>();
|
||||
}
|
||||
};
|
||||
|
||||
bindings::Gecko_SetOwnerDocumentNeedsStyleFlush(element.0);
|
||||
|
@ -1879,25 +1905,6 @@ unsafe fn maybe_restyle<'a>(data: &'a mut AtomicRefMut<ElementData>,
|
|||
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]
|
||||
pub extern "C" fn Servo_Element_GetStyleRuleList(element: RawGeckoElementBorrowed,
|
||||
rules: RawGeckoServoStyleRuleListBorrowedMut) {
|
||||
|
@ -1982,12 +1989,12 @@ pub extern "C" fn Servo_ResolveStyle(element: RawGeckoElementBorrowed,
|
|||
{
|
||||
let element = GeckoElement(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 {
|
||||
data.has_styles()
|
||||
data.has_styles()
|
||||
} else {
|
||||
data.has_current_styles()
|
||||
element.has_current_styles(&*data)
|
||||
};
|
||||
|
||||
if !valid_styles {
|
||||
|
@ -2003,9 +2010,11 @@ pub extern "C" fn Servo_ResolveStyle(element: RawGeckoElementBorrowed,
|
|||
#[no_mangle]
|
||||
pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
|
||||
pseudo_tag: *mut nsIAtom,
|
||||
snapshots: *const ServoElementSnapshotTable,
|
||||
raw_data: RawServoStyleSetBorrowed)
|
||||
-> ServoComputedValuesStrong
|
||||
{
|
||||
debug_assert!(!snapshots.is_null());
|
||||
let global_style_data = &*GLOBAL_STYLE_DATA;
|
||||
let guard = global_style_data.shared_lock.read();
|
||||
let element = GeckoElement(element);
|
||||
|
@ -2031,7 +2040,8 @@ pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
|
|||
let shared = create_shared_context(&global_style_data,
|
||||
&guard,
|
||||
&mut doc_data.borrow_mut(),
|
||||
TraversalFlags::empty());
|
||||
TraversalFlags::empty(),
|
||||
unsafe { &*snapshots });
|
||||
let mut tlc = ThreadLocalStyleContext::new(&shared);
|
||||
let mut context = StyleContext {
|
||||
shared: &shared,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue