mirror of
https://github.com/servo/servo.git
synced 2025-06-23 08:34:42 +01:00
Auto merge of #18060 - heycam:single-origin, r=emilio
style: Don't rebuild cascade origins that haven't changed. From https://bugzilla.mozilla.org/show_bug.cgi?id=1382925. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/18060) <!-- Reviewable:end -->
This commit is contained in:
commit
b1d7b6bfcf
13 changed files with 1364 additions and 899 deletions
|
@ -137,7 +137,7 @@ 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::stylesheets::{Origin, Stylesheet, StylesheetInDocument, UserAgentStylesheets};
|
use style::stylesheets::{Origin, Stylesheet, StylesheetInDocument, UserAgentStylesheets};
|
||||||
use style::stylist::{ExtraStyleData, Stylist};
|
use style::stylist::Stylist;
|
||||||
use style::thread_state;
|
use style::thread_state;
|
||||||
use style::timer::Timer;
|
use style::timer::Timer;
|
||||||
use style::traversal::{DomTraversal, TraversalDriver};
|
use style::traversal::{DomTraversal, TraversalDriver};
|
||||||
|
@ -1206,7 +1206,7 @@ impl LayoutThread {
|
||||||
author: &author_guard,
|
author: &author_guard,
|
||||||
ua_or_user: &ua_or_user_guard,
|
ua_or_user: &ua_or_user_guard,
|
||||||
};
|
};
|
||||||
let mut extra_data = ExtraStyleData;
|
let mut extra_data = Default::default();
|
||||||
let needs_dirtying = self.stylist.update(
|
let needs_dirtying = self.stylist.update(
|
||||||
StylesheetIterator(data.document_stylesheets.iter()),
|
StylesheetIterator(data.document_stylesheets.iter()),
|
||||||
&guards,
|
&guards,
|
||||||
|
|
|
@ -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::{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.
|
||||||
|
@ -118,7 +118,7 @@ pub struct PerDocumentStyleDataImpl {
|
||||||
pub stylesheets: StylesheetSet<GeckoStyleSheet>,
|
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: ExtraStyleData,
|
pub extra_style_data: PerOrigin<ExtraStyleData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The data itself is an `AtomicRefCell`, which guarantees the proper semantics
|
/// The data itself is an `AtomicRefCell`, which guarantees the proper semantics
|
||||||
|
@ -163,7 +163,7 @@ impl PerDocumentStyleDataImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
let author_style_disabled = self.stylesheets.author_style_disabled();
|
let author_style_disabled = self.stylesheets.author_style_disabled();
|
||||||
self.stylist.clear();
|
|
||||||
let iter = self.stylesheets.flush(document_element);
|
let iter = self.stylesheets.flush(document_element);
|
||||||
self.stylist.rebuild(
|
self.stylist.rebuild(
|
||||||
iter,
|
iter,
|
||||||
|
@ -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() }
|
||||||
|
|
|
@ -15,6 +15,7 @@ use gecko_bindings::structs::mozilla::css::ImageValue;
|
||||||
use gecko_bindings::structs::mozilla::css::URLValue;
|
use gecko_bindings::structs::mozilla::css::URLValue;
|
||||||
use gecko_bindings::structs::mozilla::css::URLValueData;
|
use gecko_bindings::structs::mozilla::css::URLValueData;
|
||||||
use gecko_bindings::structs::mozilla::MallocSizeOf;
|
use gecko_bindings::structs::mozilla::MallocSizeOf;
|
||||||
|
use gecko_bindings::structs::mozilla::OriginFlags;
|
||||||
use gecko_bindings::structs::mozilla::Side;
|
use gecko_bindings::structs::mozilla::Side;
|
||||||
use gecko_bindings::structs::mozilla::UniquePtr;
|
use gecko_bindings::structs::mozilla::UniquePtr;
|
||||||
use gecko_bindings::structs::nsIContent;
|
use gecko_bindings::structs::nsIContent;
|
||||||
|
@ -1956,6 +1957,11 @@ extern "C" {
|
||||||
RawServoStyleSheetContentsBorrowed)
|
RawServoStyleSheetContentsBorrowed)
|
||||||
-> usize;
|
-> usize;
|
||||||
}
|
}
|
||||||
|
extern "C" {
|
||||||
|
pub fn Servo_StyleSheet_GetOrigin(sheet:
|
||||||
|
RawServoStyleSheetContentsBorrowed)
|
||||||
|
-> OriginFlags;
|
||||||
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Servo_StyleSet_Init(pres_context: RawGeckoPresContextOwned)
|
pub fn Servo_StyleSet_Init(pres_context: RawGeckoPresContextOwned)
|
||||||
-> RawServoStyleSetOwned;
|
-> RawServoStyleSetOwned;
|
||||||
|
@ -2006,7 +2012,9 @@ extern "C" {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Servo_StyleSet_NoteStyleSheetsChanged(set:
|
pub fn Servo_StyleSet_NoteStyleSheetsChanged(set:
|
||||||
RawServoStyleSetBorrowed,
|
RawServoStyleSetBorrowed,
|
||||||
author_style_disabled: bool);
|
author_style_disabled: bool,
|
||||||
|
changed_origins:
|
||||||
|
OriginFlags);
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn Servo_StyleSet_GetKeyframesForName(set: RawServoStyleSetBorrowed,
|
pub fn Servo_StyleSet_GetKeyframesForName(set: RawServoStyleSetBorrowed,
|
||||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -13,6 +13,7 @@ mod ns_style_auto_array;
|
||||||
pub mod ns_style_coord;
|
pub mod ns_style_coord;
|
||||||
mod ns_t_array;
|
mod ns_t_array;
|
||||||
mod ns_timing_function;
|
mod ns_timing_function;
|
||||||
|
mod origin_flags;
|
||||||
pub mod ownership;
|
pub mod ownership;
|
||||||
pub mod refptr;
|
pub mod refptr;
|
||||||
mod style_complex_color;
|
mod style_complex_color;
|
||||||
|
|
50
components/style/gecko_bindings/sugar/origin_flags.rs
Normal file
50
components/style/gecko_bindings/sugar/origin_flags.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
//! Helper to iterate over `OriginFlags` bits.
|
||||||
|
|
||||||
|
use gecko_bindings::structs::OriginFlags;
|
||||||
|
use gecko_bindings::structs::OriginFlags_Author;
|
||||||
|
use gecko_bindings::structs::OriginFlags_User;
|
||||||
|
use gecko_bindings::structs::OriginFlags_UserAgent;
|
||||||
|
use stylesheets::Origin;
|
||||||
|
|
||||||
|
impl OriginFlags {
|
||||||
|
/// Returns an iterator over the origins present in the `OriginFlags`,
|
||||||
|
/// in order from highest priority (author) to lower (user agent).
|
||||||
|
pub fn iter(self) -> OriginFlagsIter {
|
||||||
|
OriginFlagsIter {
|
||||||
|
origin_flags: self,
|
||||||
|
cur: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterates over the origins present in an `OriginFlags`, in order from
|
||||||
|
/// highest priority (author) to lower (user agent).
|
||||||
|
pub struct OriginFlagsIter {
|
||||||
|
origin_flags: OriginFlags,
|
||||||
|
cur: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for OriginFlagsIter {
|
||||||
|
type Item = Origin;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Origin> {
|
||||||
|
loop {
|
||||||
|
let (bit, origin) = match self.cur {
|
||||||
|
0 => (OriginFlags_Author, Origin::Author),
|
||||||
|
1 => (OriginFlags_User, Origin::User),
|
||||||
|
2 => (OriginFlags_UserAgent, Origin::UserAgent),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.cur += 1;
|
||||||
|
|
||||||
|
if (self.origin_flags & bit).0 != 0 {
|
||||||
|
return Some(origin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -52,6 +52,7 @@ impl ToMediaListKey for MediaRule {}
|
||||||
|
|
||||||
/// A struct that holds the result of a media query evaluation pass for the
|
/// A struct that holds the result of a media query evaluation pass for the
|
||||||
/// media queries that evaluated successfully.
|
/// media queries that evaluated successfully.
|
||||||
|
#[derive(Debug)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct EffectiveMediaQueryResults {
|
pub struct EffectiveMediaQueryResults {
|
||||||
/// The set of media lists that matched last time.
|
/// The set of media lists that matched last time.
|
||||||
|
|
|
@ -8,7 +8,7 @@ use dom::TElement;
|
||||||
use invalidation::stylesheets::StylesheetInvalidationSet;
|
use invalidation::stylesheets::StylesheetInvalidationSet;
|
||||||
use shared_lock::SharedRwLockReadGuard;
|
use shared_lock::SharedRwLockReadGuard;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use stylesheets::StylesheetInDocument;
|
use stylesheets::{Origin, PerOrigin, StylesheetInDocument};
|
||||||
use stylist::Stylist;
|
use stylist::Stylist;
|
||||||
|
|
||||||
/// Entry for a StylesheetSet. We don't bother creating a constructor, because
|
/// Entry for a StylesheetSet. We don't bother creating a constructor, because
|
||||||
|
@ -49,14 +49,11 @@ where
|
||||||
/// include recursive `@import` rules.
|
/// include recursive `@import` rules.
|
||||||
entries: Vec<StylesheetSetEntry<S>>,
|
entries: Vec<StylesheetSetEntry<S>>,
|
||||||
|
|
||||||
/// Whether the entries list above has changed since the last restyle.
|
/// Per-origin stylesheet invalidation data.
|
||||||
dirty: bool,
|
invalidation_data: PerOrigin<InvalidationData>,
|
||||||
|
|
||||||
/// Has author style been disabled?
|
/// Has author style been disabled?
|
||||||
author_style_disabled: bool,
|
author_style_disabled: bool,
|
||||||
|
|
||||||
/// The style invalidations that we still haven't processed.
|
|
||||||
invalidations: StylesheetInvalidationSet,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> StylesheetSet<S>
|
impl<S> StylesheetSet<S>
|
||||||
|
@ -67,9 +64,8 @@ where
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
StylesheetSet {
|
StylesheetSet {
|
||||||
entries: vec![],
|
entries: vec![],
|
||||||
dirty: false,
|
invalidation_data: Default::default(),
|
||||||
author_style_disabled: false,
|
author_style_disabled: false,
|
||||||
invalidations: StylesheetInvalidationSet::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +79,18 @@ where
|
||||||
self.entries.retain(|entry| entry.sheet != *sheet);
|
self.entries.retain(|entry| entry.sheet != *sheet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn collect_invalidations_for(
|
||||||
|
&mut self,
|
||||||
|
stylist: &Stylist,
|
||||||
|
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);
|
||||||
|
data.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Appends a new stylesheet to the current set.
|
/// Appends a new stylesheet to the current set.
|
||||||
pub fn append_stylesheet(
|
pub fn append_stylesheet(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -92,12 +100,7 @@ where
|
||||||
) {
|
) {
|
||||||
debug!("StylesheetSet::append_stylesheet");
|
debug!("StylesheetSet::append_stylesheet");
|
||||||
self.remove_stylesheet_if_present(&sheet);
|
self.remove_stylesheet_if_present(&sheet);
|
||||||
self.invalidations.collect_invalidations_for(
|
self.collect_invalidations_for(stylist, &sheet, guard);
|
||||||
stylist,
|
|
||||||
&sheet,
|
|
||||||
guard
|
|
||||||
);
|
|
||||||
self.dirty = true;
|
|
||||||
self.entries.push(StylesheetSetEntry { sheet });
|
self.entries.push(StylesheetSetEntry { sheet });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,13 +113,8 @@ where
|
||||||
) {
|
) {
|
||||||
debug!("StylesheetSet::prepend_stylesheet");
|
debug!("StylesheetSet::prepend_stylesheet");
|
||||||
self.remove_stylesheet_if_present(&sheet);
|
self.remove_stylesheet_if_present(&sheet);
|
||||||
self.invalidations.collect_invalidations_for(
|
self.collect_invalidations_for(stylist, &sheet, guard);
|
||||||
stylist,
|
|
||||||
&sheet,
|
|
||||||
guard
|
|
||||||
);
|
|
||||||
self.entries.insert(0, StylesheetSetEntry { sheet });
|
self.entries.insert(0, StylesheetSetEntry { sheet });
|
||||||
self.dirty = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a given stylesheet before another stylesheet in the document.
|
/// Insert a given stylesheet before another stylesheet in the document.
|
||||||
|
@ -132,13 +130,8 @@ where
|
||||||
let index = self.entries.iter().position(|entry| {
|
let index = self.entries.iter().position(|entry| {
|
||||||
entry.sheet == before_sheet
|
entry.sheet == before_sheet
|
||||||
}).expect("`before_sheet` stylesheet not found");
|
}).expect("`before_sheet` stylesheet not found");
|
||||||
self.invalidations.collect_invalidations_for(
|
self.collect_invalidations_for(stylist, &sheet, guard);
|
||||||
stylist,
|
|
||||||
&sheet,
|
|
||||||
guard
|
|
||||||
);
|
|
||||||
self.entries.insert(index, StylesheetSetEntry { sheet });
|
self.entries.insert(index, StylesheetSetEntry { sheet });
|
||||||
self.dirty = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove a given stylesheet from the set.
|
/// Remove a given stylesheet from the set.
|
||||||
|
@ -150,12 +143,7 @@ where
|
||||||
) {
|
) {
|
||||||
debug!("StylesheetSet::remove_stylesheet");
|
debug!("StylesheetSet::remove_stylesheet");
|
||||||
self.remove_stylesheet_if_present(&sheet);
|
self.remove_stylesheet_if_present(&sheet);
|
||||||
self.dirty = true;
|
self.collect_invalidations_for(stylist, &sheet, guard);
|
||||||
self.invalidations.collect_invalidations_for(
|
|
||||||
stylist,
|
|
||||||
&sheet,
|
|
||||||
guard
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Notes that the author style has been disabled for this document.
|
/// Notes that the author style has been disabled for this document.
|
||||||
|
@ -165,29 +153,35 @@ where
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.author_style_disabled = disabled;
|
self.author_style_disabled = disabled;
|
||||||
self.dirty = true;
|
self.invalidation_data.author.invalidations.invalidate_fully();
|
||||||
self.invalidations.invalidate_fully();
|
self.invalidation_data.author.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether the given set has changed from the last flush.
|
/// Returns whether the given set has changed from the last flush.
|
||||||
pub fn has_changed(&self) -> bool {
|
pub fn has_changed(&self) -> bool {
|
||||||
self.dirty
|
self.invalidation_data
|
||||||
|
.iter_origins()
|
||||||
|
.any(|(d, _)| d.dirty)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flush the current set, unmarking it as dirty, and returns an iterator
|
/// Flush the current set, unmarking it as dirty, and returns an iterator
|
||||||
/// over the new stylesheet list.
|
/// over the new stylesheet list.
|
||||||
pub fn flush<E>(
|
pub fn flush<E>(
|
||||||
&mut self,
|
&mut self,
|
||||||
document_element: Option<E>
|
document_element: Option<E>,
|
||||||
) -> StylesheetIterator<S>
|
) -> StylesheetIterator<S>
|
||||||
where
|
where
|
||||||
E: TElement,
|
E: TElement,
|
||||||
{
|
{
|
||||||
debug!("StylesheetSet::flush");
|
debug!("StylesheetSet::flush");
|
||||||
debug_assert!(self.dirty);
|
debug_assert!(self.has_changed());
|
||||||
|
|
||||||
self.dirty = false;
|
for (data, _) in self.invalidation_data.iter_mut_origins() {
|
||||||
self.invalidations.flush(document_element);
|
if data.dirty {
|
||||||
|
data.invalidations.flush(document_element);
|
||||||
|
data.dirty = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.iter()
|
self.iter()
|
||||||
}
|
}
|
||||||
|
@ -202,7 +196,36 @@ where
|
||||||
///
|
///
|
||||||
/// FIXME(emilio): Make this more granular.
|
/// FIXME(emilio): Make this more granular.
|
||||||
pub fn force_dirty(&mut self) {
|
pub fn force_dirty(&mut self) {
|
||||||
self.dirty = true;
|
for (data, _) in self.invalidation_data.iter_mut_origins() {
|
||||||
self.invalidations.invalidate_fully();
|
data.invalidations.invalidate_fully();
|
||||||
|
data.dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark the stylesheets for the specified origin as dirty, because
|
||||||
|
/// something external may have invalidated it.
|
||||||
|
pub fn force_dirty_origin(&mut self, origin: &Origin) {
|
||||||
|
let data = self.invalidation_data.borrow_mut_for_origin(origin);
|
||||||
|
data.invalidations.invalidate_fully();
|
||||||
|
data.dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InvalidationData {
|
||||||
|
/// The stylesheet invalidations for this origin that we still haven't
|
||||||
|
/// processed.
|
||||||
|
invalidations: StylesheetInvalidationSet,
|
||||||
|
|
||||||
|
/// Whether the sheets for this origin in the `StylesheetSet`'s entry list
|
||||||
|
/// has changed since the last restyle.
|
||||||
|
dirty: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for InvalidationData {
|
||||||
|
fn default() -> Self {
|
||||||
|
InvalidationData {
|
||||||
|
invalidations: StylesheetInvalidationSet::new(),
|
||||||
|
dirty: false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ mod loader;
|
||||||
mod media_rule;
|
mod media_rule;
|
||||||
mod memory;
|
mod memory;
|
||||||
mod namespace_rule;
|
mod namespace_rule;
|
||||||
|
mod origin;
|
||||||
mod page_rule;
|
mod page_rule;
|
||||||
mod rule_list;
|
mod rule_list;
|
||||||
mod rule_parser;
|
mod rule_parser;
|
||||||
|
@ -43,6 +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, PerOrigin, PerOriginClear};
|
||||||
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};
|
||||||
|
@ -82,22 +84,6 @@ impl UrlExtraData {
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
impl Eq for UrlExtraData {}
|
impl Eq for UrlExtraData {}
|
||||||
|
|
||||||
/// Each style rule has an origin, which determines where it enters the cascade.
|
|
||||||
///
|
|
||||||
/// http://dev.w3.org/csswg/css-cascade/#cascading-origins
|
|
||||||
#[derive(Clone, PartialEq, Eq, Copy, Debug)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
pub enum Origin {
|
|
||||||
/// http://dev.w3.org/csswg/css-cascade/#cascade-origin-ua
|
|
||||||
UserAgent,
|
|
||||||
|
|
||||||
/// http://dev.w3.org/csswg/css-cascade/#cascade-origin-author
|
|
||||||
Author,
|
|
||||||
|
|
||||||
/// http://dev.w3.org/csswg/css-cascade/#cascade-origin-user
|
|
||||||
User,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A CSS rule.
|
/// A CSS rule.
|
||||||
///
|
///
|
||||||
/// TODO(emilio): Lots of spec links should be around.
|
/// TODO(emilio): Lots of spec links should be around.
|
||||||
|
|
146
components/style/stylesheets/origin.rs
Normal file
146
components/style/stylesheets/origin.rs
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
///! [CSS cascade origins](https://drafts.csswg.org/css-cascade/#cascading-origins).
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::mem::transmute;
|
||||||
|
|
||||||
|
/// Each style rule has an origin, which determines where it enters the cascade.
|
||||||
|
///
|
||||||
|
/// https://drafts.csswg.org/css-cascade/#cascading-origins
|
||||||
|
#[derive(Clone, PartialEq, Eq, Copy, Debug)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
pub enum Origin {
|
||||||
|
/// https://drafts.csswg.org/css-cascade/#cascade-origin-us
|
||||||
|
UserAgent,
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-cascade/#cascade-origin-user
|
||||||
|
User,
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-cascade/#cascade-origin-author
|
||||||
|
Author,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An object that stores a `T` for each origin of the CSS cascade.
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct PerOrigin<T> {
|
||||||
|
/// Data for `Origin::UserAgent`.
|
||||||
|
pub user_agent: T,
|
||||||
|
|
||||||
|
/// Data for `Origin::User`.
|
||||||
|
pub user: T,
|
||||||
|
|
||||||
|
/// Data for `Origin::Author`.
|
||||||
|
pub author: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> PerOrigin<T> {
|
||||||
|
/// Returns a reference to the per-origin data for the specified origin.
|
||||||
|
#[inline]
|
||||||
|
pub fn borrow_for_origin(&self, origin: &Origin) -> &T {
|
||||||
|
match *origin {
|
||||||
|
Origin::UserAgent => &self.user_agent,
|
||||||
|
Origin::User => &self.user,
|
||||||
|
Origin::Author => &self.author,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the per-origin data for the specified
|
||||||
|
/// origin.
|
||||||
|
#[inline]
|
||||||
|
pub fn borrow_mut_for_origin(&mut self, origin: &Origin) -> &mut T {
|
||||||
|
match *origin {
|
||||||
|
Origin::UserAgent => &mut self.user_agent,
|
||||||
|
Origin::User => &mut self.user,
|
||||||
|
Origin::Author => &mut self.author,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterates over references to per-origin extra style data, from highest
|
||||||
|
/// level (author) to lowest (user agent).
|
||||||
|
pub fn iter_origins(&self) -> PerOriginIter<T> {
|
||||||
|
PerOriginIter {
|
||||||
|
data: &self,
|
||||||
|
cur: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterates over mutable references to per-origin extra style data, from
|
||||||
|
/// highest level (author) to lowest (user agent).
|
||||||
|
pub fn iter_mut_origins(&mut self) -> PerOriginIterMut<T> {
|
||||||
|
PerOriginIterMut {
|
||||||
|
data: self,
|
||||||
|
cur: 0,
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An object that can be cleared.
|
||||||
|
pub trait PerOriginClear {
|
||||||
|
/// Clears the object.
|
||||||
|
fn clear(&mut self);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> PerOriginClear for PerOrigin<T> where T: PerOriginClear {
|
||||||
|
fn clear(&mut self) {
|
||||||
|
self.user_agent.clear();
|
||||||
|
self.user.clear();
|
||||||
|
self.author.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator over `PerOrigin<T>`, from highest level (author) to lowest
|
||||||
|
/// (user agent).
|
||||||
|
///
|
||||||
|
/// We rely on this specific order for correctly looking up @font-face,
|
||||||
|
/// @counter-style and @keyframes rules.
|
||||||
|
pub struct PerOriginIter<'a, T: 'a> {
|
||||||
|
data: &'a PerOrigin<T>,
|
||||||
|
cur: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Iterator for PerOriginIter<'a, T> where T: 'a {
|
||||||
|
type Item = (&'a T, Origin);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let result = match self.cur {
|
||||||
|
0 => (&self.data.author, Origin::Author),
|
||||||
|
1 => (&self.data.user, Origin::User),
|
||||||
|
2 => (&self.data.user_agent, Origin::UserAgent),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
self.cur += 1;
|
||||||
|
Some(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Like `PerOriginIter<T>`, but iterates over mutable references to the
|
||||||
|
/// per-origin data.
|
||||||
|
///
|
||||||
|
/// We must use unsafe code here since it's not possible for the borrow
|
||||||
|
/// checker to know that we are safely returning a different reference
|
||||||
|
/// each time from `next()`.
|
||||||
|
pub struct PerOriginIterMut<'a, T: 'a> {
|
||||||
|
data: *mut PerOrigin<T>,
|
||||||
|
cur: usize,
|
||||||
|
_marker: PhantomData<&'a mut PerOrigin<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Iterator for PerOriginIterMut<'a, T> where T: 'a {
|
||||||
|
type Item = (&'a mut T, Origin);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let result = match self.cur {
|
||||||
|
0 => (unsafe { transmute(&mut (*self.data).author) }, Origin::Author),
|
||||||
|
1 => (unsafe { transmute(&mut (*self.data).user) }, Origin::User),
|
||||||
|
2 => (unsafe { transmute(&mut (*self.data).user_agent) }, Origin::UserAgent),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
self.cur += 1;
|
||||||
|
Some(result)
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,7 +40,8 @@ use style_traits::viewport::ViewportConstraints;
|
||||||
#[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, UserAgentStylesheets};
|
use stylesheets::{StylesheetInDocument, Origin, PerOrigin, PerOriginClear};
|
||||||
|
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};
|
||||||
use thread_state;
|
use thread_state;
|
||||||
|
@ -73,9 +74,6 @@ pub struct Stylist {
|
||||||
/// Viewport constraints based on the current device.
|
/// Viewport constraints based on the current device.
|
||||||
viewport_constraints: Option<ViewportConstraints>,
|
viewport_constraints: Option<ViewportConstraints>,
|
||||||
|
|
||||||
/// Effective media query results cached from the last rebuild.
|
|
||||||
effective_media_query_results: EffectiveMediaQueryResults,
|
|
||||||
|
|
||||||
/// If true, the quirks-mode stylesheet is applied.
|
/// If true, the quirks-mode stylesheet is applied.
|
||||||
#[cfg_attr(feature = "servo", ignore_heap_size_of = "defined in selectors")]
|
#[cfg_attr(feature = "servo", ignore_heap_size_of = "defined in selectors")]
|
||||||
quirks_mode: QuirksMode,
|
quirks_mode: QuirksMode,
|
||||||
|
@ -83,14 +81,10 @@ pub struct Stylist {
|
||||||
/// If true, the device has changed, and the stylist needs to be updated.
|
/// If true, the device has changed, and the stylist needs to be updated.
|
||||||
is_device_dirty: bool,
|
is_device_dirty: bool,
|
||||||
|
|
||||||
/// If true, the stylist is in a cleared state (e.g. just-constructed, or
|
|
||||||
/// had clear() called on it with no following rebuild()).
|
|
||||||
is_cleared: bool,
|
|
||||||
|
|
||||||
/// Selector maps for all of the style sheets in the stylist, after
|
/// Selector maps for all of the style sheets in the stylist, after
|
||||||
/// evalutaing media rules against the current device, split out per
|
/// evalutaing media rules against the current device, split out per
|
||||||
/// cascade level.
|
/// cascade level.
|
||||||
cascade_data: CascadeData,
|
cascade_data: PerOrigin<CascadeData>,
|
||||||
|
|
||||||
/// The rule tree, that stores the results of selector matching.
|
/// The rule tree, that stores the results of selector matching.
|
||||||
rule_tree: RuleTree,
|
rule_tree: RuleTree,
|
||||||
|
@ -102,98 +96,10 @@ pub struct Stylist {
|
||||||
/// 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>>,
|
||||||
|
|
||||||
/// A monotonically increasing counter to represent the order on which a
|
|
||||||
/// style rule appears in a stylesheet, needed to sort them by source order.
|
|
||||||
rules_source_order: u32,
|
|
||||||
|
|
||||||
/// The total number of times the stylist has been rebuilt.
|
/// The total number of times the stylist has been rebuilt.
|
||||||
num_rebuilds: usize,
|
num_rebuilds: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This struct holds data which users of Stylist may want to extract
|
|
||||||
/// from stylesheets which can be done at the same time as updating.
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct ExtraStyleData {
|
|
||||||
/// Extra data from user agent stylesheets
|
|
||||||
user_agent: PerOriginExtraStyleData,
|
|
||||||
/// Extra data from author stylesheets
|
|
||||||
author: PerOriginExtraStyleData,
|
|
||||||
/// Extra data from user stylesheets
|
|
||||||
user: PerOriginExtraStyleData,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This struct holds data which users of Stylist may want to extract
|
|
||||||
/// from stylesheets which can be done at the same time as updating.
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct PerOriginExtraStyleData {
|
|
||||||
/// A list of effective font-face rules and their origin.
|
|
||||||
pub font_faces: Vec<Arc<Locked<FontFaceRule>>>,
|
|
||||||
/// A map of effective counter-style rules.
|
|
||||||
pub counter_styles: PrecomputedHashMap<Atom, Arc<Locked<CounterStyleRule>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
impl ExtraStyleData {
|
|
||||||
/// Clear the internal data.
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.user_agent.clear();
|
|
||||||
self.author.clear();
|
|
||||||
self.user.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a reference to the per-origin extra style data for
|
|
||||||
/// the specified origin.
|
|
||||||
#[inline]
|
|
||||||
pub fn borrow_mut_for_origin(&mut self, origin: &Origin) -> &mut PerOriginExtraStyleData {
|
|
||||||
match *origin {
|
|
||||||
Origin::UserAgent => &mut self.user_agent,
|
|
||||||
Origin::Author => &mut self.author,
|
|
||||||
Origin::User => &mut self.user,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterates over the per-origin extra style data, from highest level (user)
|
|
||||||
/// to lowest (user agent).
|
|
||||||
pub fn iter_origins(&self) -> ExtraStyleDataIter {
|
|
||||||
ExtraStyleDataIter {
|
|
||||||
extra_style_data: &self,
|
|
||||||
cur: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
impl PerOriginExtraStyleData {
|
|
||||||
/// Clears the stored @font-face and @counter-style rules.
|
|
||||||
fn clear(&mut self) {
|
|
||||||
self.font_faces.clear();
|
|
||||||
self.counter_styles.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add the given @font-face rule.
|
|
||||||
fn add_font_face(&mut self, rule: &Arc<Locked<FontFaceRule>>) {
|
|
||||||
self.font_faces.push(rule.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add the given @counter-style rule.
|
|
||||||
fn add_counter_style(&mut self, guard: &SharedRwLockReadGuard,
|
|
||||||
rule: &Arc<Locked<CounterStyleRule>>) {
|
|
||||||
let name = rule.read_with(guard).mName.raw::<nsIAtom>().into();
|
|
||||||
self.counter_styles.insert(name, rule.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[cfg(feature = "servo")]
|
|
||||||
pub struct ExtraStyleData;
|
|
||||||
|
|
||||||
#[cfg(feature = "servo")]
|
|
||||||
impl ExtraStyleData {
|
|
||||||
fn clear(&mut self) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// What cascade levels to include when styling elements.
|
/// What cascade levels to include when styling elements.
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
pub enum RuleInclusion {
|
pub enum RuleInclusion {
|
||||||
|
@ -225,13 +131,10 @@ impl Stylist {
|
||||||
viewport_constraints: None,
|
viewport_constraints: None,
|
||||||
device: device,
|
device: device,
|
||||||
is_device_dirty: true,
|
is_device_dirty: true,
|
||||||
is_cleared: true,
|
|
||||||
quirks_mode: quirks_mode,
|
quirks_mode: quirks_mode,
|
||||||
effective_media_query_results: EffectiveMediaQueryResults::new(),
|
|
||||||
|
|
||||||
cascade_data: CascadeData::new(),
|
cascade_data: Default::default(),
|
||||||
precomputed_pseudo_element_decls: PerPseudoElementMap::default(),
|
precomputed_pseudo_element_decls: PerPseudoElementMap::default(),
|
||||||
rules_source_order: 0,
|
|
||||||
rule_tree: RuleTree::new(),
|
rule_tree: RuleTree::new(),
|
||||||
num_rebuilds: 0,
|
num_rebuilds: 0,
|
||||||
}
|
}
|
||||||
|
@ -241,12 +144,12 @@ impl Stylist {
|
||||||
|
|
||||||
/// Returns the number of selectors.
|
/// Returns the number of selectors.
|
||||||
pub fn num_selectors(&self) -> usize {
|
pub fn num_selectors(&self) -> usize {
|
||||||
self.cascade_data.iter_origins().map(|d| d.num_selectors).sum()
|
self.cascade_data.iter_origins().map(|(d, _)| d.num_selectors).sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of declarations.
|
/// Returns the number of declarations.
|
||||||
pub fn num_declarations(&self) -> usize {
|
pub fn num_declarations(&self) -> usize {
|
||||||
self.cascade_data.iter_origins().map(|d| d.num_declarations).sum()
|
self.cascade_data.iter_origins().map(|(d, _)| d.num_declarations).sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of times the stylist has been rebuilt.
|
/// Returns the number of times the stylist has been rebuilt.
|
||||||
|
@ -257,13 +160,13 @@ impl Stylist {
|
||||||
/// Returns the number of revalidation_selectors.
|
/// Returns the number of revalidation_selectors.
|
||||||
pub fn num_revalidation_selectors(&self) -> usize {
|
pub fn num_revalidation_selectors(&self) -> usize {
|
||||||
self.cascade_data.iter_origins()
|
self.cascade_data.iter_origins()
|
||||||
.map(|d| d.selectors_for_cache_revalidation.len()).sum()
|
.map(|(d, _)| d.selectors_for_cache_revalidation.len()).sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of entries in invalidation maps.
|
/// Returns the number of entries in invalidation maps.
|
||||||
pub fn num_invalidations(&self) -> usize {
|
pub fn num_invalidations(&self) -> usize {
|
||||||
self.cascade_data.iter_origins()
|
self.cascade_data.iter_origins()
|
||||||
.map(|d| d.invalidation_map.len()).sum()
|
.map(|(d, _)| d.invalidation_map.len()).sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invokes `f` with the `InvalidationMap` for each origin.
|
/// Invokes `f` with the `InvalidationMap` for each origin.
|
||||||
|
@ -274,8 +177,8 @@ impl Stylist {
|
||||||
pub fn each_invalidation_map<F>(&self, mut f: F)
|
pub fn each_invalidation_map<F>(&self, mut f: F)
|
||||||
where F: FnMut(&InvalidationMap)
|
where F: FnMut(&InvalidationMap)
|
||||||
{
|
{
|
||||||
for origin_cascade_data in self.cascade_data.iter_origins() {
|
for (data, _) in self.cascade_data.iter_origins() {
|
||||||
f(&origin_cascade_data.invalidation_map)
|
f(&data.invalidation_map)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,32 +189,46 @@ impl Stylist {
|
||||||
/// device: Someone might have set this on us.
|
/// device: Someone might have set this on us.
|
||||||
/// quirks_mode: Again, someone might have set this on us.
|
/// quirks_mode: Again, someone might have set this on us.
|
||||||
/// num_rebuilds: clear() followed by rebuild() should just increment this
|
/// num_rebuilds: clear() followed by rebuild() should just increment this
|
||||||
|
/// rule_tree: So we can re-use rule nodes across rebuilds.
|
||||||
///
|
///
|
||||||
/// We don't just use struct update syntax with Stylist::new(self.device)
|
/// We don't just use struct update syntax with Stylist::new(self.device)
|
||||||
/// beause for some of our members we can clear them instead of creating new
|
/// beause for some of our members we can clear them instead of creating new
|
||||||
/// objects. This does cause unfortunate code duplication with
|
/// objects. This does cause unfortunate code duplication with
|
||||||
/// Stylist::new.
|
/// Stylist::new.
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
if self.is_cleared {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.is_cleared = true;
|
|
||||||
|
|
||||||
self.effective_media_query_results.clear();
|
|
||||||
self.viewport_constraints = None;
|
|
||||||
// preserve current device
|
|
||||||
self.is_device_dirty = true;
|
|
||||||
// preserve current quirks_mode value
|
|
||||||
self.cascade_data.clear();
|
self.cascade_data.clear();
|
||||||
self.precomputed_pseudo_element_decls.clear();
|
self.precomputed_pseudo_element_decls.clear();
|
||||||
self.rules_source_order = 0;
|
self.viewport_constraints = None;
|
||||||
// We want to keep rule_tree around across stylist rebuilds.
|
|
||||||
// preserve num_rebuilds value, since it should stay across
|
// XXX(heycam) Why do this, if we are preserving the Device?
|
||||||
// clear()/rebuild() cycles.
|
self.is_device_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// rebuild the stylist for the given document stylesheets, and optionally
|
/// 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
|
||||||
/// 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
|
||||||
|
@ -324,15 +241,35 @@ impl Stylist {
|
||||||
ua_stylesheets: Option<&UserAgentStylesheets>,
|
ua_stylesheets: Option<&UserAgentStylesheets>,
|
||||||
stylesheets_changed: bool,
|
stylesheets_changed: bool,
|
||||||
author_style_disabled: bool,
|
author_style_disabled: bool,
|
||||||
extra_data: &mut ExtraStyleData
|
extra_data: &mut PerOrigin<ExtraStyleData>
|
||||||
) -> bool
|
) -> bool
|
||||||
where
|
where
|
||||||
I: Iterator<Item = &'a S> + Clone,
|
I: Iterator<Item = &'a S> + Clone,
|
||||||
S: StylesheetInDocument + ToMediaListKey + 'static,
|
S: StylesheetInDocument + ToMediaListKey + 'static,
|
||||||
{
|
{
|
||||||
debug_assert!(!self.is_cleared || self.is_device_dirty);
|
debug_assert!(!self.any_origin_cleared() || self.is_device_dirty);
|
||||||
|
|
||||||
self.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) {
|
if !(self.is_device_dirty || stylesheets_changed) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -340,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.
|
||||||
//
|
//
|
||||||
|
@ -362,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 {
|
||||||
|
self.device.account_for_viewport_rule(constraints);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref constraints) = self.viewport_constraints {
|
// XXX(heycam): We should probably just move the `extra_data` to be
|
||||||
self.device.account_for_viewport_rule(constraints);
|
// 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 {
|
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 {
|
||||||
|
@ -404,13 +361,13 @@ impl Stylist {
|
||||||
ua_stylesheets: Option<&UserAgentStylesheets>,
|
ua_stylesheets: Option<&UserAgentStylesheets>,
|
||||||
stylesheets_changed: bool,
|
stylesheets_changed: bool,
|
||||||
author_style_disabled: bool,
|
author_style_disabled: bool,
|
||||||
extra_data: &mut ExtraStyleData
|
extra_data: &mut PerOrigin<ExtraStyleData>
|
||||||
) -> bool
|
) -> bool
|
||||||
where
|
where
|
||||||
I: Iterator<Item = &'a S> + Clone,
|
I: Iterator<Item = &'a S> + Clone,
|
||||||
S: StylesheetInDocument + ToMediaListKey + 'static,
|
S: StylesheetInDocument + ToMediaListKey + 'static,
|
||||||
{
|
{
|
||||||
debug_assert!(!self.is_cleared || self.is_device_dirty);
|
debug_assert!(!self.any_origin_cleared() || self.is_device_dirty);
|
||||||
|
|
||||||
// We have to do a dirtiness check before clearing, because if
|
// We have to do a dirtiness check before clearing, because if
|
||||||
// we're not actually dirty we need to no-op here.
|
// we're not actually dirty we need to no-op here.
|
||||||
|
@ -426,7 +383,7 @@ impl Stylist {
|
||||||
&mut self,
|
&mut self,
|
||||||
stylesheet: &S,
|
stylesheet: &S,
|
||||||
guard: &SharedRwLockReadGuard,
|
guard: &SharedRwLockReadGuard,
|
||||||
_extra_data: &mut ExtraStyleData
|
_extra_data: &mut PerOrigin<ExtraStyleData>
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
S: StylesheetInDocument + ToMediaListKey + 'static,
|
S: StylesheetInDocument + ToMediaListKey + 'static,
|
||||||
|
@ -436,13 +393,14 @@ impl Stylist {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.effective_media_query_results.saw_effective(stylesheet);
|
|
||||||
|
|
||||||
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.cascade_data.borrow_mut_for_origin(&origin);
|
||||||
|
|
||||||
|
origin_cascade_data
|
||||||
|
.effective_media_query_results
|
||||||
|
.saw_effective(stylesheet);
|
||||||
|
|
||||||
for rule in stylesheet.effective_rules(&self.device, guard) {
|
for rule in stylesheet.effective_rules(&self.device, guard) {
|
||||||
match *rule {
|
match *rule {
|
||||||
CssRule::Style(ref locked) => {
|
CssRule::Style(ref locked) => {
|
||||||
|
@ -467,7 +425,7 @@ impl Stylist {
|
||||||
.expect("Unexpected tree pseudo-element?")
|
.expect("Unexpected tree pseudo-element?")
|
||||||
.push(ApplicableDeclarationBlock::new(
|
.push(ApplicableDeclarationBlock::new(
|
||||||
StyleSource::Style(locked.clone()),
|
StyleSource::Style(locked.clone()),
|
||||||
self.rules_source_order,
|
origin_cascade_data.rules_source_order,
|
||||||
CascadeLevel::UANormal,
|
CascadeLevel::UANormal,
|
||||||
selector.specificity()
|
selector.specificity()
|
||||||
));
|
));
|
||||||
|
@ -490,7 +448,7 @@ impl Stylist {
|
||||||
selector.clone(),
|
selector.clone(),
|
||||||
hashes.clone(),
|
hashes.clone(),
|
||||||
locked.clone(),
|
locked.clone(),
|
||||||
self.rules_source_order
|
origin_cascade_data.rules_source_order
|
||||||
);
|
);
|
||||||
|
|
||||||
map.insert(rule, self.quirks_mode);
|
map.insert(rule, self.quirks_mode);
|
||||||
|
@ -515,18 +473,22 @@ impl Stylist {
|
||||||
self.quirks_mode);
|
self.quirks_mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.rules_source_order += 1;
|
origin_cascade_data.rules_source_order += 1;
|
||||||
}
|
}
|
||||||
CssRule::Import(ref lock) => {
|
CssRule::Import(ref lock) => {
|
||||||
let import_rule = lock.read_with(guard);
|
let import_rule = lock.read_with(guard);
|
||||||
self.effective_media_query_results.saw_effective(import_rule);
|
origin_cascade_data
|
||||||
|
.effective_media_query_results
|
||||||
|
.saw_effective(import_rule);
|
||||||
|
|
||||||
// NOTE: effective_rules visits the inner stylesheet if
|
// NOTE: effective_rules visits the inner stylesheet if
|
||||||
// appropriate.
|
// appropriate.
|
||||||
}
|
}
|
||||||
CssRule::Media(ref lock) => {
|
CssRule::Media(ref lock) => {
|
||||||
let media_rule = lock.read_with(guard);
|
let media_rule = lock.read_with(guard);
|
||||||
self.effective_media_query_results.saw_effective(media_rule);
|
origin_cascade_data
|
||||||
|
.effective_media_query_results
|
||||||
|
.saw_effective(media_rule);
|
||||||
}
|
}
|
||||||
CssRule::Keyframes(ref keyframes_rule) => {
|
CssRule::Keyframes(ref keyframes_rule) => {
|
||||||
let keyframes_rule = keyframes_rule.read_with(guard);
|
let keyframes_rule = keyframes_rule.read_with(guard);
|
||||||
|
@ -567,18 +529,18 @@ impl Stylist {
|
||||||
pub fn might_have_attribute_dependency(&self,
|
pub fn might_have_attribute_dependency(&self,
|
||||||
local_name: &LocalName)
|
local_name: &LocalName)
|
||||||
-> bool {
|
-> bool {
|
||||||
if self.is_cleared || self.is_device_dirty {
|
if self.any_origin_cleared() || self.is_device_dirty {
|
||||||
// We can't tell what attributes are in our style rules until
|
// We can't tell what attributes are in our style rules until
|
||||||
// we rebuild.
|
// we rebuild.
|
||||||
true
|
true
|
||||||
} else if *local_name == local_name!("style") {
|
} else if *local_name == local_name!("style") {
|
||||||
self.cascade_data
|
self.cascade_data
|
||||||
.iter_origins()
|
.iter_origins()
|
||||||
.any(|d| d.style_attribute_dependency)
|
.any(|(d, _)| d.style_attribute_dependency)
|
||||||
} else {
|
} else {
|
||||||
self.cascade_data
|
self.cascade_data
|
||||||
.iter_origins()
|
.iter_origins()
|
||||||
.any(|d| {
|
.any(|(d, _)| {
|
||||||
d.attribute_dependencies
|
d.attribute_dependencies
|
||||||
.might_contain_hash(local_name.get_hash())
|
.might_contain_hash(local_name.get_hash())
|
||||||
})
|
})
|
||||||
|
@ -588,9 +550,9 @@ impl Stylist {
|
||||||
/// Returns whether the given ElementState bit might be relied upon by a
|
/// Returns whether the given ElementState bit might be relied upon by a
|
||||||
/// selector of some rule in the stylist.
|
/// selector of some rule in the stylist.
|
||||||
pub fn might_have_state_dependency(&self, state: ElementState) -> bool {
|
pub fn might_have_state_dependency(&self, state: ElementState) -> bool {
|
||||||
if self.is_cleared || self.is_device_dirty {
|
if self.any_origin_cleared() || self.is_device_dirty {
|
||||||
// If self.is_cleared is true, we can't tell what states our style
|
// We can't tell what states our style rules rely on until
|
||||||
// rules rely on until we rebuild.
|
// we rebuild.
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
self.has_state_dependency(state)
|
self.has_state_dependency(state)
|
||||||
|
@ -602,7 +564,7 @@ impl Stylist {
|
||||||
pub fn has_state_dependency(&self, state: ElementState) -> bool {
|
pub fn has_state_dependency(&self, state: ElementState) -> bool {
|
||||||
self.cascade_data
|
self.cascade_data
|
||||||
.iter_origins()
|
.iter_origins()
|
||||||
.any(|d| d.state_dependencies.intersects(state))
|
.any(|(d, _)| d.state_dependencies.intersects(state))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the style for a given "precomputed" pseudo-element, taking the
|
/// Computes the style for a given "precomputed" pseudo-element, taking the
|
||||||
|
@ -852,6 +814,12 @@ impl Stylist {
|
||||||
self.quirks_mode)
|
self.quirks_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_rules_for_pseudo(&self, pseudo: &PseudoElement) -> bool {
|
||||||
|
self.cascade_data
|
||||||
|
.iter_origins()
|
||||||
|
.any(|(d, _)| d.has_rules_for_pseudo(pseudo))
|
||||||
|
}
|
||||||
|
|
||||||
/// Computes the cascade inputs for a lazily-cascaded pseudo-element.
|
/// Computes the cascade inputs for a lazily-cascaded pseudo-element.
|
||||||
///
|
///
|
||||||
/// See the documentation on lazy pseudo-elements in
|
/// See the documentation on lazy pseudo-elements in
|
||||||
|
@ -868,7 +836,7 @@ impl Stylist {
|
||||||
let pseudo = pseudo.canonical();
|
let pseudo = pseudo.canonical();
|
||||||
debug_assert!(pseudo.is_lazy());
|
debug_assert!(pseudo.is_lazy());
|
||||||
|
|
||||||
if !self.cascade_data.has_rules_for_pseudo(&pseudo) {
|
if !self.has_rules_for_pseudo(&pseudo) {
|
||||||
return CascadeInputs::default()
|
return CascadeInputs::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1031,8 +999,14 @@ impl Stylist {
|
||||||
let effective_now =
|
let effective_now =
|
||||||
stylesheet.is_effective_for_device(&self.device, guard);
|
stylesheet.is_effective_for_device(&self.device, guard);
|
||||||
|
|
||||||
|
let origin = stylesheet.origin(guard);
|
||||||
|
let origin_cascade_data =
|
||||||
|
self.cascade_data.borrow_for_origin(&origin);
|
||||||
|
|
||||||
let effective_then =
|
let effective_then =
|
||||||
self.effective_media_query_results.was_effective(stylesheet);
|
origin_cascade_data
|
||||||
|
.effective_media_query_results
|
||||||
|
.was_effective(stylesheet);
|
||||||
|
|
||||||
if effective_now != effective_then {
|
if effective_now != effective_then {
|
||||||
debug!(" > Stylesheet changed -> {}, {}",
|
debug!(" > Stylesheet changed -> {}, {}",
|
||||||
|
@ -1071,7 +1045,9 @@ impl Stylist {
|
||||||
import_rule.stylesheet
|
import_rule.stylesheet
|
||||||
.is_effective_for_device(&self.device, guard);
|
.is_effective_for_device(&self.device, guard);
|
||||||
let effective_then =
|
let effective_then =
|
||||||
self.effective_media_query_results.was_effective(import_rule);
|
origin_cascade_data
|
||||||
|
.effective_media_query_results
|
||||||
|
.was_effective(import_rule);
|
||||||
if effective_now != effective_then {
|
if effective_now != effective_then {
|
||||||
debug!(" > @import rule changed {} -> {}",
|
debug!(" > @import rule changed {} -> {}",
|
||||||
effective_then, effective_now);
|
effective_then, effective_now);
|
||||||
|
@ -1088,7 +1064,9 @@ impl Stylist {
|
||||||
let effective_now =
|
let effective_now =
|
||||||
mq.evaluate(&self.device, self.quirks_mode);
|
mq.evaluate(&self.device, self.quirks_mode);
|
||||||
let effective_then =
|
let effective_then =
|
||||||
self.effective_media_query_results.was_effective(media_rule);
|
origin_cascade_data
|
||||||
|
.effective_media_query_results
|
||||||
|
.was_effective(media_rule);
|
||||||
if effective_now != effective_then {
|
if effective_now != effective_then {
|
||||||
debug!(" > @media rule changed {} -> {}",
|
debug!(" > @media rule changed {} -> {}",
|
||||||
effective_then, effective_now);
|
effective_then, effective_now);
|
||||||
|
@ -1324,7 +1302,7 @@ impl Stylist {
|
||||||
pub fn may_have_rules_for_id(&self, id: &Atom) -> bool {
|
pub fn may_have_rules_for_id(&self, id: &Atom) -> bool {
|
||||||
self.cascade_data
|
self.cascade_data
|
||||||
.iter_origins()
|
.iter_origins()
|
||||||
.any(|d| d.mapped_ids.might_contain_hash(id.get_hash()))
|
.any(|(d, _)| d.mapped_ids.might_contain_hash(id.get_hash()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return whether the device is dirty, that is, whether the screen size or
|
/// Return whether the device is dirty, that is, whether the screen size or
|
||||||
|
@ -1339,7 +1317,7 @@ impl Stylist {
|
||||||
pub fn get_animation(&self, name: &Atom) -> Option<&KeyframesAnimation> {
|
pub fn get_animation(&self, name: &Atom) -> Option<&KeyframesAnimation> {
|
||||||
self.cascade_data
|
self.cascade_data
|
||||||
.iter_origins()
|
.iter_origins()
|
||||||
.filter_map(|d| d.animations.get(name))
|
.filter_map(|(d, _)| d.animations.get(name))
|
||||||
.next()
|
.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1364,8 +1342,8 @@ impl Stylist {
|
||||||
// the lookups, which means that the bitvecs are comparable. We verify
|
// the lookups, which means that the bitvecs are comparable. We verify
|
||||||
// this in the caller by asserting that the bitvecs are same-length.
|
// this in the caller by asserting that the bitvecs are same-length.
|
||||||
let mut results = BitVec::new();
|
let mut results = BitVec::new();
|
||||||
for origin_cascade_data in self.cascade_data.iter_origins() {
|
for (data, _) in self.cascade_data.iter_origins() {
|
||||||
origin_cascade_data.selectors_for_cache_revalidation.lookup(
|
data.selectors_for_cache_revalidation.lookup(
|
||||||
*element, self.quirks_mode, &mut |selector_and_hashes| {
|
*element, self.quirks_mode, &mut |selector_and_hashes| {
|
||||||
results.push(matches_selector(&selector_and_hashes.selector,
|
results.push(matches_selector(&selector_and_hashes.selector,
|
||||||
selector_and_hashes.selector_offset,
|
selector_and_hashes.selector_offset,
|
||||||
|
@ -1431,6 +1409,44 @@ impl Stylist {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This struct holds data which users of Stylist may want to extract
|
||||||
|
/// from stylesheets which can be done at the same time as updating.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ExtraStyleData {
|
||||||
|
/// A list of effective font-face rules and their origin.
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
pub font_faces: Vec<Arc<Locked<FontFaceRule>>>,
|
||||||
|
|
||||||
|
/// A map of effective counter-style rules.
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
pub counter_styles: PrecomputedHashMap<Atom, Arc<Locked<CounterStyleRule>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
impl ExtraStyleData {
|
||||||
|
/// Add the given @font-face rule.
|
||||||
|
fn add_font_face(&mut self, rule: &Arc<Locked<FontFaceRule>>) {
|
||||||
|
self.font_faces.push(rule.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add the given @counter-style rule.
|
||||||
|
fn add_counter_style(&mut self, guard: &SharedRwLockReadGuard,
|
||||||
|
rule: &Arc<Locked<CounterStyleRule>>) {
|
||||||
|
let name = rule.read_with(guard).mName.raw::<nsIAtom>().into();
|
||||||
|
self.counter_styles.insert(name, rule.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PerOriginClear for ExtraStyleData {
|
||||||
|
fn clear(&mut self) {
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
{
|
||||||
|
self.font_faces.clear();
|
||||||
|
self.counter_styles.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// SelectorMapEntry implementation for use in our revalidation selector map.
|
/// SelectorMapEntry implementation for use in our revalidation selector map.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct RevalidationSelectorAndHashes {
|
struct RevalidationSelectorAndHashes {
|
||||||
|
@ -1599,111 +1615,11 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data resulting from performing the CSS cascade.
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct CascadeData {
|
|
||||||
/// Rules from user agent stylesheets
|
|
||||||
user_agent: PerOriginCascadeData,
|
|
||||||
/// Rules from author stylesheets
|
|
||||||
author: PerOriginCascadeData,
|
|
||||||
/// Rules from user stylesheets
|
|
||||||
user: PerOriginCascadeData,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CascadeData {
|
|
||||||
fn new() -> Self {
|
|
||||||
CascadeData {
|
|
||||||
user_agent: PerOriginCascadeData::new(),
|
|
||||||
author: PerOriginCascadeData::new(),
|
|
||||||
user: PerOriginCascadeData::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn borrow_mut_for_origin(&mut self, origin: &Origin) -> &mut PerOriginCascadeData {
|
|
||||||
match *origin {
|
|
||||||
Origin::UserAgent => &mut self.user_agent,
|
|
||||||
Origin::Author => &mut self.author,
|
|
||||||
Origin::User => &mut self.user,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear(&mut self) {
|
|
||||||
self.user_agent.clear();
|
|
||||||
self.author.clear();
|
|
||||||
self.user.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_rules_for_pseudo(&self, pseudo: &PseudoElement) -> bool {
|
|
||||||
self.iter_origins().any(|d| d.has_rules_for_pseudo(pseudo))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn iter_origins(&self) -> CascadeDataIter {
|
|
||||||
CascadeDataIter {
|
|
||||||
cascade_data: &self,
|
|
||||||
cur: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterator over `PerOriginCascadeData`, from highest level (user) to lowest
|
|
||||||
/// (user agent).
|
|
||||||
///
|
|
||||||
/// We rely on this specific order for correctly looking up animations
|
|
||||||
/// (prioritizing rules at higher cascade levels), among other things.
|
|
||||||
struct CascadeDataIter<'a> {
|
|
||||||
cascade_data: &'a CascadeData,
|
|
||||||
cur: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for CascadeDataIter<'a> {
|
|
||||||
type Item = &'a PerOriginCascadeData;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<&'a PerOriginCascadeData> {
|
|
||||||
let result = match self.cur {
|
|
||||||
0 => &self.cascade_data.user,
|
|
||||||
1 => &self.cascade_data.author,
|
|
||||||
2 => &self.cascade_data.user_agent,
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
self.cur += 1;
|
|
||||||
Some(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterator over `PerOriginExtraStyleData`, from highest level (user) to lowest
|
|
||||||
/// (user agent).
|
|
||||||
///
|
|
||||||
/// We rely on this specific order for correctly looking up the @font-face
|
|
||||||
/// and @counter-style rules.
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
pub struct ExtraStyleDataIter<'a> {
|
|
||||||
extra_style_data: &'a ExtraStyleData,
|
|
||||||
cur: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
impl<'a> Iterator for ExtraStyleDataIter<'a> {
|
|
||||||
type Item = (&'a PerOriginExtraStyleData, Origin);
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<(&'a PerOriginExtraStyleData, Origin)> {
|
|
||||||
let result = match self.cur {
|
|
||||||
0 => (&self.extra_style_data.user, Origin::User),
|
|
||||||
1 => (&self.extra_style_data.author, Origin::Author),
|
|
||||||
2 => (&self.extra_style_data.user_agent, Origin::UserAgent),
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
self.cur += 1;
|
|
||||||
Some(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Data resulting from performing the CSS cascade that is specific to a given
|
/// Data resulting from performing the CSS cascade that is specific to a given
|
||||||
/// origin.
|
/// origin.
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct PerOriginCascadeData {
|
struct CascadeData {
|
||||||
/// Rules from stylesheets at this `CascadeData`'s origin.
|
/// Rules from stylesheets at this `CascadeData`'s origin.
|
||||||
element_map: SelectorMap<Rule>,
|
element_map: SelectorMap<Rule>,
|
||||||
|
|
||||||
|
@ -1751,14 +1667,26 @@ struct PerOriginCascadeData {
|
||||||
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
|
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
|
||||||
selectors_for_cache_revalidation: SelectorMap<RevalidationSelectorAndHashes>,
|
selectors_for_cache_revalidation: SelectorMap<RevalidationSelectorAndHashes>,
|
||||||
|
|
||||||
|
/// Effective media query results cached from the last rebuild.
|
||||||
|
effective_media_query_results: EffectiveMediaQueryResults,
|
||||||
|
|
||||||
|
/// A monotonically increasing counter to represent the order on which a
|
||||||
|
/// style rule appears in a stylesheet, needed to sort them by source order.
|
||||||
|
rules_source_order: u32,
|
||||||
|
|
||||||
/// The total number of selectors.
|
/// The total number of selectors.
|
||||||
num_selectors: usize,
|
num_selectors: usize,
|
||||||
|
|
||||||
/// The total number of declarations.
|
/// The total number of declarations.
|
||||||
num_declarations: usize,
|
num_declarations: usize,
|
||||||
|
|
||||||
|
/// If true, the `CascadeData` is in a cleared state (e.g. just-constructed,
|
||||||
|
/// or had `clear()` called on it with no following `rebuild()` on the
|
||||||
|
/// `Stylist`).
|
||||||
|
is_cleared: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PerOriginCascadeData {
|
impl CascadeData {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
element_map: SelectorMap::new(),
|
element_map: SelectorMap::new(),
|
||||||
|
@ -1770,8 +1698,11 @@ impl PerOriginCascadeData {
|
||||||
state_dependencies: ElementState::empty(),
|
state_dependencies: ElementState::empty(),
|
||||||
mapped_ids: NonCountingBloomFilter::new(),
|
mapped_ids: NonCountingBloomFilter::new(),
|
||||||
selectors_for_cache_revalidation: SelectorMap::new(),
|
selectors_for_cache_revalidation: SelectorMap::new(),
|
||||||
|
effective_media_query_results: EffectiveMediaQueryResults::new(),
|
||||||
|
rules_source_order: 0,
|
||||||
num_selectors: 0,
|
num_selectors: 0,
|
||||||
num_declarations: 0,
|
num_declarations: 0,
|
||||||
|
is_cleared: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1783,7 +1714,17 @@ impl PerOriginCascadeData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_rules_for_pseudo(&self, pseudo: &PseudoElement) -> bool {
|
||||||
|
self.pseudos_map.get(pseudo).is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PerOriginClear for CascadeData {
|
||||||
fn clear(&mut self) {
|
fn clear(&mut self) {
|
||||||
|
if self.is_cleared {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.element_map = SelectorMap::new();
|
self.element_map = SelectorMap::new();
|
||||||
self.pseudos_map = Default::default();
|
self.pseudos_map = Default::default();
|
||||||
self.animations = Default::default();
|
self.animations = Default::default();
|
||||||
|
@ -1793,12 +1734,17 @@ impl PerOriginCascadeData {
|
||||||
self.state_dependencies = ElementState::empty();
|
self.state_dependencies = ElementState::empty();
|
||||||
self.mapped_ids.clear();
|
self.mapped_ids.clear();
|
||||||
self.selectors_for_cache_revalidation = SelectorMap::new();
|
self.selectors_for_cache_revalidation = SelectorMap::new();
|
||||||
|
self.effective_media_query_results.clear();
|
||||||
|
self.rules_source_order = 0;
|
||||||
self.num_selectors = 0;
|
self.num_selectors = 0;
|
||||||
self.num_declarations = 0;
|
self.num_declarations = 0;
|
||||||
|
self.is_cleared = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn has_rules_for_pseudo(&self, pseudo: &PseudoElement) -> bool {
|
impl Default for CascadeData {
|
||||||
self.pseudos_map.get(pseudo).is_some()
|
fn default() -> Self {
|
||||||
|
CascadeData::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,10 @@ use style::gecko_bindings::structs::{nsCSSFontFaceRule, nsCSSCounterStyleRule};
|
||||||
use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, PropertyValuePair};
|
use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, PropertyValuePair};
|
||||||
use style::gecko_bindings::structs::IterationCompositeOperation;
|
use style::gecko_bindings::structs::IterationCompositeOperation;
|
||||||
use style::gecko_bindings::structs::MallocSizeOf;
|
use style::gecko_bindings::structs::MallocSizeOf;
|
||||||
|
use style::gecko_bindings::structs::OriginFlags;
|
||||||
|
use style::gecko_bindings::structs::OriginFlags_Author;
|
||||||
|
use style::gecko_bindings::structs::OriginFlags_User;
|
||||||
|
use style::gecko_bindings::structs::OriginFlags_UserAgent;
|
||||||
use style::gecko_bindings::structs::RawGeckoGfxMatrix4x4;
|
use style::gecko_bindings::structs::RawGeckoGfxMatrix4x4;
|
||||||
use style::gecko_bindings::structs::RawGeckoPresContextOwned;
|
use style::gecko_bindings::structs::RawGeckoPresContextOwned;
|
||||||
use style::gecko_bindings::structs::SeenPtrs;
|
use style::gecko_bindings::structs::SeenPtrs;
|
||||||
|
@ -114,6 +118,7 @@ use style::stylesheets::{FontFeatureValuesRule, ImportRule, KeyframesRule, Mallo
|
||||||
use style::stylesheets::{MallocSizeOfWithRepeats, MediaRule};
|
use style::stylesheets::{MallocSizeOfWithRepeats, MediaRule};
|
||||||
use style::stylesheets::{NamespaceRule, Origin, PageRule, SizeOfState, StyleRule, SupportsRule};
|
use style::stylesheets::{NamespaceRule, Origin, PageRule, SizeOfState, StyleRule, SupportsRule};
|
||||||
use style::stylesheets::StylesheetContents;
|
use style::stylesheets::StylesheetContents;
|
||||||
|
use style::stylesheets::StylesheetInDocument;
|
||||||
use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
|
use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
|
||||||
use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue};
|
use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue};
|
||||||
use style::stylesheets::supports_rule::parse_condition_or_declaration;
|
use style::stylesheets::supports_rule::parse_condition_or_declaration;
|
||||||
|
@ -845,12 +850,10 @@ pub extern "C" fn Servo_StyleSet_AppendStyleSheet(
|
||||||
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
||||||
let mut data = &mut *data;
|
let mut data = &mut *data;
|
||||||
let guard = global_style_data.shared_lock.read();
|
let guard = global_style_data.shared_lock.read();
|
||||||
data.stylesheets.append_stylesheet(
|
let sheet = unsafe { GeckoStyleSheet::new(sheet) };
|
||||||
&data.stylist,
|
let origin = sheet.contents(&guard).origin;
|
||||||
unsafe { GeckoStyleSheet::new(sheet) },
|
data.stylesheets.append_stylesheet(&data.stylist, sheet, &guard);
|
||||||
&guard
|
data.clear_stylist_origin(&origin);
|
||||||
);
|
|
||||||
data.clear_stylist();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -894,12 +897,10 @@ pub extern "C" fn Servo_StyleSet_PrependStyleSheet(
|
||||||
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
||||||
let mut data = &mut *data;
|
let mut data = &mut *data;
|
||||||
let guard = global_style_data.shared_lock.read();
|
let guard = global_style_data.shared_lock.read();
|
||||||
data.stylesheets.prepend_stylesheet(
|
let sheet = unsafe { GeckoStyleSheet::new(sheet) };
|
||||||
&data.stylist,
|
let origin = sheet.contents(&guard).origin;
|
||||||
unsafe { GeckoStyleSheet::new(sheet) },
|
data.stylesheets.prepend_stylesheet(&data.stylist, sheet, &guard);
|
||||||
&guard,
|
data.clear_stylist_origin(&origin);
|
||||||
);
|
|
||||||
data.clear_stylist();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -912,13 +913,15 @@ pub extern "C" fn Servo_StyleSet_InsertStyleSheetBefore(
|
||||||
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
||||||
let mut data = &mut *data;
|
let mut 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 origin = sheet.contents(&guard).origin;
|
||||||
data.stylesheets.insert_stylesheet_before(
|
data.stylesheets.insert_stylesheet_before(
|
||||||
&data.stylist,
|
&data.stylist,
|
||||||
unsafe { GeckoStyleSheet::new(sheet) },
|
sheet,
|
||||||
unsafe { GeckoStyleSheet::new(before_sheet) },
|
unsafe { GeckoStyleSheet::new(before_sheet) },
|
||||||
&guard,
|
&guard,
|
||||||
);
|
);
|
||||||
data.clear_stylist();
|
data.clear_stylist_origin(&origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -930,12 +933,10 @@ pub extern "C" fn Servo_StyleSet_RemoveStyleSheet(
|
||||||
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
||||||
let mut data = &mut *data;
|
let mut data = &mut *data;
|
||||||
let guard = global_style_data.shared_lock.read();
|
let guard = global_style_data.shared_lock.read();
|
||||||
data.stylesheets.remove_stylesheet(
|
let sheet = unsafe { GeckoStyleSheet::new(sheet) };
|
||||||
&data.stylist,
|
let origin = sheet.contents(&guard).origin;
|
||||||
unsafe { GeckoStyleSheet::new(sheet) },
|
data.stylesheets.remove_stylesheet(&data.stylist, sheet, &guard);
|
||||||
&guard,
|
data.clear_stylist_origin(&origin);
|
||||||
);
|
|
||||||
data.clear_stylist();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -954,11 +955,14 @@ pub extern "C" fn Servo_StyleSet_FlushStyleSheets(
|
||||||
pub extern "C" fn Servo_StyleSet_NoteStyleSheetsChanged(
|
pub extern "C" fn Servo_StyleSet_NoteStyleSheetsChanged(
|
||||||
raw_data: RawServoStyleSetBorrowed,
|
raw_data: RawServoStyleSetBorrowed,
|
||||||
author_style_disabled: bool,
|
author_style_disabled: bool,
|
||||||
|
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();
|
for origin in changed_origins.iter() {
|
||||||
|
data.stylesheets.force_dirty_origin(&origin);
|
||||||
|
data.clear_stylist_origin(&origin);
|
||||||
|
}
|
||||||
data.stylesheets.set_author_style_disabled(author_style_disabled);
|
data.stylesheets.set_author_style_disabled(author_style_disabled);
|
||||||
data.clear_stylist();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -1008,6 +1012,17 @@ pub extern "C" fn Servo_StyleSheet_SizeOfIncludingThis(
|
||||||
.malloc_size_of_children(&guard, malloc_size_of)
|
.malloc_size_of_children(&guard, malloc_size_of)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn Servo_StyleSheet_GetOrigin(
|
||||||
|
sheet: RawServoStyleSheetContentsBorrowed
|
||||||
|
) -> OriginFlags {
|
||||||
|
match StylesheetContents::as_arc(&sheet).origin {
|
||||||
|
Origin::UserAgent => OriginFlags_UserAgent,
|
||||||
|
Origin::User => OriginFlags_User,
|
||||||
|
Origin::Author => OriginFlags_Author,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn read_locked_arc<T, R, F>(raw: &<Locked<T> as HasFFI>::FFIType, func: F) -> R
|
fn read_locked_arc<T, R, F>(raw: &<Locked<T> as HasFFI>::FFIType, func: F) -> R
|
||||||
where Locked<T>: HasArcFFI, F: FnOnce(&T) -> R
|
where Locked<T>: HasArcFFI, F: FnOnce(&T) -> R
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue