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 shared_lock::{Locked, StylesheetGuards, SharedRwLockReadGuard};
use stylesheet_set::StylesheetSet;
use stylesheets::{PerOrigin, StylesheetContents, StylesheetInDocument};
use stylesheets::{Origin, PerOrigin, StylesheetContents, StylesheetInDocument};
use stylist::{ExtraStyleData, Stylist};
/// Little wrapper to a Gecko style sheet.
@ -193,6 +193,11 @@ impl PerDocumentStyleDataImpl {
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.
fn visited_links_enabled(&self) -> bool {
unsafe { bindings::Gecko_AreVisitedLinksEnabled() }

View file

@ -204,6 +204,23 @@ impl Stylist {
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.
fn any_origin_cleared(&self) -> bool {
self.cascade_data
@ -211,7 +228,7 @@ impl Stylist {
.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.
///
/// 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);
for (data, _) in self.cascade_data.iter_mut_origins() {
data.is_cleared = false;
// 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;
*to_update.borrow_mut_for_origin(&origin) = true;
}
}
if !(self.is_device_dirty || stylesheets_changed) {
@ -242,8 +277,9 @@ impl Stylist {
self.num_rebuilds += 1;
// Update viewport_constraints regardless of which origins'
// `CascadeData` we're updating.
self.viewport_constraints = None;
if viewport_rule::enabled() {
// TODO(emilio): This doesn't look so efficient.
//
@ -264,29 +300,48 @@ impl Stylist {
self.viewport_constraints =
ViewportConstraints::maybe_new(&self.device,
&cascaded_rule,
self.quirks_mode)
self.quirks_mode);
if let Some(ref constraints) = self.viewport_constraints {
self.device.account_for_viewport_rule(constraints);
}
}
if let Some(ref constraints) = self.viewport_constraints {
self.device.account_for_viewport_rule(constraints);
// 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();
}
}
extra_data.clear();
if let Some(ua_stylesheets) = ua_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);
}
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,
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| {
!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 {