servo/components/style/invalidation/media_queries.rs
author Emilio Cobos Álvarez <emilio@crisal.io>
Tue, 05 Sep 2017 12:51:17 -0500
changeset 428503 49d106783eecf3140a81575e807e5f6325d6bba6
parent 425985 809640e1d6115fbecdea719f03a586d0062aa896
child 429684 623f813e5a7563a8b5065761324b007f32585d95
permissions -rw-r--r--
servo: Merge #18384 - style: Don't waste a whole selector map for each class / id in the document (from emilio:invalidation-map-bloat); r=bholley On top of #18375, only last commit needs review. Source-Repo: https://github.com/servo/servo Source-Revision: 4721ef81fd7fb6ee15546a6abe140a66ce0280e8

/* 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, MallocEnclosingSizeOfFn, MallocSizeOfHash, MediaRule};
use stylesheets::{NestedRuleIterationCondition, Stylesheet, SupportsRule};

/// 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(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct MediaListKey(usize);

impl MediaListKey {
    /// Create a MediaListKey from a raw usize.
    pub fn from_raw(k: usize) -> Self {
        MediaListKey(k)
    }
}

/// 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.
#[derive(Debug)]
#[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());
    }

    /// Measure heap usage.
    pub fn malloc_size_of_children(&self, malloc_enclosing_size_of: MallocEnclosingSizeOfFn)
                                   -> usize {
        self.set.malloc_shallow_size_of_hash(malloc_enclosing_size_of)
    }
}

/// 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)
    }
}