Cache effective media query results.

This patch also makes RulesIterator not iterate over rules for which we don't
process nested rules.

There's nothing depending on this behavior right now afaik, and this will make
us duplicate less code in following patches.

Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1357461
MozReview-Commit-ID: CaMFQtAVnJF
This commit is contained in:
Emilio Cobos Álvarez 2017-06-02 17:38:41 +02:00
parent b5232c940d
commit 5c66d3c77a
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
6 changed files with 242 additions and 87 deletions

View file

@ -0,0 +1,133 @@
/* 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/. */
//! Code related to the invalidation of media-query-affected rules.
use context::QuirksMode;
use fnv::FnvHashSet;
use media_queries::Device;
use shared_lock::SharedRwLockReadGuard;
use stylesheets::{DocumentRule, ImportRule, MediaRule, SupportsRule};
use stylesheets::{NestedRuleIterationCondition, Stylesheet};
/// A key for a given media query result.
///
/// NOTE: It happens to be the case that all the media lists we care about
/// happen to have a stable address, so we can just use an opaque pointer to
/// represent them.
///
/// Also, note that right now when a rule or stylesheet is removed, we do a full
/// style flush, so there's no need to worry about other item created with the
/// same pointer address.
///
/// If this changes, though, we may need to remove the item from the cache if
/// present before it goes away.
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct MediaListKey(usize);
/// A trait to get a given `MediaListKey` for a given item that can hold a
/// `MediaList`.
pub trait ToMediaListKey : Sized {
/// Get a `MediaListKey` for this item. This key needs to uniquely identify
/// the item.
#[allow(unsafe_code)]
fn to_media_list_key(&self) -> MediaListKey {
use std::mem;
MediaListKey(unsafe { mem::transmute(self as *const Self) })
}
}
impl ToMediaListKey for Stylesheet {}
impl ToMediaListKey for ImportRule {}
impl ToMediaListKey for MediaRule {}
/// A struct that holds the result of a media query evaluation pass for the
/// media queries that evaluated successfully.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct EffectiveMediaQueryResults {
/// The set of media lists that matched last time.
set: FnvHashSet<MediaListKey>,
}
impl EffectiveMediaQueryResults {
/// Trivially constructs an empty `EffectiveMediaQueryResults`.
pub fn new() -> Self {
Self {
set: FnvHashSet::default(),
}
}
/// Resets the results, using an empty key.
pub fn clear(&mut self) {
self.set.clear()
}
/// Returns whether a given item was known to be effective when the results
/// were cached.
pub fn was_effective<T>(&self, item: &T) -> bool
where T: ToMediaListKey,
{
self.set.contains(&item.to_media_list_key())
}
/// Notices that an effective item has been seen, and caches it as matching.
pub fn saw_effective<T>(&mut self, item: &T)
where T: ToMediaListKey,
{
// NOTE(emilio): We can't assert that we don't cache the same item twice
// because of stylesheet reusing... shrug.
self.set.insert(item.to_media_list_key());
}
}
/// A filter that filters over effective rules, but allowing all potentially
/// effective `@media` rules.
pub struct PotentiallyEffectiveMediaRules;
impl NestedRuleIterationCondition for PotentiallyEffectiveMediaRules {
fn process_import(
_: &SharedRwLockReadGuard,
_: &Device,
_: QuirksMode,
_: &ImportRule)
-> bool
{
true
}
fn process_media(
_: &SharedRwLockReadGuard,
_: &Device,
_: QuirksMode,
_: &MediaRule)
-> bool
{
true
}
/// Whether we should process the nested rules in a given `@-moz-document` rule.
fn process_document(
guard: &SharedRwLockReadGuard,
device: &Device,
quirks_mode: QuirksMode,
rule: &DocumentRule)
-> bool
{
use stylesheets::EffectiveRules;
EffectiveRules::process_document(guard, device, quirks_mode, rule)
}
/// Whether we should process the nested rules in a given `@supports` rule.
fn process_supports(
guard: &SharedRwLockReadGuard,
device: &Device,
quirks_mode: QuirksMode,
rule: &SupportsRule)
-> bool
{
use stylesheets::EffectiveRules;
EffectiveRules::process_supports(guard, device, quirks_mode, rule)
}
}

View file

@ -4,4 +4,5 @@
//! Different bits of code related to invalidating style.
pub mod media_queries;
pub mod stylesheets;

View file

@ -175,6 +175,11 @@ impl StylesheetSet {
self.dirty = false;
self.invalidations.flush(document_element);
self.iter()
}
/// Returns an iterator over the current list of stylesheets.
pub fn iter(&self) -> StylesheetIterator {
StylesheetIterator(self.entries.iter())
}

View file

@ -1048,7 +1048,6 @@ impl NestedRuleIterationCondition for AllRules {
true
}
/// Whether we should process the nested rules in a given `@media` rule.
fn process_media(
_: &SharedRwLockReadGuard,
_: &Device,
@ -1059,7 +1058,6 @@ impl NestedRuleIterationCondition for AllRules {
true
}
/// Whether we should process the nested rules in a given `@-moz-document` rule.
fn process_document(
_: &SharedRwLockReadGuard,
_: &Device,
@ -1070,7 +1068,6 @@ impl NestedRuleIterationCondition for AllRules {
true
}
/// Whether we should process the nested rules in a given `@supports` rule.
fn process_supports(
_: &SharedRwLockReadGuard,
_: &Device,
@ -1158,36 +1155,31 @@ impl<'a, 'b, C> Iterator for RulesIterator<'a, 'b, C>
sub_iter = match *rule {
CssRule::Import(ref import_rule) => {
let import_rule = import_rule.read_with(self.guard);
if C::process_import(self.guard, self.device, self.quirks_mode, import_rule) {
Some(import_rule.stylesheet.rules.read_with(self.guard).0.iter())
} else {
None
if !C::process_import(self.guard, self.device, self.quirks_mode, import_rule) {
continue;
}
Some(import_rule.stylesheet.rules.read_with(self.guard).0.iter())
}
CssRule::Document(ref doc_rule) => {
let doc_rule = doc_rule.read_with(self.guard);
if C::process_document(self.guard, self.device, self.quirks_mode, doc_rule) {
Some(doc_rule.rules.read_with(self.guard).0.iter())
} else {
None
if !C::process_document(self.guard, self.device, self.quirks_mode, doc_rule) {
continue;
}
Some(doc_rule.rules.read_with(self.guard).0.iter())
}
CssRule::Media(ref lock) => {
let media_rule = lock.read_with(self.guard);
if C::process_media(self.guard, self.device, self.quirks_mode, media_rule) {
Some(media_rule.rules.read_with(self.guard).0.iter())
} else {
None
if !C::process_media(self.guard, self.device, self.quirks_mode, media_rule) {
continue;
}
Some(media_rule.rules.read_with(self.guard).0.iter())
}
CssRule::Supports(ref lock) => {
let supports_rule = lock.read_with(self.guard);
if C::process_supports(self.guard, self.device, self.quirks_mode, supports_rule) {
Some(supports_rule.rules.read_with(self.guard).0.iter())
} else {
None
if !C::process_supports(self.guard, self.device, self.quirks_mode, supports_rule) {
continue;
}
Some(supports_rule.rules.read_with(self.guard).0.iter())
}
CssRule::Namespace(_) |
CssRule::Style(_) |

View file

@ -14,6 +14,7 @@ use error_reporting::RustLogReporter;
use font_metrics::FontMetricsProvider;
#[cfg(feature = "gecko")]
use gecko_bindings::structs::{nsIAtom, StyleRuleInclusion};
use invalidation::media_queries::EffectiveMediaQueryResults;
use keyframes::KeyframesAnimation;
use media_queries::Device;
use properties::{self, CascadeFlags, ComputedValues};
@ -39,9 +40,8 @@ use style_traits::viewport::ViewportConstraints;
use stylearc::Arc;
#[cfg(feature = "gecko")]
use stylesheets::{CounterStyleRule, FontFaceRule};
use stylesheets::{CssRule, DocumentRule, ImportRule, MediaRule, StyleRule, SupportsRule};
use stylesheets::{CssRule, StyleRule};
use stylesheets::{Stylesheet, Origin, UserAgentStylesheets};
use stylesheets::NestedRuleIterationCondition;
use thread_state;
use viewport::{self, MaybeNew, ViewportRule};
@ -83,6 +83,9 @@ pub struct Stylist {
/// Viewport constraints based on the current device.
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.
quirks_mode: QuirksMode,
@ -222,57 +225,6 @@ impl From<StyleRuleInclusion> for RuleInclusion {
}
}
/// A filter that filters over effective rules, but allowing all potentially
/// effective `@media` rules.
pub struct PotentiallyEffectiveMediaRules;
impl NestedRuleIterationCondition for PotentiallyEffectiveMediaRules {
fn process_import(
_: &SharedRwLockReadGuard,
_: &Device,
_: QuirksMode,
_: &ImportRule)
-> bool
{
true
}
fn process_media(
_: &SharedRwLockReadGuard,
_: &Device,
_: QuirksMode,
_: &MediaRule)
-> bool
{
true
}
/// Whether we should process the nested rules in a given `@-moz-document` rule.
fn process_document(
guard: &SharedRwLockReadGuard,
device: &Device,
quirks_mode: QuirksMode,
rule: &DocumentRule)
-> bool
{
use stylesheets::EffectiveRules;
EffectiveRules::process_document(guard, device, quirks_mode, rule)
}
/// Whether we should process the nested rules in a given `@supports` rule.
fn process_supports(
guard: &SharedRwLockReadGuard,
device: &Device,
quirks_mode: QuirksMode,
rule: &SupportsRule)
-> bool
{
use stylesheets::EffectiveRules;
EffectiveRules::process_supports(guard, device, quirks_mode, rule)
}
}
impl Stylist {
/// Construct a new `Stylist`, using given `Device` and `QuirksMode`.
/// If more members are added here, think about whether they should
@ -285,6 +237,7 @@ impl Stylist {
is_device_dirty: true,
is_cleared: true,
quirks_mode: quirks_mode,
effective_media_query_results: EffectiveMediaQueryResults::new(),
element_map: PerPseudoElementSelectorMap::new(),
pseudos_map: Default::default(),
@ -355,6 +308,7 @@ impl Stylist {
self.is_cleared = true;
self.effective_media_query_results.clear();
self.viewport_constraints = None;
// preserve current device
self.is_device_dirty = true;
@ -482,6 +436,8 @@ impl Stylist {
return;
}
self.effective_media_query_results.saw_effective(stylesheet);
for rule in stylesheet.effective_rules(&self.device, guard) {
match *rule {
CssRule::Style(ref locked) => {
@ -515,10 +471,17 @@ impl Stylist {
}
self.rules_source_order += 1;
}
CssRule::Import(..) => {
// effective_rules visits the inner stylesheet if
CssRule::Import(ref lock) => {
let import_rule = lock.read_with(guard);
self.effective_media_query_results.saw_effective(import_rule);
// NOTE: effective_rules visits the inner stylesheet if
// appropriate.
}
CssRule::Media(ref lock) => {
let media_rule = lock.read_with(guard);
self.effective_media_query_results.saw_effective(media_rule);
}
CssRule::Keyframes(ref keyframes_rule) => {
let keyframes_rule = keyframes_rule.read_with(guard);
debug!("Found valid keyframes rule: {:?}", *keyframes_rule);
@ -816,16 +779,50 @@ impl Stylist {
device.account_for_viewport_rule(constraints);
}
self.is_device_dirty |= stylesheets.iter().any(|stylesheet| {
let mq = stylesheet.media.read_with(guard);
if mq.evaluate(&self.device, self.quirks_mode) != mq.evaluate(&device, self.quirks_mode) {
self.device = device;
let features_changed = self.media_features_change_changed_style(
stylesheets.iter(),
guard
);
self.is_device_dirty |= features_changed;
}
/// Returns whether, given a media feature change, any previously-applicable
/// style has become non-applicable, or vice-versa.
pub fn media_features_change_changed_style<'a, I>(
&self,
stylesheets: I,
guard: &SharedRwLockReadGuard,
) -> bool
where I: Iterator<Item = &'a Arc<Stylesheet>>
{
use invalidation::media_queries::PotentiallyEffectiveMediaRules;
debug!("Stylist::media_features_change_changed_style");
for stylesheet in stylesheets {
let effective_now =
stylesheet.media.read_with(guard)
.evaluate(&self.device, self.quirks_mode);
let effective_then =
self.effective_media_query_results.was_effective(&**stylesheet);
if effective_now != effective_then {
debug!(" > Stylesheet changed -> {}, {}",
effective_then, effective_now);
return true
}
if !effective_now {
continue;
}
let mut iter =
stylesheet.iter_rules::<PotentiallyEffectiveMediaRules>(
&self.device,
guard);
guard
);
while let Some(rule) = iter.next() {
match *rule {
@ -844,8 +841,13 @@ impl Stylist {
CssRule::Import(ref lock) => {
let import_rule = lock.read_with(guard);
let mq = import_rule.stylesheet.media.read_with(guard);
let effective_now = mq.evaluate(&self.device, self.quirks_mode);
if effective_now != mq.evaluate(&device, self.quirks_mode) {
let effective_now =
mq.evaluate(&self.device, self.quirks_mode);
let effective_then =
self.effective_media_query_results.was_effective(import_rule);
if effective_now != effective_then {
debug!(" > @import rule changed {} -> {}",
effective_then, effective_now);
return true;
}
@ -856,8 +858,13 @@ impl Stylist {
CssRule::Media(ref lock) => {
let media_rule = lock.read_with(guard);
let mq = media_rule.media_queries.read_with(guard);
let effective_now = mq.evaluate(&self.device, self.quirks_mode);
if effective_now != mq.evaluate(&device, self.quirks_mode) {
let effective_now =
mq.evaluate(&self.device, self.quirks_mode);
let effective_then =
self.effective_media_query_results.was_effective(media_rule);
if effective_now != effective_then {
debug!(" > @media rule changed {} -> {}",
effective_then, effective_now);
return true;
}
@ -867,11 +874,9 @@ impl Stylist {
}
}
}
}
return false;
});
self.device = device;
return false;
}
/// Returns the viewport constraints that apply to this document because of

View file

@ -747,6 +747,24 @@ pub extern "C" fn Servo_StyleSet_AppendStyleSheet(raw_data: RawServoStyleSetBorr
data.clear_stylist();
}
#[no_mangle]
pub extern "C" fn Servo_StyleSet_MediumFeaturesChanged(
raw_data: RawServoStyleSetBorrowed,
) -> bool {
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
// NOTE(emilio): We don't actually need to flush the stylist here and ensure
// it's up to date.
//
// In case it isn't we would trigger a rebuild + restyle as needed too.
let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
data.stylist.media_features_change_changed_style(
data.stylesheets.iter(),
&guard,
)
}
#[no_mangle]
pub extern "C" fn Servo_StyleSet_PrependStyleSheet(raw_data: RawServoStyleSetBorrowed,
raw_sheet: RawServoStyleSheetBorrowed,
@ -1408,6 +1426,7 @@ pub extern "C" fn Servo_StyleSet_Init(pres_context: RawGeckoPresContextOwned)
pub extern "C" fn Servo_StyleSet_RebuildData(raw_data: RawServoStyleSetBorrowed) {
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
data.reset_device(&guard);
}