mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Auto merge of #12563 - emilio:stylo, r=bholley,jdm,pcwalton
stylo: Improve restyling performance This commit adds hooks to the Servo style traversal to avoid traversing all the DOM for every restyle. Additionally it changes the behavior of the dirty flag to be propagated top down, to prevent extra overhead when an element is dirtied. This commit doesn't aim to change the behavior on Servo just yet, since Servo does extra job when dirtying the node related with DOM revision counters that might be necessary. CC @asajeffrey for the DOM revision counters stuff. When a node is dirty, do all its descendants really need to increment the revision counter, or is this an unintended effect? My intuition is that this is hurting performance quite a lot for servo. r? @bholley <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors <!-- Either: --> - [x] These changes do not require tests because no geckolib tests yet. <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- 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/12563) <!-- Reviewable:end -->
This commit is contained in:
commit
944d371b8f
18 changed files with 195 additions and 177 deletions
|
@ -376,6 +376,7 @@ impl Document {
|
|||
// that workable.
|
||||
match self.GetDocumentElement() {
|
||||
Some(root) => {
|
||||
root.upcast::<Node>().is_dirty() ||
|
||||
root.upcast::<Node>().has_dirty_descendants() ||
|
||||
!self.modified_elements.borrow().is_empty()
|
||||
}
|
||||
|
@ -1371,6 +1372,7 @@ impl Document {
|
|||
}
|
||||
|
||||
pub fn finish_load(&self, load: LoadType) {
|
||||
debug!("Document got finish_load: {:?}", load);
|
||||
// The parser might need the loader, so restrict the lifetime of the borrow.
|
||||
{
|
||||
let mut loader = self.loader.borrow_mut();
|
||||
|
@ -1396,9 +1398,9 @@ impl Document {
|
|||
// If we don't have a parser, and the reflow timer has been reset, explicitly
|
||||
// trigger a reflow.
|
||||
if let LoadType::Stylesheet(_) = load {
|
||||
self.window().reflow(ReflowGoal::ForDisplay,
|
||||
ReflowQueryType::NoQuery,
|
||||
ReflowReason::StylesheetLoaded);
|
||||
self.window.reflow(ReflowGoal::ForDisplay,
|
||||
ReflowQueryType::NoQuery,
|
||||
ReflowReason::StylesheetLoaded);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1487,24 +1489,25 @@ impl Document {
|
|||
return;
|
||||
}
|
||||
self.domcontentloaded_dispatched.set(true);
|
||||
assert!(self.ReadyState() != DocumentReadyState::Complete,
|
||||
"Complete before DOMContentLoaded?");
|
||||
|
||||
update_with_current_time_ms(&self.dom_content_loaded_event_start);
|
||||
|
||||
let window = self.window();
|
||||
window.dom_manipulation_task_source().queue_event(self.upcast(), atom!("DOMContentLoaded"),
|
||||
EventBubbles::Bubbles, EventCancelable::NotCancelable, window);
|
||||
|
||||
window.reflow(ReflowGoal::ForDisplay,
|
||||
ReflowQueryType::NoQuery,
|
||||
ReflowReason::DOMContentLoaded);
|
||||
|
||||
update_with_current_time_ms(&self.dom_content_loaded_event_end);
|
||||
}
|
||||
|
||||
pub fn notify_constellation_load(&self) {
|
||||
let pipeline_id = self.window.pipeline();
|
||||
let event = ConstellationMsg::DOMLoad(pipeline_id);
|
||||
self.window.constellation_chan().send(event).unwrap();
|
||||
|
||||
let load_event = ConstellationMsg::LoadComplete(pipeline_id);
|
||||
self.window.constellation_chan().send(load_event).unwrap();
|
||||
}
|
||||
|
||||
pub fn set_current_parser(&self, script: Option<ParserRef>) {
|
||||
|
@ -2908,16 +2911,18 @@ impl DocumentProgressHandler {
|
|||
// http://w3c.github.io/navigation-timing/#widl-PerformanceNavigationTiming-loadEventStart
|
||||
update_with_current_time_ms(&document.load_event_start);
|
||||
|
||||
debug!("About to dispatch load for {:?}", document.url());
|
||||
let _ = wintarget.dispatch_event_with_target(document.upcast(), &event);
|
||||
|
||||
// http://w3c.github.io/navigation-timing/#widl-PerformanceNavigationTiming-loadEventEnd
|
||||
update_with_current_time_ms(&document.load_event_end);
|
||||
|
||||
document.notify_constellation_load();
|
||||
|
||||
window.reflow(ReflowGoal::ForDisplay,
|
||||
ReflowQueryType::NoQuery,
|
||||
ReflowReason::DocumentLoaded);
|
||||
|
||||
document.notify_constellation_load();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -479,19 +479,7 @@ impl Node {
|
|||
return
|
||||
}
|
||||
|
||||
// 2. Dirty descendants.
|
||||
fn dirty_subtree(node: &Node) {
|
||||
// Stop if this subtree is already dirty.
|
||||
if node.is_dirty() { return }
|
||||
|
||||
node.set_flag(IS_DIRTY | HAS_DIRTY_DESCENDANTS, true);
|
||||
|
||||
for kid in node.children() {
|
||||
dirty_subtree(kid.r());
|
||||
}
|
||||
}
|
||||
|
||||
dirty_subtree(self);
|
||||
self.set_flag(IS_DIRTY, true);
|
||||
|
||||
// 4. Dirty ancestors.
|
||||
for ancestor in self.ancestors() {
|
||||
|
@ -1299,6 +1287,31 @@ impl TreeIterator {
|
|||
depth: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_skipping_children(&mut self) -> Option<Root<Node>> {
|
||||
let current = match self.current.take() {
|
||||
None => return None,
|
||||
Some(current) => current,
|
||||
};
|
||||
|
||||
self.next_skipping_children_impl(current)
|
||||
}
|
||||
|
||||
fn next_skipping_children_impl(&mut self, current: Root<Node>) -> Option<Root<Node>> {
|
||||
for ancestor in current.inclusive_ancestors() {
|
||||
if self.depth == 0 {
|
||||
break;
|
||||
}
|
||||
if let Some(next_sibling) = ancestor.GetNextSibling() {
|
||||
self.current = Some(next_sibling);
|
||||
return Some(current);
|
||||
}
|
||||
self.depth -= 1;
|
||||
}
|
||||
debug_assert!(self.depth == 0);
|
||||
self.current = None;
|
||||
Some(current)
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for TreeIterator {
|
||||
|
@ -1315,19 +1328,8 @@ impl Iterator for TreeIterator {
|
|||
self.depth += 1;
|
||||
return Some(current);
|
||||
};
|
||||
for ancestor in current.inclusive_ancestors() {
|
||||
if self.depth == 0 {
|
||||
break;
|
||||
}
|
||||
if let Some(next_sibling) = ancestor.GetNextSibling() {
|
||||
self.current = Some(next_sibling);
|
||||
return Some(current);
|
||||
}
|
||||
self.depth -= 1;
|
||||
}
|
||||
debug_assert!(self.depth == 0);
|
||||
self.current = None;
|
||||
Some(current)
|
||||
|
||||
self.next_skipping_children_impl(current)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -450,13 +450,15 @@ impl WindowMethods for Window {
|
|||
// Right now, just print to the console
|
||||
// Ensure that stderr doesn't trample through the alert() we use to
|
||||
// communicate test results (see executorservo.py in wptrunner).
|
||||
let stderr = stderr();
|
||||
let mut stderr = stderr.lock();
|
||||
let stdout = stdout();
|
||||
let mut stdout = stdout.lock();
|
||||
writeln!(&mut stdout, "ALERT: {}", s).unwrap();
|
||||
stdout.flush().unwrap();
|
||||
stderr.flush().unwrap();
|
||||
{
|
||||
let stderr = stderr();
|
||||
let mut stderr = stderr.lock();
|
||||
let stdout = stdout();
|
||||
let mut stdout = stdout.lock();
|
||||
writeln!(&mut stdout, "ALERT: {}", s).unwrap();
|
||||
stdout.flush().unwrap();
|
||||
stderr.flush().unwrap();
|
||||
}
|
||||
|
||||
let (sender, receiver) = ipc::channel().unwrap();
|
||||
self.constellation_chan().send(ConstellationMsg::Alert(self.pipeline(), s.to_string(), sender)).unwrap();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue