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