diff options
Diffstat (limited to '')
-rw-r--r-- | servo/components/style/stylesheets/mod.rs | 565 |
1 files changed, 565 insertions, 0 deletions
diff --git a/servo/components/style/stylesheets/mod.rs b/servo/components/style/stylesheets/mod.rs new file mode 100644 index 0000000000..800ebee12f --- /dev/null +++ b/servo/components/style/stylesheets/mod.rs @@ -0,0 +1,565 @@ +/* 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/. */ + +//! Style sheets and their CSS rules. + +mod cascading_at_rule; +pub mod container_rule; +mod counter_style_rule; +mod document_rule; +mod font_face_rule; +pub mod font_feature_values_rule; +pub mod font_palette_values_rule; +pub mod import_rule; +pub mod keyframes_rule; +pub mod layer_rule; +mod loader; +mod media_rule; +mod namespace_rule; +pub mod origin; +mod page_rule; +mod property_rule; +mod rule_list; +mod rule_parser; +mod rules_iterator; +mod style_rule; +mod stylesheet; +pub mod supports_rule; +pub mod viewport_rule; + +#[cfg(feature = "gecko")] +use crate::gecko_bindings::sugar::refptr::RefCounted; +#[cfg(feature = "gecko")] +use crate::gecko_bindings::{bindings, structs}; +use crate::parser::ParserContext; +use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked}; +use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; +use crate::str::CssStringWriter; +use cssparser::{parse_one_rule, Parser, ParserInput}; +#[cfg(feature = "gecko")] +use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; +use servo_arc::Arc; +use std::borrow::Cow; +use std::fmt; +#[cfg(feature = "gecko")] +use std::mem::{self, ManuallyDrop}; +use style_traits::ParsingMode; +#[cfg(feature = "gecko")] +use to_shmem::{self, SharedMemoryBuilder, ToShmem}; + +pub use self::container_rule::ContainerRule; +pub use self::counter_style_rule::CounterStyleRule; +pub use self::document_rule::DocumentRule; +pub use self::font_face_rule::FontFaceRule; +pub use self::font_feature_values_rule::FontFeatureValuesRule; +pub use self::font_palette_values_rule::FontPaletteValuesRule; +pub use self::import_rule::ImportRule; +pub use self::keyframes_rule::KeyframesRule; +pub use self::layer_rule::{LayerBlockRule, LayerStatementRule}; +pub use self::loader::StylesheetLoader; +pub use self::media_rule::MediaRule; +pub use self::namespace_rule::NamespaceRule; +pub use self::origin::{Origin, OriginSet, OriginSetIterator, PerOrigin, PerOriginIter}; +pub use self::page_rule::{PageRule, PageSelector, PageSelectors}; +pub use self::property_rule::PropertyRule; +pub use self::rule_list::{CssRules, CssRulesHelpers}; +pub use self::rule_parser::{InsertRuleContext, State, TopLevelRuleParser}; +pub use self::rules_iterator::{AllRules, EffectiveRules}; +pub use self::rules_iterator::{ + EffectiveRulesIterator, NestedRuleIterationCondition, RulesIterator, +}; +pub use self::style_rule::StyleRule; +pub use self::stylesheet::{AllowImportRules, SanitizationData, SanitizationKind}; +pub use self::stylesheet::{DocumentStyleSheet, Namespaces, Stylesheet}; +pub use self::stylesheet::{StylesheetContents, StylesheetInDocument, UserAgentStylesheets}; +pub use self::supports_rule::SupportsRule; +pub use self::viewport_rule::ViewportRule; + +/// The CORS mode used for a CSS load. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)] +pub enum CorsMode { + /// No CORS mode, so cross-origin loads can be done. + None, + /// Anonymous CORS request. + Anonymous, +} + +/// Extra data that the backend may need to resolve url values. +/// +/// If the usize's lowest bit is 0, then this is a strong reference to a +/// structs::URLExtraData object. +/// +/// Otherwise, shifting the usize's bits the right by one gives the +/// UserAgentStyleSheetID value corresponding to the style sheet whose +/// URLExtraData this is, which is stored in URLExtraData_sShared. We don't +/// hold a strong reference to that object from here, but we rely on that +/// array's objects being held alive until shutdown. +/// +/// We use this packed representation rather than an enum so that +/// `from_ptr_ref` can work. +#[cfg(feature = "gecko")] +#[derive(PartialEq)] +#[repr(C)] +pub struct UrlExtraData(usize); + +/// Extra data that the backend may need to resolve url values. +#[cfg(not(feature = "gecko"))] +pub type UrlExtraData = ::servo_url::ServoUrl; + +#[cfg(feature = "gecko")] +impl Clone for UrlExtraData { + fn clone(&self) -> UrlExtraData { + UrlExtraData::new(self.ptr()) + } +} + +#[cfg(feature = "gecko")] +impl Drop for UrlExtraData { + fn drop(&mut self) { + // No need to release when we have an index into URLExtraData_sShared. + if self.0 & 1 == 0 { + unsafe { + self.as_ref().release(); + } + } + } +} + +#[cfg(feature = "gecko")] +impl ToShmem for UrlExtraData { + fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> { + if self.0 & 1 == 0 { + let shared_extra_datas = unsafe { &structs::URLExtraData_sShared }; + let self_ptr = self.as_ref() as *const _ as *mut _; + let sheet_id = shared_extra_datas + .iter() + .position(|r| r.mRawPtr == self_ptr); + let sheet_id = match sheet_id { + Some(id) => id, + None => { + return Err(String::from( + "ToShmem failed for UrlExtraData: expected sheet's URLExtraData to be in \ + URLExtraData::sShared", + )); + }, + }; + Ok(ManuallyDrop::new(UrlExtraData((sheet_id << 1) | 1))) + } else { + Ok(ManuallyDrop::new(UrlExtraData(self.0))) + } + } +} + +#[cfg(feature = "gecko")] +impl UrlExtraData { + /// Create a new UrlExtraData wrapping a pointer to the specified Gecko + /// URLExtraData object. + pub fn new(ptr: *mut structs::URLExtraData) -> UrlExtraData { + unsafe { + (*ptr).addref(); + } + UrlExtraData(ptr as usize) + } + + /// True if this URL scheme is chrome. + #[inline] + pub fn chrome_rules_enabled(&self) -> bool { + self.as_ref().mChromeRulesEnabled + } + + /// Create a reference to this `UrlExtraData` from a reference to pointer. + /// + /// The pointer must be valid and non null. + /// + /// This method doesn't touch refcount. + #[inline] + pub unsafe fn from_ptr_ref(ptr: &*mut structs::URLExtraData) -> &Self { + mem::transmute(ptr) + } + + /// Returns a pointer to the Gecko URLExtraData object. + pub fn ptr(&self) -> *mut structs::URLExtraData { + if self.0 & 1 == 0 { + self.0 as *mut structs::URLExtraData + } else { + unsafe { + let sheet_id = self.0 >> 1; + structs::URLExtraData_sShared[sheet_id].mRawPtr + } + } + } + + fn as_ref(&self) -> &structs::URLExtraData { + unsafe { &*(self.ptr() as *const structs::URLExtraData) } + } +} + +#[cfg(feature = "gecko")] +impl fmt::Debug for UrlExtraData { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + macro_rules! define_debug_struct { + ($struct_name:ident, $gecko_class:ident, $debug_fn:ident) => { + struct $struct_name(*mut structs::$gecko_class); + impl fmt::Debug for $struct_name { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + use nsstring::nsCString; + let mut spec = nsCString::new(); + unsafe { + bindings::$debug_fn(self.0, &mut spec); + } + spec.fmt(formatter) + } + } + }; + } + + define_debug_struct!(DebugURI, nsIURI, Gecko_nsIURI_Debug); + define_debug_struct!( + DebugReferrerInfo, + nsIReferrerInfo, + Gecko_nsIReferrerInfo_Debug + ); + + formatter + .debug_struct("URLExtraData") + .field("chrome_rules_enabled", &self.chrome_rules_enabled()) + .field("base", &DebugURI(self.as_ref().mBaseURI.raw())) + .field( + "referrer", + &DebugReferrerInfo(self.as_ref().mReferrerInfo.raw()), + ) + .finish() + } +} + +// XXX We probably need to figure out whether we should mark Eq here. +// It is currently marked so because properties::UnparsedValue wants Eq. +#[cfg(feature = "gecko")] +impl Eq for UrlExtraData {} + +/// A CSS rule. +/// +/// TODO(emilio): Lots of spec links should be around. +#[derive(Clone, Debug, ToShmem)] +#[allow(missing_docs)] +pub enum CssRule { + // No Charset here, CSSCharsetRule has been removed from CSSOM + // https://drafts.csswg.org/cssom/#changes-from-5-december-2013 + Namespace(Arc<NamespaceRule>), + Import(Arc<Locked<ImportRule>>), + Style(Arc<Locked<StyleRule>>), + Media(Arc<MediaRule>), + Container(Arc<ContainerRule>), + FontFace(Arc<Locked<FontFaceRule>>), + FontFeatureValues(Arc<FontFeatureValuesRule>), + FontPaletteValues(Arc<FontPaletteValuesRule>), + CounterStyle(Arc<Locked<CounterStyleRule>>), + Viewport(Arc<ViewportRule>), + Keyframes(Arc<Locked<KeyframesRule>>), + Supports(Arc<SupportsRule>), + Page(Arc<Locked<PageRule>>), + Property(Arc<PropertyRule>), + Document(Arc<DocumentRule>), + LayerBlock(Arc<LayerBlockRule>), + LayerStatement(Arc<LayerStatementRule>), +} + +impl CssRule { + /// Measure heap usage. + #[cfg(feature = "gecko")] + fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { + match *self { + // Not all fields are currently fully measured. Extra measurement + // may be added later. + CssRule::Namespace(_) => 0, + + // We don't need to measure ImportRule::stylesheet because we measure + // it on the C++ side in the child list of the ServoStyleSheet. + CssRule::Import(_) => 0, + + CssRule::Style(ref lock) => { + lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops) + }, + CssRule::Media(ref arc) => { + arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops) + }, + CssRule::Container(ref arc) => { + arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops) + }, + CssRule::FontFace(_) => 0, + CssRule::FontFeatureValues(_) => 0, + CssRule::FontPaletteValues(_) => 0, + CssRule::CounterStyle(_) => 0, + CssRule::Viewport(_) => 0, + CssRule::Keyframes(_) => 0, + CssRule::Supports(ref arc) => { + arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops) + }, + CssRule::Page(ref lock) => { + lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops) + }, + CssRule::Property(ref rule) => { + rule.unconditional_shallow_size_of(ops) + rule.size_of(guard, ops) + }, + CssRule::Document(ref arc) => { + arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops) + }, + // TODO(emilio): Add memory reporting for these rules. + CssRule::LayerBlock(_) | CssRule::LayerStatement(_) => 0, + } + } +} + +/// https://drafts.csswg.org/cssom-1/#dom-cssrule-type +#[allow(missing_docs)] +#[derive(Clone, Copy, Debug, Eq, FromPrimitive, PartialEq)] +#[repr(u8)] +pub enum CssRuleType { + // https://drafts.csswg.org/cssom/#the-cssrule-interface + Style = 1, + // Charset = 2, // Historical + Import = 3, + Media = 4, + FontFace = 5, + Page = 6, + // https://drafts.csswg.org/css-animations-1/#interface-cssrule-idl + Keyframes = 7, + Keyframe = 8, + // https://drafts.csswg.org/cssom/#the-cssrule-interface + // Margin = 9, // Not implemented yet. + Namespace = 10, + // https://drafts.csswg.org/css-counter-styles-3/#extentions-to-cssrule-interface + CounterStyle = 11, + // https://drafts.csswg.org/css-conditional-3/#extentions-to-cssrule-interface + Supports = 12, + // https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#extentions-to-cssrule-interface + Document = 13, + // https://drafts.csswg.org/css-fonts/#om-fontfeaturevalues + FontFeatureValues = 14, + // https://drafts.csswg.org/css-device-adapt/#css-rule-interface + Viewport = 15, + // After viewport, all rules should return 0 from the API, but we still need + // a constant somewhere. + LayerBlock = 16, + LayerStatement = 17, + Container = 18, + FontPaletteValues = 19, + // 20 is an arbitrary number to use for Property. + Property = 20, +} + +impl CssRuleType { + /// Returns a bit that identifies this rule type. + #[inline] + pub const fn bit(self) -> u32 { + 1 << self as u32 + } +} + +/// Set of rule types. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct CssRuleTypes(u32); + +impl From<CssRuleType> for CssRuleTypes { + fn from(ty: CssRuleType) -> Self { + Self(ty.bit()) + } +} + +impl CssRuleTypes { + /// Returns whether the rule is in the current set. + #[inline] + pub fn contains(self, ty: CssRuleType) -> bool { + self.0 & ty.bit() != 0 + } + + /// Returns all the rules specified in the set. + pub fn bits(self) -> u32 { + self.0 + } + + /// Inserts a rule type into the set. + #[inline] + pub fn insert(&mut self, ty: CssRuleType) { + self.0 |= ty.bit() + } +} + +#[allow(missing_docs)] +pub enum RulesMutateError { + Syntax, + IndexSize, + HierarchyRequest, + InvalidState, +} + +impl CssRule { + /// Returns the CSSOM rule type of this rule. + pub fn rule_type(&self) -> CssRuleType { + match *self { + CssRule::Style(_) => CssRuleType::Style, + CssRule::Import(_) => CssRuleType::Import, + CssRule::Media(_) => CssRuleType::Media, + CssRule::FontFace(_) => CssRuleType::FontFace, + CssRule::FontFeatureValues(_) => CssRuleType::FontFeatureValues, + CssRule::FontPaletteValues(_) => CssRuleType::FontPaletteValues, + CssRule::CounterStyle(_) => CssRuleType::CounterStyle, + CssRule::Keyframes(_) => CssRuleType::Keyframes, + CssRule::Namespace(_) => CssRuleType::Namespace, + CssRule::Viewport(_) => CssRuleType::Viewport, + CssRule::Supports(_) => CssRuleType::Supports, + CssRule::Page(_) => CssRuleType::Page, + CssRule::Property(_) => CssRuleType::Property, + CssRule::Document(_) => CssRuleType::Document, + CssRule::LayerBlock(_) => CssRuleType::LayerBlock, + CssRule::LayerStatement(_) => CssRuleType::LayerStatement, + CssRule::Container(_) => CssRuleType::Container, + } + } + + /// Parse a CSS rule. + /// + /// Returns a parsed CSS rule and the final state of the parser. + /// + /// Input state is None for a nested rule + pub fn parse( + css: &str, + insert_rule_context: InsertRuleContext, + parent_stylesheet_contents: &StylesheetContents, + shared_lock: &SharedRwLock, + state: State, + loader: Option<&dyn StylesheetLoader>, + allow_import_rules: AllowImportRules, + ) -> Result<Self, RulesMutateError> { + let url_data = parent_stylesheet_contents.url_data.read(); + let namespaces = parent_stylesheet_contents.namespaces.read(); + let context = ParserContext::new( + parent_stylesheet_contents.origin, + &url_data, + None, + ParsingMode::DEFAULT, + parent_stylesheet_contents.quirks_mode, + Cow::Borrowed(&*namespaces), + None, + None, + ); + + let mut input = ParserInput::new(css); + let mut input = Parser::new(&mut input); + + // nested rules are in the body state + let mut rule_parser = TopLevelRuleParser { + context, + shared_lock: &shared_lock, + loader, + state, + dom_error: None, + insert_rule_context: Some(insert_rule_context), + allow_import_rules, + declaration_parser_state: Default::default(), + rules: Default::default(), + }; + + match parse_one_rule(&mut input, &mut rule_parser) { + Ok(_) => Ok(rule_parser.rules.pop().unwrap()), + Err(_) => Err(rule_parser.dom_error.unwrap_or(RulesMutateError::Syntax)), + } + } +} + +impl DeepCloneWithLock for CssRule { + /// Deep clones this CssRule. + fn deep_clone_with_lock( + &self, + lock: &SharedRwLock, + guard: &SharedRwLockReadGuard, + params: &DeepCloneParams, + ) -> CssRule { + match *self { + CssRule::Namespace(ref arc) => CssRule::Namespace(arc.clone()), + CssRule::Import(ref arc) => { + let rule = arc + .read_with(guard) + .deep_clone_with_lock(lock, guard, params); + CssRule::Import(Arc::new(lock.wrap(rule))) + }, + CssRule::Style(ref arc) => { + let rule = arc.read_with(guard); + CssRule::Style(Arc::new( + lock.wrap(rule.deep_clone_with_lock(lock, guard, params)), + )) + }, + CssRule::Container(ref arc) => { + CssRule::Container(Arc::new(arc.deep_clone_with_lock(lock, guard, params))) + }, + CssRule::Media(ref arc) => { + CssRule::Media(Arc::new(arc.deep_clone_with_lock(lock, guard, params))) + }, + CssRule::FontFace(ref arc) => { + let rule = arc.read_with(guard); + CssRule::FontFace(Arc::new(lock.wrap(rule.clone()))) + }, + CssRule::FontFeatureValues(ref arc) => CssRule::FontFeatureValues(arc.clone()), + CssRule::FontPaletteValues(ref arc) => CssRule::FontPaletteValues(arc.clone()), + CssRule::CounterStyle(ref arc) => { + let rule = arc.read_with(guard); + CssRule::CounterStyle(Arc::new(lock.wrap(rule.clone()))) + }, + CssRule::Viewport(ref arc) => CssRule::Viewport(arc.clone()), + CssRule::Keyframes(ref arc) => { + let rule = arc.read_with(guard); + CssRule::Keyframes(Arc::new( + lock.wrap(rule.deep_clone_with_lock(lock, guard, params)), + )) + }, + CssRule::Supports(ref arc) => { + CssRule::Supports(Arc::new(arc.deep_clone_with_lock(lock, guard, params))) + }, + CssRule::Page(ref arc) => { + let rule = arc.read_with(guard); + CssRule::Page(Arc::new( + lock.wrap(rule.deep_clone_with_lock(lock, guard, params)), + )) + }, + CssRule::Property(ref arc) => { + // @property rules are immutable, so we don't need any of the `Locked` + // shenanigans, actually, and can just share the rule. + CssRule::Property(arc.clone()) + }, + CssRule::Document(ref arc) => { + CssRule::Document(Arc::new(arc.deep_clone_with_lock(lock, guard, params))) + }, + CssRule::LayerStatement(ref arc) => CssRule::LayerStatement(arc.clone()), + CssRule::LayerBlock(ref arc) => { + CssRule::LayerBlock(Arc::new(arc.deep_clone_with_lock(lock, guard, params))) + }, + } + } +} + +impl ToCssWithGuard for CssRule { + // https://drafts.csswg.org/cssom/#serialize-a-css-rule + fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { + match *self { + CssRule::Namespace(ref rule) => rule.to_css(guard, dest), + CssRule::Import(ref lock) => lock.read_with(guard).to_css(guard, dest), + CssRule::Style(ref lock) => lock.read_with(guard).to_css(guard, dest), + CssRule::FontFace(ref lock) => lock.read_with(guard).to_css(guard, dest), + CssRule::FontFeatureValues(ref rule) => rule.to_css(guard, dest), + CssRule::FontPaletteValues(ref rule) => rule.to_css(guard, dest), + CssRule::CounterStyle(ref lock) => lock.read_with(guard).to_css(guard, dest), + CssRule::Viewport(ref rule) => rule.to_css(guard, dest), + CssRule::Keyframes(ref lock) => lock.read_with(guard).to_css(guard, dest), + CssRule::Media(ref rule) => rule.to_css(guard, dest), + CssRule::Supports(ref rule) => rule.to_css(guard, dest), + CssRule::Page(ref lock) => lock.read_with(guard).to_css(guard, dest), + CssRule::Property(ref rule) => rule.to_css(guard, dest), + CssRule::Document(ref rule) => rule.to_css(guard, dest), + CssRule::LayerBlock(ref rule) => rule.to_css(guard, dest), + CssRule::LayerStatement(ref rule) => rule.to_css(guard, dest), + CssRule::Container(ref rule) => rule.to_css(guard, dest), + } + } +} |