Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Take all the snapshots into account in the style system #16778

Merged
merged 4 commits into from May 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

87 changes: 57 additions & 30 deletions components/layout_thread/lib.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
Expand All @@ -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()),
Expand Down Expand Up @@ -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();

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 => 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);
// 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;
}
debug!("Noting restyle for {:?}: {:?}", el, restyle_data);
};

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() {
Expand All @@ -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() {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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.
Expand Down
31 changes: 22 additions & 9 deletions components/script/dom/node.rs
Expand Up @@ -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,
}
}

Expand Down Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions components/script/layout_wrapper.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion components/style/Cargo.toml
Expand Up @@ -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"
3 changes: 3 additions & 0 deletions components/style/build_gecko.rs
Expand Up @@ -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"))
Expand All @@ -329,6 +330,7 @@ mod bindings {
"NS_AUTHOR_SPECIFIED_.*",
"NS_THEME_.*",
"NODE_.*",
"ELEMENT_.*",
"NS_FONT_.*",
"NS_STYLE_.*",
"NS_MATHML_.*",
Expand Down Expand Up @@ -670,6 +672,7 @@ mod bindings {
"Keyframe",
"ServoBundledURI",
"ServoElementSnapshot",
"ServoElementSnapshotTable",
"SheetParsingMode",
"StyleBasicShape",
"StyleBasicShapeType",
Expand Down
5 changes: 4 additions & 1 deletion components/style/context.rs
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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> {
Expand Down