style: Add ability to clear and rebuild individual origins.
draft
style: Add ability to clear and rebuild individual origins.
MozReview-Commit-ID: 2msIfteoA9Q
--- a/servo/components/style/gecko/data.rs
+++ b/servo/components/style/gecko/data.rs
@@ -12,17 +12,17 @@ use gecko_bindings::structs::RawGeckoPre
use gecko_bindings::structs::nsIDocument;
use gecko_bindings::sugar::ownership::{HasArcFFI, HasBoxFFI, HasFFI, HasSimpleFFI};
use invalidation::media_queries::{MediaListKey, ToMediaListKey};
use media_queries::{Device, MediaList};
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.
#[derive(PartialEq, Eq, Debug)]
pub struct GeckoStyleSheet(*const ServoStyleSheet);
impl ToMediaListKey for ::gecko::data::GeckoStyleSheet {
fn to_media_list_key(&self) -> MediaListKey {
@@ -188,16 +188,21 @@ impl PerDocumentStyleDataImpl {
}
/// Clear the stylist. This will be a no-op if the stylist is
/// already cleared; the stylist handles that.
pub fn clear_stylist(&mut self) {
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() }
}
/// Returns whether visited styles are enabled.
pub fn visited_styles_enabled(&self) -> bool {
self.visited_links_enabled() && !self.is_private_browsing_enabled()
}
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -199,24 +199,41 @@ impl Stylist {
self.cascade_data.clear();
self.precomputed_pseudo_element_decls.clear();
self.viewport_constraints = None;
// XXX(heycam) Why do this, if we are preserving the Device?
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
.iter_origins()
.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
/// (which is indicated by the `stylesheets_changed` parameter), or the
/// device is dirty, which means we need to re-evaluate media queries.
pub fn rebuild<'a, I, S>(
&mut self,
doc_stylesheets: I,
@@ -227,28 +244,47 @@ impl Stylist {
extra_data: &mut PerOrigin<ExtraStyleData>
) -> bool
where
I: Iterator<Item = &'a S> + Clone,
S: StylesheetInDocument + ToMediaListKey + 'static,
{
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) {
return false;
}
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.
//
// Presumably when we properly implement this we can at least have a
// bit on the stylesheet that says whether it contains viewport
// rules to skip it entirely?
//
// Processing it with the rest of rules seems tricky since it
@@ -259,39 +295,58 @@ impl Stylist {
declarations: viewport_rule::Cascade::from_stylesheets(
doc_stylesheets.clone(), guards.author, &self.device
).finish()
};
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 {
self.add_stylesheet(stylesheet, guards.author, extra_data);
}
self.is_device_dirty = false;
true