mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
style: Replicate the list of stylesheets on the layout thread.
This is a patch that unifies a bit how Gecko and Stylo stylesheets work, in order to be able to eventually move the stylesheets into the stylist, and be able to incrementally update the invalidation map.
This commit is contained in:
parent
b8159e659e
commit
d1725b1f19
14 changed files with 413 additions and 198 deletions
|
@ -11,16 +11,17 @@ use Atom;
|
|||
use dom::{TElement, TNode};
|
||||
use fnv::FnvHashSet;
|
||||
use invalidation::element::restyle_hints::RestyleHint;
|
||||
use media_queries::Device;
|
||||
use selector_parser::SelectorImpl;
|
||||
use selectors::attr::CaseSensitivity;
|
||||
use selectors::parser::{Component, Selector};
|
||||
use shared_lock::SharedRwLockReadGuard;
|
||||
use stylesheets::{CssRule, StylesheetInDocument};
|
||||
use stylist::Stylist;
|
||||
|
||||
/// An invalidation scope represents a kind of subtree that may need to be
|
||||
/// restyled.
|
||||
#[derive(Debug, Hash, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
enum InvalidationScope {
|
||||
/// All the descendants of an element with a given id.
|
||||
ID(Atom),
|
||||
|
@ -54,6 +55,7 @@ impl InvalidationScope {
|
|||
///
|
||||
/// TODO(emilio): We might be able to do the same analysis for removals and
|
||||
/// media query changes too?
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub struct StylesheetInvalidationSet {
|
||||
/// The style scopes we know we have to restyle so far.
|
||||
invalid_scopes: FnvHashSet<InvalidationScope>,
|
||||
|
@ -82,7 +84,7 @@ impl StylesheetInvalidationSet {
|
|||
/// next time.
|
||||
pub fn collect_invalidations_for<S>(
|
||||
&mut self,
|
||||
stylist: &Stylist,
|
||||
device: &Device,
|
||||
stylesheet: &S,
|
||||
guard: &SharedRwLockReadGuard
|
||||
)
|
||||
|
@ -96,12 +98,12 @@ impl StylesheetInvalidationSet {
|
|||
}
|
||||
|
||||
if !stylesheet.enabled() ||
|
||||
!stylesheet.is_effective_for_device(stylist.device(), guard) {
|
||||
!stylesheet.is_effective_for_device(device, guard) {
|
||||
debug!(" > Stylesheet was not effective");
|
||||
return; // Nothing to do here.
|
||||
}
|
||||
|
||||
for rule in stylesheet.effective_rules(stylist.device(), guard) {
|
||||
for rule in stylesheet.effective_rules(device, guard) {
|
||||
self.collect_invalidations_for_rule(rule, guard);
|
||||
if self.fully_invalid {
|
||||
self.invalid_scopes.clear();
|
||||
|
@ -121,6 +123,11 @@ impl StylesheetInvalidationSet {
|
|||
if let Some(e) = document_element {
|
||||
self.process_invalidations(e);
|
||||
}
|
||||
self.clear();
|
||||
}
|
||||
|
||||
/// Clears the invalidation set without processing.
|
||||
pub fn clear(&mut self) {
|
||||
self.invalid_scopes.clear();
|
||||
self.fully_invalid = false;
|
||||
}
|
||||
|
|
|
@ -6,13 +6,14 @@
|
|||
|
||||
use dom::TElement;
|
||||
use invalidation::stylesheets::StylesheetInvalidationSet;
|
||||
use media_queries::Device;
|
||||
use shared_lock::SharedRwLockReadGuard;
|
||||
use std::slice;
|
||||
use stylesheets::{OriginSet, PerOrigin, StylesheetInDocument};
|
||||
use stylist::Stylist;
|
||||
|
||||
/// Entry for a StylesheetSet. We don't bother creating a constructor, because
|
||||
/// there's no sensible defaults for the member variables.
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub struct StylesheetSetEntry<S>
|
||||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static,
|
||||
|
@ -38,6 +39,7 @@ where
|
|||
}
|
||||
|
||||
/// The set of stylesheets effective for a given document.
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub struct StylesheetSet<S>
|
||||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static,
|
||||
|
@ -69,6 +71,16 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the number of stylesheets in the set.
|
||||
pub fn len(&self) -> usize {
|
||||
self.entries.len()
|
||||
}
|
||||
|
||||
/// Returns the number of stylesheets in the set.
|
||||
pub fn get(&self, index: usize) -> Option<&S> {
|
||||
self.entries.get(index).map(|s| &s.sheet)
|
||||
}
|
||||
|
||||
/// Returns whether author styles have been disabled for the current
|
||||
/// stylesheet set.
|
||||
pub fn author_style_disabled(&self) -> bool {
|
||||
|
@ -81,46 +93,57 @@ where
|
|||
|
||||
fn collect_invalidations_for(
|
||||
&mut self,
|
||||
stylist: &Stylist,
|
||||
device: Option<&Device>,
|
||||
sheet: &S,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
) {
|
||||
let origin = sheet.contents(guard).origin;
|
||||
let data = self.invalidation_data.borrow_mut_for_origin(&origin);
|
||||
data.invalidations.collect_invalidations_for(stylist, sheet, guard);
|
||||
if let Some(device) = device {
|
||||
data.invalidations.collect_invalidations_for(device, sheet, guard);
|
||||
}
|
||||
data.dirty = true;
|
||||
}
|
||||
|
||||
/// Appends a new stylesheet to the current set.
|
||||
///
|
||||
/// FIXME(emilio): `device` shouldn't be optional, but a bunch of work needs
|
||||
/// to happen to make the invalidations work properly in servo.
|
||||
pub fn append_stylesheet(
|
||||
&mut self,
|
||||
stylist: &Stylist,
|
||||
device: Option<&Device>,
|
||||
sheet: S,
|
||||
guard: &SharedRwLockReadGuard
|
||||
) {
|
||||
debug!("StylesheetSet::append_stylesheet");
|
||||
self.remove_stylesheet_if_present(&sheet);
|
||||
self.collect_invalidations_for(stylist, &sheet, guard);
|
||||
self.collect_invalidations_for(device, &sheet, guard);
|
||||
self.entries.push(StylesheetSetEntry { sheet });
|
||||
}
|
||||
|
||||
/// Prepend a new stylesheet to the current set.
|
||||
///
|
||||
/// FIXME(emilio): `device` shouldn't be optional, but a bunch of work needs
|
||||
/// to happen to make the invalidations work properly in servo.
|
||||
pub fn prepend_stylesheet(
|
||||
&mut self,
|
||||
stylist: &Stylist,
|
||||
device: Option<&Device>,
|
||||
sheet: S,
|
||||
guard: &SharedRwLockReadGuard
|
||||
) {
|
||||
debug!("StylesheetSet::prepend_stylesheet");
|
||||
self.remove_stylesheet_if_present(&sheet);
|
||||
self.collect_invalidations_for(stylist, &sheet, guard);
|
||||
self.collect_invalidations_for(device, &sheet, guard);
|
||||
self.entries.insert(0, StylesheetSetEntry { sheet });
|
||||
}
|
||||
|
||||
/// Insert a given stylesheet before another stylesheet in the document.
|
||||
///
|
||||
/// FIXME(emilio): `device` shouldn't be optional, but a bunch of work needs
|
||||
/// to happen to make the invalidations work properly in servo.
|
||||
pub fn insert_stylesheet_before(
|
||||
&mut self,
|
||||
stylist: &Stylist,
|
||||
device: Option<&Device>,
|
||||
sheet: S,
|
||||
before_sheet: S,
|
||||
guard: &SharedRwLockReadGuard
|
||||
|
@ -130,20 +153,23 @@ where
|
|||
let index = self.entries.iter().position(|entry| {
|
||||
entry.sheet == before_sheet
|
||||
}).expect("`before_sheet` stylesheet not found");
|
||||
self.collect_invalidations_for(stylist, &sheet, guard);
|
||||
self.collect_invalidations_for(device, &sheet, guard);
|
||||
self.entries.insert(index, StylesheetSetEntry { sheet });
|
||||
}
|
||||
|
||||
/// Remove a given stylesheet from the set.
|
||||
///
|
||||
/// FIXME(emilio): `device` shouldn't be optional, but a bunch of work needs
|
||||
/// to happen to make the invalidations work properly in servo.
|
||||
pub fn remove_stylesheet(
|
||||
&mut self,
|
||||
stylist: &Stylist,
|
||||
device: Option<&Device>,
|
||||
sheet: S,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
) {
|
||||
debug!("StylesheetSet::remove_stylesheet");
|
||||
self.remove_stylesheet_if_present(&sheet);
|
||||
self.collect_invalidations_for(stylist, &sheet, guard);
|
||||
self.collect_invalidations_for(device, &sheet, guard);
|
||||
}
|
||||
|
||||
/// Notes that the author style has been disabled for this document.
|
||||
|
@ -174,7 +200,6 @@ where
|
|||
E: TElement,
|
||||
{
|
||||
debug!("StylesheetSet::flush");
|
||||
debug_assert!(self.has_changed());
|
||||
|
||||
let mut origins = OriginSet::empty();
|
||||
for (data, origin) in self.invalidation_data.iter_mut_origins() {
|
||||
|
@ -188,6 +213,26 @@ where
|
|||
(self.iter(), origins)
|
||||
}
|
||||
|
||||
/// Flush stylesheets, but without running any of the invalidation passes.
|
||||
///
|
||||
/// FIXME(emilio): This should eventually disappear. Please keep this
|
||||
/// Servo-only.
|
||||
#[cfg(feature = "servo")]
|
||||
pub fn flush_without_invalidation(&mut self) -> (StylesheetIterator<S>, OriginSet) {
|
||||
debug!("StylesheetSet::flush_without_invalidation");
|
||||
|
||||
let mut origins = OriginSet::empty();
|
||||
for (data, origin) in self.invalidation_data.iter_mut_origins() {
|
||||
if data.dirty {
|
||||
data.invalidations.clear();
|
||||
data.dirty = false;
|
||||
origins |= origin;
|
||||
}
|
||||
}
|
||||
|
||||
(self.iter(), origins)
|
||||
}
|
||||
|
||||
/// Returns an iterator over the current list of stylesheets.
|
||||
pub fn iter(&self) -> StylesheetIterator<S> {
|
||||
StylesheetIterator(self.entries.iter())
|
||||
|
@ -204,6 +249,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
struct InvalidationData {
|
||||
/// The stylesheet invalidations for this origin that we still haven't
|
||||
/// processed.
|
||||
|
|
|
@ -196,16 +196,19 @@ impl Stylist {
|
|||
author_style_disabled: bool,
|
||||
extra_data: &mut PerOrigin<ExtraStyleData>,
|
||||
mut origins_to_rebuild: OriginSet,
|
||||
) -> bool
|
||||
) -> OriginSet
|
||||
where
|
||||
I: Iterator<Item = &'a S> + Clone,
|
||||
S: StylesheetInDocument + ToMediaListKey + 'static,
|
||||
{
|
||||
debug_assert!(!origins_to_rebuild.is_empty() || self.is_device_dirty);
|
||||
if self.is_device_dirty {
|
||||
origins_to_rebuild = OriginSet::all();
|
||||
}
|
||||
|
||||
if origins_to_rebuild.is_empty() {
|
||||
return origins_to_rebuild;
|
||||
}
|
||||
|
||||
self.num_rebuilds += 1;
|
||||
|
||||
// Update viewport_constraints regardless of which origins'
|
||||
|
@ -279,37 +282,7 @@ impl Stylist {
|
|||
}
|
||||
|
||||
self.is_device_dirty = false;
|
||||
true
|
||||
}
|
||||
|
||||
/// clear the stylist and then rebuild it. Chances are, you want to use
|
||||
/// either clear() or rebuild(), with the latter done lazily, instead.
|
||||
pub fn update<'a, I, S>(
|
||||
&mut self,
|
||||
doc_stylesheets: I,
|
||||
guards: &StylesheetGuards,
|
||||
ua_stylesheets: Option<&UserAgentStylesheets>,
|
||||
stylesheets_changed: bool,
|
||||
author_style_disabled: bool,
|
||||
extra_data: &mut PerOrigin<ExtraStyleData>
|
||||
) -> bool
|
||||
where
|
||||
I: Iterator<Item = &'a S> + Clone,
|
||||
S: StylesheetInDocument + ToMediaListKey + 'static,
|
||||
{
|
||||
// We have to do a dirtiness check before clearing, because if
|
||||
// we're not actually dirty we need to no-op here.
|
||||
if !(self.is_device_dirty || stylesheets_changed) {
|
||||
return false;
|
||||
}
|
||||
self.rebuild(
|
||||
doc_stylesheets,
|
||||
guards,
|
||||
ua_stylesheets,
|
||||
author_style_disabled,
|
||||
extra_data,
|
||||
OriginSet::all(),
|
||||
)
|
||||
origins_to_rebuild
|
||||
}
|
||||
|
||||
fn add_stylesheet<S>(
|
||||
|
@ -876,13 +849,19 @@ impl Stylist {
|
|||
/// FIXME(emilio): The semantics of the device for Servo and Gecko are
|
||||
/// different enough we may want to unify them.
|
||||
#[cfg(feature = "servo")]
|
||||
pub fn set_device(&mut self,
|
||||
mut device: Device,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
stylesheets: &[Arc<::stylesheets::Stylesheet>]) {
|
||||
pub fn set_device<'a, I, S>(
|
||||
&mut self,
|
||||
mut device: Device,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
stylesheets: I,
|
||||
)
|
||||
where
|
||||
I: Iterator<Item = &'a S> + Clone,
|
||||
S: StylesheetInDocument + ToMediaListKey + 'static,
|
||||
{
|
||||
let cascaded_rule = ViewportRule {
|
||||
declarations: viewport_rule::Cascade::from_stylesheets(
|
||||
stylesheets.iter().map(|s| &**s),
|
||||
stylesheets.clone(),
|
||||
guard,
|
||||
&device
|
||||
).finish(),
|
||||
|
@ -897,7 +876,7 @@ impl Stylist {
|
|||
|
||||
self.device = device;
|
||||
let features_changed = self.media_features_change_changed_style(
|
||||
stylesheets.iter().map(|s| &**s),
|
||||
stylesheets,
|
||||
guard
|
||||
);
|
||||
self.is_device_dirty |= !features_changed.is_empty();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue