style: Move the StyleSheetSet into the Stylist.

This will allow tracking whether there have been only additions to the
stylesheet set, and in that case don't destroy and completely rebuild the
invalidation map.
This commit is contained in:
Emilio Cobos Álvarez 2017-08-21 15:13:06 +02:00
parent 02b203891f
commit cbf388a5fd
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
7 changed files with 399 additions and 316 deletions

View file

@ -135,8 +135,7 @@ use style::properties::PropertyId;
use style::selector_parser::SnapshotMap; use style::selector_parser::SnapshotMap;
use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, REPOSITION, STORE_OVERFLOW}; use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, REPOSITION, STORE_OVERFLOW};
use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards}; use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards};
use style::stylesheet_set::StylesheetSet; use style::stylesheets::{Origin, OriginSet, Stylesheet, DocumentStyleSheet, StylesheetInDocument, UserAgentStylesheets};
use style::stylesheets::{Origin, Stylesheet, DocumentStyleSheet, StylesheetInDocument, UserAgentStylesheets};
use style::stylist::Stylist; use style::stylist::Stylist;
use style::thread_state; use style::thread_state;
use style::timer::Timer; use style::timer::Timer;
@ -160,9 +159,6 @@ pub struct LayoutThread {
/// Performs CSS selector matching and style resolution. /// Performs CSS selector matching and style resolution.
stylist: Stylist, stylist: Stylist,
/// The list of stylesheets synchronized with the document.
stylesheets: StylesheetSet<DocumentStyleSheet>,
/// Is the current reflow of an iframe, as opposed to a root window? /// Is the current reflow of an iframe, as opposed to a root window?
is_iframe: bool, is_iframe: bool,
@ -519,7 +515,6 @@ impl LayoutThread {
webrender_api: webrender_api_sender.create_api(), webrender_api: webrender_api_sender.create_api(),
webrender_document, webrender_document,
stylist: Stylist::new(device, QuirksMode::NoQuirks), stylist: Stylist::new(device, QuirksMode::NoQuirks),
stylesheets: StylesheetSet::new(),
rw_data: Arc::new(Mutex::new( rw_data: Arc::new(Mutex::new(
LayoutThreadData { LayoutThreadData {
constellation_chan: constellation_chan, constellation_chan: constellation_chan,
@ -670,16 +665,14 @@ impl LayoutThread {
match before_stylesheet { match before_stylesheet {
Some(insertion_point) => { Some(insertion_point) => {
self.stylesheets.insert_stylesheet_before( self.stylist.insert_stylesheet_before(
Some(self.stylist.device()),
DocumentStyleSheet(stylesheet.clone()), DocumentStyleSheet(stylesheet.clone()),
DocumentStyleSheet(insertion_point), DocumentStyleSheet(insertion_point),
&guard, &guard,
) )
} }
None => { None => {
self.stylesheets.append_stylesheet( self.stylist.append_stylesheet(
Some(self.stylist.device()),
DocumentStyleSheet(stylesheet.clone()), DocumentStyleSheet(stylesheet.clone()),
&guard, &guard,
) )
@ -688,8 +681,7 @@ impl LayoutThread {
} }
Msg::RemoveStylesheet(stylesheet) => { Msg::RemoveStylesheet(stylesheet) => {
let guard = stylesheet.shared_lock.read(); let guard = stylesheet.shared_lock.read();
self.stylesheets.remove_stylesheet( self.stylist.remove_stylesheet(
Some(self.stylist.device()),
DocumentStyleSheet(stylesheet.clone()), DocumentStyleSheet(stylesheet.clone()),
&guard, &guard,
); );
@ -1154,8 +1146,9 @@ impl LayoutThread {
let author_guard = document_shared_lock.read(); let author_guard = document_shared_lock.read();
let device = Device::new(MediaType::screen(), initial_viewport, device_pixel_ratio); let device = Device::new(MediaType::screen(), initial_viewport, device_pixel_ratio);
let sheet_origins_affected_by_device_change = let sheet_origins_affected_by_device_change =
self.stylist.set_device(device, &author_guard, self.stylesheets.iter()); self.stylist.set_device(device, &author_guard);
self.stylist.force_stylesheet_origins_dirty(sheet_origins_affected_by_device_change);
self.viewport_size = self.viewport_size =
self.stylist.viewport_constraints().map_or(current_screen_size, |constraints| { self.stylist.viewport_constraints().map_or(current_screen_size, |constraints| {
debug!("Viewport constraints: {:?}", constraints); debug!("Viewport constraints: {:?}", constraints);
@ -1173,7 +1166,7 @@ impl LayoutThread {
.send(ConstellationMsg::ViewportConstrained(self.id, constraints.clone())) .send(ConstellationMsg::ViewportConstrained(self.id, constraints.clone()))
.unwrap(); .unwrap();
} }
if self.stylesheets.iter().any(|sheet| sheet.0.dirty_on_viewport_size_change()) { if self.stylist.iter_stylesheets().any(|sheet| sheet.0.dirty_on_viewport_size_change()) {
let mut iter = element.as_node().traverse_preorder(); let mut iter = element.as_node().traverse_preorder();
let mut next = iter.next(); let mut next = iter.next();
@ -1207,65 +1200,41 @@ impl LayoutThread {
ua_or_user: &ua_or_user_guard, ua_or_user: &ua_or_user_guard,
}; };
let needs_dirtying = { {
debug!("Flushing stylist");
let mut origins_dirty = sheet_origins_affected_by_device_change;
debug!("Device changes: {:?}", origins_dirty);
if self.first_reflow.get() { if self.first_reflow.get() {
debug!("First reflow, rebuilding user and UA rules"); debug!("First reflow, rebuilding user and UA rules");
let mut ua_and_user = OriginSet::empty();
origins_dirty |= Origin::User; ua_and_user |= Origin::User;
origins_dirty |= Origin::UserAgent; ua_and_user |= Origin::UserAgent;
self.stylist.force_stylesheet_origins_dirty(ua_and_user);
for stylesheet in &ua_stylesheets.user_or_user_agent_stylesheets { for stylesheet in &ua_stylesheets.user_or_user_agent_stylesheets {
self.handle_add_stylesheet(stylesheet, &ua_or_user_guard); self.handle_add_stylesheet(stylesheet, &ua_or_user_guard);
} }
} }
let (iter, invalidation_origins_dirty) = self.stylesheets.flush(Some(element));
debug!("invalidation: {:?}", invalidation_origins_dirty);
origins_dirty |= invalidation_origins_dirty;
if data.stylesheets_changed { if data.stylesheets_changed {
debug!("Doc sheets changed, flushing author sheets too"); debug!("Doc sheets changed, flushing author sheets too");
origins_dirty |= Origin::Author; self.stylist.force_stylesheet_origins_dirty(Origin::Author.into());
} }
if !origins_dirty.is_empty() {
let mut extra_data = Default::default(); let mut extra_data = Default::default();
self.stylist.rebuild( self.stylist.flush(
iter,
&guards, &guards,
Some(ua_stylesheets), Some(ua_stylesheets),
/* author_style_disabled = */ false,
&mut extra_data, &mut extra_data,
origins_dirty, Some(element),
); );
} }
!origins_dirty.is_empty() if viewport_size_changed {
};
let needs_reflow = viewport_size_changed && !needs_dirtying;
if needs_dirtying {
if let Some(mut d) = element.mutate_data() {
if d.has_styles() {
d.restyle.hint.insert(RestyleHint::restyle_subtree());
}
}
}
if needs_reflow {
if let Some(mut flow) = self.try_get_layout_root(element.as_node()) { if let Some(mut flow) = self.try_get_layout_root(element.as_node()) {
LayoutThread::reflow_all_nodes(FlowRef::deref_mut(&mut flow)); LayoutThread::reflow_all_nodes(FlowRef::deref_mut(&mut flow));
} }
} }
let restyles = document.drain_pending_restyles(); let restyles = document.drain_pending_restyles();
debug!("Draining restyles: {} (needs dirtying? {:?})", debug!("Draining restyles: {}", restyles.len());
restyles.len(), needs_dirtying);
let mut map = SnapshotMap::new(); let mut map = SnapshotMap::new();
let elements_with_snapshot: Vec<_> = let elements_with_snapshot: Vec<_> =
restyles restyles

View file

@ -16,7 +16,6 @@ use media_queries::{Device, MediaList};
use properties::ComputedValues; 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 stylesheets::{PerOrigin, StylesheetContents, StylesheetInDocument}; use stylesheets::{PerOrigin, StylesheetContents, StylesheetInDocument};
use stylist::{ExtraStyleData, Stylist}; use stylist::{ExtraStyleData, Stylist};
@ -24,6 +23,8 @@ use stylist::{ExtraStyleData, Stylist};
#[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug)]
pub struct GeckoStyleSheet(*const ServoStyleSheet); pub struct GeckoStyleSheet(*const ServoStyleSheet);
unsafe impl Sync for GeckoStyleSheet {}
impl ToMediaListKey for ::gecko::data::GeckoStyleSheet { impl ToMediaListKey for ::gecko::data::GeckoStyleSheet {
fn to_media_list_key(&self) -> MediaListKey { fn to_media_list_key(&self) -> MediaListKey {
use std::mem; use std::mem;
@ -114,9 +115,6 @@ pub struct PerDocumentStyleDataImpl {
/// Rule processor. /// Rule processor.
pub stylist: Stylist, pub stylist: Stylist,
/// List of stylesheets, mirrored from Gecko.
pub stylesheets: StylesheetSet<GeckoStyleSheet>,
/// List of effective @font-face and @counter-style rules. /// List of effective @font-face and @counter-style rules.
pub extra_style_data: PerOrigin<ExtraStyleData>, pub extra_style_data: PerOrigin<ExtraStyleData>,
} }
@ -135,7 +133,6 @@ impl PerDocumentStyleData {
PerDocumentStyleData(AtomicRefCell::new(PerDocumentStyleDataImpl { PerDocumentStyleData(AtomicRefCell::new(PerDocumentStyleDataImpl {
stylist: Stylist::new(device, quirks_mode.into()), stylist: Stylist::new(device, quirks_mode.into()),
stylesheets: StylesheetSet::new(),
extra_style_data: Default::default(), extra_style_data: Default::default(),
})) }))
} }
@ -153,26 +150,20 @@ impl PerDocumentStyleData {
impl PerDocumentStyleDataImpl { impl PerDocumentStyleDataImpl {
/// Recreate the style data if the stylesheets have changed. /// Recreate the style data if the stylesheets have changed.
pub fn flush_stylesheets<E>(&mut self, pub fn flush_stylesheets<E>(
&mut self,
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
document_element: Option<E>) document_element: Option<E>,
where E: TElement, )
where
E: TElement,
{ {
if !self.stylesheets.has_changed() { self.stylist.flush(
return;
}
let author_style_disabled = self.stylesheets.author_style_disabled();
let (iter, dirty_origins) = self.stylesheets.flush(document_element);
self.stylist.rebuild(
iter,
&StylesheetGuards::same(guard), &StylesheetGuards::same(guard),
/* ua_sheets = */ None, /* ua_sheets = */ None,
author_style_disabled,
&mut self.extra_style_data, &mut self.extra_style_data,
dirty_origins, document_element,
); )
} }
/// Returns whether private browsing is enabled. /// Returns whether private browsing is enabled.

View file

@ -106,9 +106,6 @@ where
} }
/// Appends a new stylesheet to the current set. /// 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( pub fn append_stylesheet(
&mut self, &mut self,
device: Option<&Device>, device: Option<&Device>,
@ -122,9 +119,6 @@ where
} }
/// Prepend a new stylesheet to the current set. /// 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( pub fn prepend_stylesheet(
&mut self, &mut self,
device: Option<&Device>, device: Option<&Device>,
@ -138,15 +132,12 @@ where
} }
/// Insert a given stylesheet before another stylesheet in the document. /// 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( pub fn insert_stylesheet_before(
&mut self, &mut self,
device: Option<&Device>, device: Option<&Device>,
sheet: S, sheet: S,
before_sheet: S, before_sheet: S,
guard: &SharedRwLockReadGuard guard: &SharedRwLockReadGuard,
) { ) {
debug!("StylesheetSet::insert_stylesheet_before"); debug!("StylesheetSet::insert_stylesheet_before");
self.remove_stylesheet_if_present(&sheet); self.remove_stylesheet_if_present(&sheet);
@ -158,9 +149,6 @@ where
} }
/// Remove a given stylesheet from the set. /// 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( pub fn remove_stylesheet(
&mut self, &mut self,
device: Option<&Device>, device: Option<&Device>,

View file

@ -44,7 +44,7 @@ pub use self::memory::{MallocSizeOf, MallocSizeOfFn, MallocSizeOfWithGuard};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub use self::memory::{MallocSizeOfWithRepeats, SizeOfState}; pub use self::memory::{MallocSizeOfWithRepeats, SizeOfState};
pub use self::namespace_rule::NamespaceRule; pub use self::namespace_rule::NamespaceRule;
pub use self::origin::{Origin, OriginSet, PerOrigin}; pub use self::origin::{Origin, OriginSet, PerOrigin, PerOriginIter};
pub use self::page_rule::PageRule; pub use self::page_rule::PageRule;
pub use self::rule_parser::{State, TopLevelRuleParser}; pub use self::rule_parser::{State, TopLevelRuleParser};
pub use self::rule_list::{CssRules, CssRulesHelpers}; pub use self::rule_list::{CssRules, CssRulesHelpers};

View file

@ -279,7 +279,11 @@ impl StylesheetInDocument for Stylesheet {
/// A simple wrapper over an `Arc<Stylesheet>`, with pointer comparison, and /// A simple wrapper over an `Arc<Stylesheet>`, with pointer comparison, and
/// suitable for its use in a `StylesheetSet`. /// suitable for its use in a `StylesheetSet`.
#[derive(Clone)] #[derive(Clone)]
pub struct DocumentStyleSheet(pub Arc<Stylesheet>); #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct DocumentStyleSheet(
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
pub Arc<Stylesheet>
);
impl PartialEq for DocumentStyleSheet { impl PartialEq for DocumentStyleSheet {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {

View file

@ -37,10 +37,11 @@ use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
use smallvec::VecLike; use smallvec::VecLike;
use std::fmt::Debug; use std::fmt::Debug;
use style_traits::viewport::ViewportConstraints; use style_traits::viewport::ViewportConstraints;
use stylesheet_set::{StylesheetSet, StylesheetIterator};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use stylesheets::{CounterStyleRule, FontFaceRule}; use stylesheets::{CounterStyleRule, FontFaceRule};
use stylesheets::{CssRule, StyleRule}; use stylesheets::{CssRule, StyleRule};
use stylesheets::{StylesheetInDocument, Origin, OriginSet, PerOrigin}; use stylesheets::{StylesheetInDocument, Origin, OriginSet, PerOrigin, PerOriginIter};
use stylesheets::UserAgentStylesheets; use stylesheets::UserAgentStylesheets;
use stylesheets::keyframes_rule::KeyframesAnimation; use stylesheets::keyframes_rule::KeyframesAnimation;
use stylesheets::viewport_rule::{self, MaybeNew, ViewportRule}; use stylesheets::viewport_rule::{self, MaybeNew, ViewportRule};
@ -48,140 +49,43 @@ use thread_state;
pub use ::fnv::FnvHashMap; pub use ::fnv::FnvHashMap;
/// This structure holds all the selectors and device characteristics /// The type of the stylesheets that the stylist contains.
/// for a given document. The selectors are converted into `Rule`s #[cfg(feature = "servo")]
/// (defined in rust-selectors), and sorted into `SelectorMap`s keyed pub type StylistSheet = ::stylesheets::DocumentStyleSheet;
/// off stylesheet origin and pseudo-element (see `CascadeData`).
/// /// The type of the stylesheets that the stylist contains.
/// This structure is effectively created once per pipeline, in the #[cfg(feature = "gecko")]
/// LayoutThread corresponding to that pipeline. pub type StylistSheet = ::gecko::data::GeckoStyleSheet;
/// All the computed information for a stylesheet.
#[derive(Default)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct Stylist { struct DocumentCascadeData {
/// Device that the stylist is currently evaluating against. /// Common data for all the origins.
/// per_origin: PerOrigin<CascadeData>,
/// This field deserves a bigger comment due to the different use that Gecko
/// and Servo give to it (that we should eventually unify).
///
/// With Gecko, the device is never changed. Gecko manually tracks whether
/// the device data should be reconstructed, and "resets" the state of the
/// device.
///
/// On Servo, on the other hand, the device is a really cheap representation
/// that is recreated each time some constraint changes and calling
/// `set_device`.
device: Device,
/// Viewport constraints based on the current device.
viewport_constraints: Option<ViewportConstraints>,
/// If true, the quirks-mode stylesheet is applied.
#[cfg_attr(feature = "servo", ignore_heap_size_of = "defined in selectors")]
quirks_mode: QuirksMode,
/// Selector maps for all of the style sheets in the stylist, after
/// evalutaing media rules against the current device, split out per
/// cascade level.
cascade_data: PerOrigin<CascadeData>,
/// The rule tree, that stores the results of selector matching.
rule_tree: RuleTree,
/// Applicable declarations for a given non-eagerly cascaded pseudo-element. /// Applicable declarations for a given non-eagerly cascaded pseudo-element.
///
/// These are eagerly computed once, and then used to resolve the new /// These are eagerly computed once, and then used to resolve the new
/// computed values on the fly on layout. /// computed values on the fly on layout.
/// ///
/// These are only filled from UA stylesheets.
///
/// FIXME(emilio): Use the rule tree! /// FIXME(emilio): Use the rule tree!
precomputed_pseudo_element_decls: PerPseudoElementMap<Vec<ApplicableDeclarationBlock>>, precomputed_pseudo_element_decls: PerPseudoElementMap<Vec<ApplicableDeclarationBlock>>,
/// The total number of times the stylist has been rebuilt.
num_rebuilds: usize,
} }
/// What cascade levels to include when styling elements. impl DocumentCascadeData {
#[derive(Copy, Clone, PartialEq)] fn iter_origins(&self) -> PerOriginIter<CascadeData> {
pub enum RuleInclusion { self.per_origin.iter_origins()
/// Include rules for style sheets at all cascade levels. This is the
/// normal rule inclusion mode.
All,
/// Only include rules from UA and user level sheets. Used to implement
/// `getDefaultComputedStyle`.
DefaultOnly,
} }
#[cfg(feature = "gecko")] /// Rebuild the cascade data for the given document stylesheets, and
impl From<StyleRuleInclusion> for RuleInclusion { /// optionally with a set of user agent stylesheets.
fn from(value: StyleRuleInclusion) -> Self { fn rebuild<'a, I, S>(
match value {
StyleRuleInclusion::All => RuleInclusion::All,
StyleRuleInclusion::DefaultOnly => RuleInclusion::DefaultOnly,
}
}
}
impl Stylist {
/// Construct a new `Stylist`, using given `Device` and `QuirksMode`.
/// If more members are added here, think about whether they should
/// be reset in clear().
#[inline]
pub fn new(device: Device, quirks_mode: QuirksMode) -> Self {
Stylist {
viewport_constraints: None,
device: device,
quirks_mode: quirks_mode,
cascade_data: Default::default(),
precomputed_pseudo_element_decls: PerPseudoElementMap::default(),
rule_tree: RuleTree::new(),
num_rebuilds: 0,
}
// FIXME: Add iso-8859-9.css when the documents encoding is ISO-8859-8.
}
/// Returns the number of selectors.
pub fn num_selectors(&self) -> usize {
self.cascade_data.iter_origins().map(|(d, _)| d.num_selectors).sum()
}
/// Returns the number of declarations.
pub fn num_declarations(&self) -> usize {
self.cascade_data.iter_origins().map(|(d, _)| d.num_declarations).sum()
}
/// Returns the number of times the stylist has been rebuilt.
pub fn num_rebuilds(&self) -> usize {
self.num_rebuilds
}
/// Returns the number of revalidation_selectors.
pub fn num_revalidation_selectors(&self) -> usize {
self.cascade_data.iter_origins()
.map(|(d, _)| d.selectors_for_cache_revalidation.len()).sum()
}
/// Returns the number of entries in invalidation maps.
pub fn num_invalidations(&self) -> usize {
self.cascade_data.iter_origins()
.map(|(d, _)| d.invalidation_map.len()).sum()
}
/// Invokes `f` with the `InvalidationMap` for each origin.
///
/// NOTE(heycam) This might be better as an `iter_invalidation_maps`, once
/// we have `impl trait` and can return that easily without bothering to
/// create a whole new iterator type.
pub fn each_invalidation_map<F>(&self, mut f: F)
where F: FnMut(&InvalidationMap)
{
for (data, _) in self.cascade_data.iter_origins() {
f(&data.invalidation_map)
}
}
/// Rebuild the stylist for the given document stylesheets, and optionally
/// with a set of user agent stylesheets.
pub fn rebuild<'a, I, S>(
&mut self, &mut self,
device: &Device,
quirks_mode: QuirksMode,
doc_stylesheets: I, doc_stylesheets: I,
guards: &StylesheetGuards, guards: &StylesheetGuards,
ua_stylesheets: Option<&UserAgentStylesheets>, ua_stylesheets: Option<&UserAgentStylesheets>,
@ -195,41 +99,9 @@ impl Stylist {
{ {
debug_assert!(!origins_to_rebuild.is_empty()); debug_assert!(!origins_to_rebuild.is_empty());
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
// overrides the viewport size which may change the evaluation of
// media queries (or may not? how are viewport units in media
// queries defined?)
let cascaded_rule = ViewportRule {
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);
if let Some(ref constraints) = self.viewport_constraints {
self.device.account_for_viewport_rule(constraints);
}
}
for origin in origins_to_rebuild.iter() { for origin in origins_to_rebuild.iter() {
extra_data.borrow_mut_for_origin(&origin).clear(); extra_data.borrow_mut_for_origin(&origin).clear();
self.cascade_data.borrow_mut_for_origin(&origin).clear(); self.per_origin.borrow_mut_for_origin(&origin).clear();
} }
if origins_to_rebuild.contains(Origin::UserAgent.into()) { if origins_to_rebuild.contains(Origin::UserAgent.into()) {
@ -248,14 +120,16 @@ impl Stylist {
if origins_to_rebuild.contains(sheet_origin.into()) { if origins_to_rebuild.contains(sheet_origin.into()) {
self.add_stylesheet( self.add_stylesheet(
device,
quirks_mode,
stylesheet, stylesheet,
guards.ua_or_user, guards.ua_or_user,
extra_data extra_data,
); );
} }
} }
if self.quirks_mode != QuirksMode::NoQuirks { if quirks_mode != QuirksMode::NoQuirks {
let stylesheet = &ua_stylesheets.quirks_mode_stylesheet; let stylesheet = &ua_stylesheets.quirks_mode_stylesheet;
let sheet_origin = let sheet_origin =
stylesheet.contents(guards.ua_or_user).origin; stylesheet.contents(guards.ua_or_user).origin;
@ -267,6 +141,8 @@ impl Stylist {
if origins_to_rebuild.contains(sheet_origin.into()) { if origins_to_rebuild.contains(sheet_origin.into()) {
self.add_stylesheet( self.add_stylesheet(
device,
quirks_mode,
&ua_stylesheets.quirks_mode_stylesheet, &ua_stylesheets.quirks_mode_stylesheet,
guards.ua_or_user, guards.ua_or_user,
extra_data extra_data
@ -285,12 +161,20 @@ impl Stylist {
}); });
for stylesheet in sheets_to_add { for stylesheet in sheets_to_add {
self.add_stylesheet(stylesheet, guards.author, extra_data); self.add_stylesheet(
device,
quirks_mode,
stylesheet,
guards.author,
extra_data
);
} }
} }
fn add_stylesheet<S>( fn add_stylesheet<S>(
&mut self, &mut self,
device: &Device,
quirks_mode: QuirksMode,
stylesheet: &S, stylesheet: &S,
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
_extra_data: &mut PerOrigin<ExtraStyleData> _extra_data: &mut PerOrigin<ExtraStyleData>
@ -299,19 +183,19 @@ impl Stylist {
S: StylesheetInDocument + ToMediaListKey + 'static, S: StylesheetInDocument + ToMediaListKey + 'static,
{ {
if !stylesheet.enabled() || if !stylesheet.enabled() ||
!stylesheet.is_effective_for_device(&self.device, guard) { !stylesheet.is_effective_for_device(device, guard) {
return; return;
} }
let origin = stylesheet.origin(guard); let origin = stylesheet.origin(guard);
let origin_cascade_data = let origin_cascade_data =
self.cascade_data.borrow_mut_for_origin(&origin); self.per_origin.borrow_mut_for_origin(&origin);
origin_cascade_data origin_cascade_data
.effective_media_query_results .effective_media_query_results
.saw_effective(stylesheet); .saw_effective(stylesheet);
for rule in stylesheet.effective_rules(&self.device, guard) { for rule in stylesheet.effective_rules(device, guard) {
match *rule { match *rule {
CssRule::Style(ref locked) => { CssRule::Style(ref locked) => {
let style_rule = locked.read_with(&guard); let style_rule = locked.read_with(&guard);
@ -352,7 +236,7 @@ impl Stylist {
}; };
let hashes = let hashes =
AncestorHashes::new(&selector, self.quirks_mode); AncestorHashes::new(&selector, quirks_mode);
let rule = Rule::new( let rule = Rule::new(
selector.clone(), selector.clone(),
@ -361,11 +245,11 @@ impl Stylist {
origin_cascade_data.rules_source_order origin_cascade_data.rules_source_order
); );
map.insert(rule, self.quirks_mode); map.insert(rule, quirks_mode);
origin_cascade_data origin_cascade_data
.invalidation_map .invalidation_map
.note_selector(selector, self.quirks_mode); .note_selector(selector, quirks_mode);
let mut visitor = StylistSelectorVisitor { let mut visitor = StylistSelectorVisitor {
needs_revalidation: false, needs_revalidation: false,
passed_rightmost_selector: false, passed_rightmost_selector: false,
@ -380,7 +264,8 @@ impl Stylist {
if visitor.needs_revalidation { if visitor.needs_revalidation {
origin_cascade_data.selectors_for_cache_revalidation.insert( origin_cascade_data.selectors_for_cache_revalidation.insert(
RevalidationSelectorAndHashes::new(selector.clone(), hashes), RevalidationSelectorAndHashes::new(selector.clone(), hashes),
self.quirks_mode); quirks_mode
);
} }
} }
origin_cascade_data.rules_source_order += 1; origin_cascade_data.rules_source_order += 1;
@ -433,12 +318,263 @@ impl Stylist {
} }
} }
} }
}
/// This structure holds all the selectors and device characteristics
/// for a given document. The selectors are converted into `Rule`s
/// and sorted into `SelectorMap`s keyed off stylesheet origin and
/// pseudo-element (see `CascadeData`).
///
/// This structure is effectively created once per pipeline, in the
/// LayoutThread corresponding to that pipeline.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct Stylist {
/// Device that the stylist is currently evaluating against.
///
/// This field deserves a bigger comment due to the different use that Gecko
/// and Servo give to it (that we should eventually unify).
///
/// With Gecko, the device is never changed. Gecko manually tracks whether
/// the device data should be reconstructed, and "resets" the state of the
/// device.
///
/// On Servo, on the other hand, the device is a really cheap representation
/// that is recreated each time some constraint changes and calling
/// `set_device`.
device: Device,
/// Viewport constraints based on the current device.
viewport_constraints: Option<ViewportConstraints>,
/// The list of stylesheets.
stylesheets: StylesheetSet<StylistSheet>,
/// If true, the quirks-mode stylesheet is applied.
#[cfg_attr(feature = "servo", ignore_heap_size_of = "defined in selectors")]
quirks_mode: QuirksMode,
/// Selector maps for all of the style sheets in the stylist, after
/// evalutaing media rules against the current device, split out per
/// cascade level.
cascade_data: DocumentCascadeData,
/// The rule tree, that stores the results of selector matching.
rule_tree: RuleTree,
/// The total number of times the stylist has been rebuilt.
num_rebuilds: usize,
}
/// What cascade levels to include when styling elements.
#[derive(Copy, Clone, PartialEq)]
pub enum RuleInclusion {
/// Include rules for style sheets at all cascade levels. This is the
/// normal rule inclusion mode.
All,
/// Only include rules from UA and user level sheets. Used to implement
/// `getDefaultComputedStyle`.
DefaultOnly,
}
#[cfg(feature = "gecko")]
impl From<StyleRuleInclusion> for RuleInclusion {
fn from(value: StyleRuleInclusion) -> Self {
match value {
StyleRuleInclusion::All => RuleInclusion::All,
StyleRuleInclusion::DefaultOnly => RuleInclusion::DefaultOnly,
}
}
}
impl Stylist {
/// Construct a new `Stylist`, using given `Device` and `QuirksMode`.
/// If more members are added here, think about whether they should
/// be reset in clear().
#[inline]
pub fn new(device: Device, quirks_mode: QuirksMode) -> Self {
Self {
viewport_constraints: None,
device,
quirks_mode,
stylesheets: StylesheetSet::new(),
cascade_data: Default::default(),
rule_tree: RuleTree::new(),
num_rebuilds: 0,
}
}
/// Returns the number of selectors.
pub fn num_selectors(&self) -> usize {
self.cascade_data.iter_origins().map(|(d, _)| d.num_selectors).sum()
}
/// Returns the number of declarations.
pub fn num_declarations(&self) -> usize {
self.cascade_data.iter_origins().map(|(d, _)| d.num_declarations).sum()
}
/// Returns the number of times the stylist has been rebuilt.
pub fn num_rebuilds(&self) -> usize {
self.num_rebuilds
}
/// Returns the number of revalidation_selectors.
pub fn num_revalidation_selectors(&self) -> usize {
self.cascade_data.iter_origins()
.map(|(d, _)| d.selectors_for_cache_revalidation.len()).sum()
}
/// Returns the number of entries in invalidation maps.
pub fn num_invalidations(&self) -> usize {
self.cascade_data.iter_origins()
.map(|(d, _)| d.invalidation_map.len()).sum()
}
/// Invokes `f` with the `InvalidationMap` for each origin.
///
/// NOTE(heycam) This might be better as an `iter_invalidation_maps`, once
/// we have `impl trait` and can return that easily without bothering to
/// create a whole new iterator type.
pub fn each_invalidation_map<F>(&self, mut f: F)
where F: FnMut(&InvalidationMap)
{
for (data, _) in self.cascade_data.iter_origins() {
f(&data.invalidation_map)
}
}
/// Flush the list of stylesheets if they changed, ensuring the stylist is
/// up-to-date.
///
/// FIXME(emilio): Move the `ua_sheets` to the Stylist too?
pub fn flush<E>(
&mut self,
guards: &StylesheetGuards,
ua_sheets: Option<&UserAgentStylesheets>,
extra_data: &mut PerOrigin<ExtraStyleData>,
document_element: Option<E>,
)
where
E: TElement,
{
if !self.stylesheets.has_changed() {
return;
}
let author_style_disabled = self.stylesheets.author_style_disabled();
let (doc_stylesheets, origins_to_rebuild) = self.stylesheets.flush(document_element);
if origins_to_rebuild.is_empty() {
return;
}
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
// overrides the viewport size which may change the evaluation of
// media queries (or may not? how are viewport units in media
// queries defined?)
let cascaded_rule = ViewportRule {
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);
if let Some(ref constraints) = self.viewport_constraints {
self.device.account_for_viewport_rule(constraints);
}
}
self.cascade_data.rebuild(
&self.device,
self.quirks_mode,
doc_stylesheets,
guards,
ua_sheets,
author_style_disabled,
extra_data,
origins_to_rebuild,
);
}
/// Insert a given stylesheet before another stylesheet in the document.
pub fn insert_stylesheet_before(
&mut self,
sheet: StylistSheet,
before_sheet: StylistSheet,
guard: &SharedRwLockReadGuard,
) {
self.stylesheets.insert_stylesheet_before(
Some(&self.device),
sheet,
before_sheet,
guard,
)
}
/// Marks a given stylesheet origin as dirty, due to, for example, changes
/// in the declarations that affect a given rule.
///
/// FIXME(emilio): Eventually it'd be nice for this to become more
/// fine-grained.
pub fn force_stylesheet_origins_dirty(&mut self, origins: OriginSet) {
self.stylesheets.force_dirty(origins)
}
/// Iterate over the given set of stylesheets.
///
/// This is very intentionally exposed only on `&mut self`, since we don't
/// want to give access to the stylesheet list from worker threads.
pub fn iter_stylesheets(&mut self) -> StylesheetIterator<StylistSheet> {
self.stylesheets.iter()
}
/// Sets whether author style is enabled or not.
pub fn set_author_style_disabled(&mut self, disabled: bool) {
self.stylesheets.set_author_style_disabled(disabled);
}
/// Returns whether we've recorded any stylesheet change so far.
pub fn stylesheets_have_changed(&self) -> bool {
self.stylesheets.has_changed()
}
/// Appends a new stylesheet to the current set.
pub fn append_stylesheet(&mut self, sheet: StylistSheet, guard: &SharedRwLockReadGuard) {
self.stylesheets.append_stylesheet(Some(&self.device), sheet, guard)
}
/// Appends a new stylesheet to the current set.
pub fn prepend_stylesheet(&mut self, sheet: StylistSheet, guard: &SharedRwLockReadGuard) {
self.stylesheets.prepend_stylesheet(Some(&self.device), sheet, guard)
}
/// Remove a given stylesheet to the current set.
pub fn remove_stylesheet(&mut self, sheet: StylistSheet, guard: &SharedRwLockReadGuard) {
self.stylesheets.remove_stylesheet(Some(&self.device), sheet, guard)
}
/// Returns whether the given attribute might appear in an attribute /// Returns whether the given attribute might appear in an attribute
/// selector of some rule in the stylist. /// selector of some rule in the stylist.
pub fn might_have_attribute_dependency(&self, pub fn might_have_attribute_dependency(
local_name: &LocalName) &self,
-> bool { local_name: &LocalName,
) -> bool {
if *local_name == local_name!("style") { if *local_name == local_name!("style") {
self.cascade_data self.cascade_data
.iter_origins() .iter_origins()
@ -483,7 +619,8 @@ impl Stylist {
-> Arc<ComputedValues> { -> Arc<ComputedValues> {
debug_assert!(pseudo.is_precomputed()); debug_assert!(pseudo.is_precomputed());
let rule_node = match self.precomputed_pseudo_element_decls.get(pseudo) { let rule_node =
match self.cascade_data.precomputed_pseudo_element_decls.get(pseudo) {
Some(declarations) => { Some(declarations) => {
self.rule_tree.insert_ordered_rules_with_important( self.rule_tree.insert_ordered_rules_with_important(
declarations.into_iter().map(|a| (a.source.clone(), a.level())), declarations.into_iter().map(|a| (a.source.clone(), a.level())),
@ -523,11 +660,12 @@ impl Stylist {
/// Returns the style for an anonymous box of the given type. /// Returns the style for an anonymous box of the given type.
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
pub fn style_for_anonymous(&self, pub fn style_for_anonymous(
&self,
guards: &StylesheetGuards, guards: &StylesheetGuards,
pseudo: &PseudoElement, pseudo: &PseudoElement,
parent_style: &ComputedValues) parent_style: &ComputedValues
-> Arc<ComputedValues> { ) -> Arc<ComputedValues> {
use font_metrics::ServoMetricsProvider; use font_metrics::ServoMetricsProvider;
// For most (but not all) pseudo-elements, we inherit all values from the parent. // For most (but not all) pseudo-elements, we inherit all values from the parent.
@ -554,8 +692,13 @@ impl Stylist {
if inherit_all { if inherit_all {
cascade_flags.insert(INHERIT_ALL); cascade_flags.insert(INHERIT_ALL);
} }
self.precomputed_values_for_pseudo(guards, &pseudo, Some(parent_style), cascade_flags, self.precomputed_values_for_pseudo(
&ServoMetricsProvider) guards,
&pseudo,
Some(parent_style),
cascade_flags,
&ServoMetricsProvider
)
} }
/// Computes a pseudo-element style lazily during layout. /// Computes a pseudo-element style lazily during layout.
@ -849,22 +992,21 @@ impl Stylist {
/// FIXME(emilio): The semantics of the device for Servo and Gecko are /// FIXME(emilio): The semantics of the device for Servo and Gecko are
/// different enough we may want to unify them. /// different enough we may want to unify them.
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
pub fn set_device<'a, I, S>( pub fn set_device(
&mut self, &mut self,
mut device: Device, mut device: Device,
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
stylesheets: I, ) -> OriginSet {
) -> OriginSet let cascaded_rule = {
where let stylesheets = self.stylesheets.iter();
I: Iterator<Item = &'a S> + Clone,
S: StylesheetInDocument + ToMediaListKey + 'static, ViewportRule {
{
let cascaded_rule = ViewportRule {
declarations: viewport_rule::Cascade::from_stylesheets( declarations: viewport_rule::Cascade::from_stylesheets(
stylesheets.clone(), stylesheets.clone(),
guard, guard,
&device &device
).finish(), ).finish(),
}
}; };
self.viewport_constraints = self.viewport_constraints =
@ -875,28 +1017,21 @@ impl Stylist {
} }
self.device = device; self.device = device;
self.media_features_change_changed_style( self.media_features_change_changed_style(guard)
stylesheets,
guard,
)
} }
/// Returns whether, given a media feature change, any previously-applicable /// Returns whether, given a media feature change, any previously-applicable
/// style has become non-applicable, or vice-versa for each origin. /// style has become non-applicable, or vice-versa for each origin.
pub fn media_features_change_changed_style<'a, I, S>( pub fn media_features_change_changed_style(
&self, &self,
stylesheets: I,
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
) -> OriginSet ) -> OriginSet {
where
I: Iterator<Item = &'a S>,
S: StylesheetInDocument + ToMediaListKey + 'static,
{
use invalidation::media_queries::PotentiallyEffectiveMediaRules; use invalidation::media_queries::PotentiallyEffectiveMediaRules;
debug!("Stylist::media_features_change_changed_style"); debug!("Stylist::media_features_change_changed_style");
let mut origins = OriginSet::empty(); let mut origins = OriginSet::empty();
let stylesheets = self.stylesheets.iter();
'stylesheets_loop: for stylesheet in stylesheets { 'stylesheets_loop: for stylesheet in stylesheets {
let effective_now = let effective_now =
@ -909,7 +1044,7 @@ impl Stylist {
} }
let origin_cascade_data = let origin_cascade_data =
self.cascade_data.borrow_for_origin(&origin); self.cascade_data.per_origin.borrow_for_origin(&origin);
let effective_then = let effective_then =
origin_cascade_data origin_cascade_data
@ -1034,7 +1169,7 @@ impl Stylist {
// nsXBLPrototypeResources::LoadResources() loads Chrome XBL style // nsXBLPrototypeResources::LoadResources() loads Chrome XBL style
// sheets under eAuthorSheetFeatures level. // sheets under eAuthorSheetFeatures level.
if let Some(map) = self.cascade_data.author.borrow_for_pseudo(pseudo_element) { if let Some(map) = self.cascade_data.per_origin.author.borrow_for_pseudo(pseudo_element) {
map.get_all_matching_rules(element, map.get_all_matching_rules(element,
&rule_hash_target, &rule_hash_target,
applicable_declarations, applicable_declarations,
@ -1081,7 +1216,7 @@ impl Stylist {
let only_default_rules = rule_inclusion == RuleInclusion::DefaultOnly; let only_default_rules = rule_inclusion == RuleInclusion::DefaultOnly;
// Step 1: Normal user-agent rules. // Step 1: Normal user-agent rules.
if let Some(map) = self.cascade_data.user_agent.borrow_for_pseudo(pseudo_element) { if let Some(map) = self.cascade_data.per_origin.user_agent.borrow_for_pseudo(pseudo_element) {
map.get_all_matching_rules(element, map.get_all_matching_rules(element,
&rule_hash_target, &rule_hash_target,
applicable_declarations, applicable_declarations,
@ -1117,7 +1252,7 @@ impl Stylist {
// Which may be more what you would probably expect. // Which may be more what you would probably expect.
if rule_hash_target.matches_user_and_author_rules() { if rule_hash_target.matches_user_and_author_rules() {
// Step 3a: User normal rules. // Step 3a: User normal rules.
if let Some(map) = self.cascade_data.user.borrow_for_pseudo(pseudo_element) { if let Some(map) = self.cascade_data.per_origin.user.borrow_for_pseudo(pseudo_element) {
map.get_all_matching_rules(element, map.get_all_matching_rules(element,
&rule_hash_target, &rule_hash_target,
applicable_declarations, applicable_declarations,
@ -1140,7 +1275,7 @@ impl Stylist {
// See nsStyleSet::FileRules(). // See nsStyleSet::FileRules().
if !cut_off_inheritance { if !cut_off_inheritance {
// Step 3c: Author normal rules. // Step 3c: Author normal rules.
if let Some(map) = self.cascade_data.author.borrow_for_pseudo(pseudo_element) { if let Some(map) = self.cascade_data.per_origin.author.borrow_for_pseudo(pseudo_element) {
map.get_all_matching_rules(element, map.get_all_matching_rules(element,
&rule_hash_target, &rule_hash_target,
applicable_declarations, applicable_declarations,

View file

@ -215,7 +215,7 @@ fn traverse_subtree(element: GeckoElement,
} }
let per_doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow(); let per_doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow();
debug_assert!(!per_doc_data.stylesheets.has_changed()); debug_assert!(!per_doc_data.stylist.stylesheets_have_changed());
let global_style_data = &*GLOBAL_STYLE_DATA; let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read(); let guard = global_style_data.shared_lock.read();
@ -892,7 +892,7 @@ pub extern "C" fn Servo_StyleSet_AppendStyleSheet(
let data = &mut *data; let data = &mut *data;
let guard = global_style_data.shared_lock.read(); let guard = global_style_data.shared_lock.read();
let sheet = unsafe { GeckoStyleSheet::new(sheet) }; let sheet = unsafe { GeckoStyleSheet::new(sheet) };
data.stylesheets.append_stylesheet(Some(data.stylist.device()), sheet, &guard); data.stylist.append_stylesheet(sheet, &guard);
} }
#[no_mangle] #[no_mangle]
@ -920,10 +920,7 @@ pub extern "C" fn Servo_StyleSet_MediumFeaturesChanged(
} }
data.stylist.device_mut().reset_computed_values(); data.stylist.device_mut().reset_computed_values();
let origins_in_which_rules_changed = let origins_in_which_rules_changed =
data.stylist.media_features_change_changed_style( data.stylist.media_features_change_changed_style(&guard);
data.stylesheets.iter(),
&guard,
);
// We'd like to return `OriginFlags` here, but bindgen bitfield enums don't // We'd like to return `OriginFlags` here, but bindgen bitfield enums don't
// work as return values with the Linux 32-bit ABI at the moment because // work as return values with the Linux 32-bit ABI at the moment because
@ -941,7 +938,7 @@ pub extern "C" fn Servo_StyleSet_PrependStyleSheet(
let data = &mut *data; let data = &mut *data;
let guard = global_style_data.shared_lock.read(); let guard = global_style_data.shared_lock.read();
let sheet = unsafe { GeckoStyleSheet::new(sheet) }; let sheet = unsafe { GeckoStyleSheet::new(sheet) };
data.stylesheets.prepend_stylesheet(Some(data.stylist.device()), sheet, &guard); data.stylist.prepend_stylesheet(sheet, &guard);
} }
#[no_mangle] #[no_mangle]
@ -955,8 +952,7 @@ pub extern "C" fn Servo_StyleSet_InsertStyleSheetBefore(
let data = &mut *data; let data = &mut *data;
let guard = global_style_data.shared_lock.read(); let guard = global_style_data.shared_lock.read();
let sheet = unsafe { GeckoStyleSheet::new(sheet) }; let sheet = unsafe { GeckoStyleSheet::new(sheet) };
data.stylesheets.insert_stylesheet_before( data.stylist.insert_stylesheet_before(
Some(data.stylist.device()),
sheet, sheet,
unsafe { GeckoStyleSheet::new(before_sheet) }, unsafe { GeckoStyleSheet::new(before_sheet) },
&guard, &guard,
@ -973,7 +969,7 @@ pub extern "C" fn Servo_StyleSet_RemoveStyleSheet(
let data = &mut *data; let data = &mut *data;
let guard = global_style_data.shared_lock.read(); let guard = global_style_data.shared_lock.read();
let sheet = unsafe { GeckoStyleSheet::new(sheet) }; let sheet = unsafe { GeckoStyleSheet::new(sheet) };
data.stylesheets.remove_stylesheet(Some(data.stylist.device()), sheet, &guard); data.stylist.remove_stylesheet(sheet, &guard);
} }
#[no_mangle] #[no_mangle]
@ -995,8 +991,8 @@ pub extern "C" fn Servo_StyleSet_NoteStyleSheetsChanged(
changed_origins: OriginFlags, changed_origins: OriginFlags,
) { ) {
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
data.stylesheets.force_dirty(OriginSet::from(changed_origins)); data.stylist.force_stylesheet_origins_dirty(OriginSet::from(changed_origins));
data.stylesheets.set_author_style_disabled(author_style_disabled); data.stylist.set_author_style_disabled(author_style_disabled);
} }
#[no_mangle] #[no_mangle]