style: Add ability to clear and rebuild individual origins.

This commit is contained in:
Cameron McCormack 2017-08-12 17:15:44 +08:00
parent 72107eb838
commit f3a7adfcc7
2 changed files with 72 additions and 12 deletions

View file

@ -17,7 +17,7 @@ use properties::ComputedValues;
use servo_arc::Arc; use servo_arc::Arc;
use shared_lock::{Locked, StylesheetGuards, SharedRwLockReadGuard}; use shared_lock::{Locked, StylesheetGuards, SharedRwLockReadGuard};
use stylesheet_set::StylesheetSet; use stylesheet_set::StylesheetSet;
use stylesheets::{PerOrigin, StylesheetContents, StylesheetInDocument}; use stylesheets::{Origin, PerOrigin, StylesheetContents, StylesheetInDocument};
use stylist::{ExtraStyleData, Stylist}; use stylist::{ExtraStyleData, Stylist};
/// Little wrapper to a Gecko style sheet. /// Little wrapper to a Gecko style sheet.
@ -193,6 +193,11 @@ impl PerDocumentStyleDataImpl {
self.stylist.clear(); self.stylist.clear();
} }
/// Clear the stylist's data for the specified origin.
pub fn clear_stylist_origin(&mut self, origin: &Origin) {
self.stylist.clear_origin(origin);
}
/// Returns whether visited links are enabled. /// Returns whether visited links are enabled.
fn visited_links_enabled(&self) -> bool { fn visited_links_enabled(&self) -> bool {
unsafe { bindings::Gecko_AreVisitedLinksEnabled() } unsafe { bindings::Gecko_AreVisitedLinksEnabled() }

View file

@ -204,6 +204,23 @@ impl Stylist {
self.is_device_dirty = true; self.is_device_dirty = true;
} }
/// Clear the stylist's state for the specified origin.
pub fn clear_origin(&mut self, origin: &Origin) {
self.cascade_data.borrow_mut_for_origin(origin).clear();
if *origin == Origin::UserAgent {
// We only collect these declarations from UA sheets.
self.precomputed_pseudo_element_decls.clear();
}
// The stored `ViewportConstraints` contains data from rules across
// all origins.
self.viewport_constraints = None;
// XXX(heycam) Why do this, if we are preserving the Device?
self.is_device_dirty = true;
}
/// Returns whether any origin's `CascadeData` has been cleared. /// Returns whether any origin's `CascadeData` has been cleared.
fn any_origin_cleared(&self) -> bool { fn any_origin_cleared(&self) -> bool {
self.cascade_data self.cascade_data
@ -211,7 +228,7 @@ impl Stylist {
.any(|(d, _)| d.is_cleared) .any(|(d, _)| d.is_cleared)
} }
/// rebuild the stylist for the given document stylesheets, and optionally /// Rebuild the stylist for the given document stylesheets, and optionally
/// with a set of user agent stylesheets. /// with a set of user agent stylesheets.
/// ///
/// This method resets all the style data each time the stylesheets change /// This method resets all the style data each time the stylesheets change
@ -232,8 +249,26 @@ impl Stylist {
{ {
debug_assert!(!self.any_origin_cleared() || self.is_device_dirty); debug_assert!(!self.any_origin_cleared() || self.is_device_dirty);
for (data, _) in self.cascade_data.iter_mut_origins() { // Determine the origins that actually need updating.
//
// XXX(heycam): What is the relationship between `stylesheets_changed`
// and the `is_cleared` fields on each origin's `CascadeData`? Can
// we avoid passing in `stylesheets_changed`?
let mut to_update: PerOrigin<bool> = Default::default();
// If we're provided with a list of UA and user style sheets, then
// we must update those cascade levels. (Servo does this, but Gecko
// just includes the UA and User sheets in `doc_stylesheets`.)
if ua_stylesheets.is_some() {
to_update.user_agent = true;
to_update.user = true;
}
for (data, origin) in self.cascade_data.iter_mut_origins() {
if data.is_cleared {
data.is_cleared = false; data.is_cleared = false;
*to_update.borrow_mut_for_origin(&origin) = true;
}
} }
if !(self.is_device_dirty || stylesheets_changed) { if !(self.is_device_dirty || stylesheets_changed) {
@ -242,8 +277,9 @@ impl Stylist {
self.num_rebuilds += 1; self.num_rebuilds += 1;
// Update viewport_constraints regardless of which origins'
// `CascadeData` we're updating.
self.viewport_constraints = None; self.viewport_constraints = None;
if viewport_rule::enabled() { if viewport_rule::enabled() {
// TODO(emilio): This doesn't look so efficient. // TODO(emilio): This doesn't look so efficient.
// //
@ -264,29 +300,48 @@ impl Stylist {
self.viewport_constraints = self.viewport_constraints =
ViewportConstraints::maybe_new(&self.device, ViewportConstraints::maybe_new(&self.device,
&cascaded_rule, &cascaded_rule,
self.quirks_mode) self.quirks_mode);
}
if let Some(ref constraints) = self.viewport_constraints { if let Some(ref constraints) = self.viewport_constraints {
self.device.account_for_viewport_rule(constraints); self.device.account_for_viewport_rule(constraints);
} }
}
extra_data.clear(); // XXX(heycam): We should probably just move the `extra_data` to be
// stored on the `Stylist` instead of Gecko's `PerDocumentStyleData`.
// That would let us clear it inside `clear()` and `clear_origin()`.
for (update, origin) in to_update.iter_origins() {
if *update {
extra_data.borrow_mut_for_origin(&origin).clear();
}
}
if let Some(ua_stylesheets) = ua_stylesheets { if let Some(ua_stylesheets) = ua_stylesheets {
for stylesheet in &ua_stylesheets.user_or_user_agent_stylesheets { for stylesheet in &ua_stylesheets.user_or_user_agent_stylesheets {
debug_assert!(matches!(
stylesheet.contents(guards.ua_or_user).origin,
Origin::UserAgent | Origin::User));
self.add_stylesheet(stylesheet, guards.ua_or_user, extra_data); self.add_stylesheet(stylesheet, guards.ua_or_user, extra_data);
} }
if self.quirks_mode != QuirksMode::NoQuirks { if self.quirks_mode != QuirksMode::NoQuirks {
let stylesheet = &ua_stylesheets.quirks_mode_stylesheet;
debug_assert!(matches!(
stylesheet.contents(guards.ua_or_user).origin,
Origin::UserAgent | Origin::User));
self.add_stylesheet(&ua_stylesheets.quirks_mode_stylesheet, self.add_stylesheet(&ua_stylesheets.quirks_mode_stylesheet,
guards.ua_or_user, extra_data); guards.ua_or_user, extra_data);
} }
} }
// Only use author stylesheets if author styles are enabled. // Only add stylesheets for origins we are updating, and only add
// Author level sheets if author style is not disabled.
let sheets_to_add = doc_stylesheets.filter(|s| { let sheets_to_add = doc_stylesheets.filter(|s| {
!author_style_disabled || s.origin(guards.author) != Origin::Author match s.contents(guards.author).origin {
Origin::UserAgent => to_update.user_agent,
Origin::Author => to_update.author && !author_style_disabled,
Origin::User => to_update.user,
}
}); });
for stylesheet in sheets_to_add { for stylesheet in sheets_to_add {