/* 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 https://mozilla.org/MPL/2.0/. */ //! Data needed to style a Gecko document. use crate::dom::TElement; use crate::gecko_bindings::bindings; use crate::gecko_bindings::structs::{ self, ServoStyleSetSizes, StyleSheet as DomStyleSheet, StyleSheetInfo, }; use crate::invalidation::media_queries::{MediaListKey, ToMediaListKey}; use crate::media_queries::{Device, MediaList}; use crate::properties::ComputedValues; use crate::selector_parser::SnapshotMap; use crate::shared_lock::{SharedRwLockReadGuard, StylesheetGuards}; use crate::stylesheets::{StylesheetContents, StylesheetInDocument}; use crate::stylist::Stylist; use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; use malloc_size_of::MallocSizeOfOps; use servo_arc::Arc; use std::fmt; /// Little wrapper to a Gecko style sheet. #[derive(Eq, PartialEq)] pub struct GeckoStyleSheet(*const DomStyleSheet); // NOTE(emilio): These are kind of a lie. We allow to make these Send + Sync so that other data // structures can also be Send and Sync, but Gecko's stylesheets are main-thread-reference-counted. // // We assert that we reference-count in the right thread (in the Addref/Release implementations). // Sending these to a different thread can't really happen (it could theoretically really happen if // we allowed @import rules inside a nested style rule, but that can't happen per spec and would be // a parser bug, caught by the asserts). unsafe impl Send for GeckoStyleSheet {} unsafe impl Sync for GeckoStyleSheet {} impl fmt::Debug for GeckoStyleSheet { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { let contents = self.contents(); formatter .debug_struct("GeckoStyleSheet") .field("origin", &contents.origin) .field("url_data", &*contents.url_data.read()) .finish() } } impl ToMediaListKey for crate::gecko::data::GeckoStyleSheet { fn to_media_list_key(&self) -> MediaListKey { use std::mem; unsafe { MediaListKey::from_raw(mem::transmute(self.0)) } } } impl GeckoStyleSheet { /// Create a `GeckoStyleSheet` from a raw `DomStyleSheet` pointer. #[inline] pub unsafe fn new(s: *const DomStyleSheet) -> Self { debug_assert!(!s.is_null()); bindings::Gecko_StyleSheet_AddRef(s); Self::from_addrefed(s) } /// Create a `GeckoStyleSheet` from a raw `DomStyleSheet` pointer that /// already holds a strong reference. #[inline] pub unsafe fn from_addrefed(s: *const DomStyleSheet) -> Self { assert!(!s.is_null()); GeckoStyleSheet(s) } /// HACK(emilio): This is so that we can avoid crashing release due to /// bug 1719963 and can hopefully get a useful report from fuzzers. #[inline] pub fn hack_is_null(&self) -> bool { self.0.is_null() } /// Get the raw `StyleSheet` that we're wrapping. pub fn raw(&self) -> &DomStyleSheet { unsafe { &*self.0 } } fn inner(&self) -> &StyleSheetInfo { unsafe { &*(self.raw().mInner as *const StyleSheetInfo) } } } impl Drop for GeckoStyleSheet { fn drop(&mut self) { unsafe { bindings::Gecko_StyleSheet_Release(self.0) }; } } impl Clone for GeckoStyleSheet { fn clone(&self) -> Self { unsafe { bindings::Gecko_StyleSheet_AddRef(self.0) }; GeckoStyleSheet(self.0) } } impl StylesheetInDocument for GeckoStyleSheet { fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { use crate::gecko_bindings::structs::mozilla::dom::MediaList as DomMediaList; unsafe { let dom_media_list = self.raw().mMedia.mRawPtr as *const DomMediaList; if dom_media_list.is_null() { return None; } let list = &*(*dom_media_list).mRawList.mRawPtr; Some(list.read_with(guard)) } } // All the stylesheets Servo knows about are enabled, because that state is // handled externally by Gecko. #[inline] fn enabled(&self) -> bool { true } #[inline] fn contents(&self) -> &StylesheetContents { debug_assert!(!self.inner().mContents.mRawPtr.is_null()); unsafe { &*self.inner().mContents.mRawPtr } } } /// The container for data that a Servo-backed Gecko document needs to style /// itself. pub struct PerDocumentStyleDataImpl { /// Rule processor. pub stylist: Stylist, /// A cache from element to resolved style. pub undisplayed_style_cache: crate::traversal::UndisplayedStyleCache, /// The generation for which our cache is valid. pub undisplayed_style_cache_generation: u64, } /// The data itself is an `AtomicRefCell`, which guarantees the proper semantics /// and unexpected races while trying to mutate it. pub struct PerDocumentStyleData(AtomicRefCell); impl PerDocumentStyleData { /// Create a `PerDocumentStyleData`. pub fn new(document: *const structs::Document) -> Self { let device = Device::new(document); let quirks_mode = device.document().mCompatMode; PerDocumentStyleData(AtomicRefCell::new(PerDocumentStyleDataImpl { stylist: Stylist::new(device, quirks_mode.into()), undisplayed_style_cache: Default::default(), undisplayed_style_cache_generation: 0, })) } /// Get an immutable reference to this style data. pub fn borrow(&self) -> AtomicRef { self.0.borrow() } /// Get an mutable reference to this style data. pub fn borrow_mut(&self) -> AtomicRefMut { self.0.borrow_mut() } } impl PerDocumentStyleDataImpl { /// Recreate the style data if the stylesheets have changed. pub fn flush_stylesheets( &mut self, guard: &SharedRwLockReadGuard, document_element: Option, snapshots: Option<&SnapshotMap>, ) -> bool where E: TElement, { self.stylist .flush(&StylesheetGuards::same(guard), document_element, snapshots) } /// Get the default computed values for this document. pub fn default_computed_values(&self) -> &Arc { self.stylist.device().default_computed_values_arc() } /// Measure heap usage. pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) { self.stylist.add_size_of(ops, sizes); } } /// The gecko-specific AuthorStyles instantiation. pub type AuthorStyles = crate::author_styles::AuthorStyles;