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 margin_rule;
mod media_rule;
mod namespace_rule;
mod nested_declarations_rule;
pub mod origin;
mod page_rule;
pub mod position_try_rule;
mod property_rule;
mod rule_list;
mod rule_parser;
mod rules_iterator;
pub mod scope_rule;
mod starting_style_rule;
mod style_rule;
mod stylesheet;
pub mod supports_rule;
#[cfg(feature = "gecko")]
use crate::gecko_bindings::sugar::refptr::RefCounted;
#[cfg(feature = "gecko")]
use crate::gecko_bindings::{bindings, structs};
use crate::parser::{NestingContext, ParserContext};
use crate::properties::{parse_property_declaration_list, PropertyDeclarationBlock};
use crate::shared_lock::{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::{self, Write};
#[cfg(feature = "gecko")]
use std::mem::{self, ManuallyDrop};
use style_traits::ParsingMode;
use to_shmem::{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::margin_rule::{MarginRule, MarginRuleType};
pub use self::media_rule::MediaRule;
pub use self::namespace_rule::NamespaceRule;
pub use self::nested_declarations_rule::NestedDeclarationsRule;
pub use self::origin::{Origin, OriginSet, OriginSetIterator, PerOrigin, PerOriginIter};
pub use self::page_rule::{PagePseudoClassFlags, PageRule, PageSelector, PageSelectors};
pub use self::position_try_rule::PositionTryRule;
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::scope_rule::ScopeRule;
pub use self::starting_style_rule::StartingStyleRule;
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;
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)]
pub enum CorsMode {
None,
Anonymous,
}
#[cfg(feature = "gecko")]
#[derive(MallocSizeOf, PartialEq)]
#[repr(C)]
pub struct UrlExtraData(usize);
#[cfg(feature = "servo")]
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
pub struct UrlExtraData(#[ignore_malloc_size_of = "Arc"] pub Arc<::url::Url>);
#[cfg(feature = "servo")]
impl UrlExtraData {
pub fn chrome_rules_enabled(&self) -> bool {
self.0.scheme() == "chrome"
}
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
#[cfg(feature = "servo")]
impl From<::url::Url> for UrlExtraData {
fn from(url: ::url::Url) -> Self {
Self(Arc::new(url))
}
}
#[cfg(not(feature = "gecko"))]
impl ToShmem for UrlExtraData {
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
unimplemented!("If servo wants to share stylesheets across processes, ToShmem for Url must be implemented");
}
}
#[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) {
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 {
std::ptr::addr_of!(structs::URLExtraData_sShared)
.as_ref()
.unwrap()
};
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 {
pub fn new(ptr: *mut structs::URLExtraData) -> UrlExtraData {
unsafe {
(*ptr).addref();
}
UrlExtraData(ptr as usize)
}
#[inline]
pub fn chrome_rules_enabled(&self) -> bool {
self.as_ref().mChromeRulesEnabled
}
#[inline]
pub unsafe fn from_ptr_ref(ptr: &*mut structs::URLExtraData) -> &Self {
mem::transmute(ptr)
}
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()
}
}
#[cfg(feature = "gecko")]
impl Eq for UrlExtraData {}
fn style_or_page_rule_to_css(
rules: Option<&Arc<Locked<CssRules>>>,
block: &Locked<PropertyDeclarationBlock>,
guard: &SharedRwLockReadGuard,
dest: &mut CssStringWriter,
) -> fmt::Result {
dest.write_char('{')?;
let declaration_block = block.read_with(guard);
let has_declarations = !declaration_block.declarations().is_empty();
if let Some(ref rules) = rules {
let rules = rules.read_with(guard);
if !rules.is_empty() {
if has_declarations {
dest.write_str("\n ")?;
declaration_block.to_css(dest)?;
}
return rules.to_css_block_without_opening(guard, dest);
}
}
if has_declarations {
dest.write_char(' ')?;
declaration_block.to_css(dest)?;
}
dest.write_str(" }")
}
#[derive(Clone, Debug, ToShmem)]
#[allow(missing_docs)]
pub enum CssRule {
Style(Arc<Locked<StyleRule>>),
Namespace(Arc<NamespaceRule>),
Import(Arc<Locked<ImportRule>>),
Media(Arc<MediaRule>),
Container(Arc<ContainerRule>),
FontFace(Arc<Locked<FontFaceRule>>),
FontFeatureValues(Arc<FontFeatureValuesRule>),
FontPaletteValues(Arc<FontPaletteValuesRule>),
CounterStyle(Arc<Locked<CounterStyleRule>>),
Keyframes(Arc<Locked<KeyframesRule>>),
Margin(Arc<MarginRule>),
Supports(Arc<SupportsRule>),
Page(Arc<Locked<PageRule>>),
Property(Arc<PropertyRule>),
Document(Arc<DocumentRule>),
LayerBlock(Arc<LayerBlockRule>),
LayerStatement(Arc<LayerStatementRule>),
Scope(Arc<ScopeRule>),
StartingStyle(Arc<StartingStyleRule>),
PositionTry(Arc<Locked<PositionTryRule>>),
NestedDeclarations(Arc<Locked<NestedDeclarationsRule>>),
}
impl CssRule {
#[cfg(feature = "gecko")]
fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
match *self {
CssRule::Namespace(_) => 0,
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::Keyframes(_) => 0,
CssRule::Margin(ref arc) => {
arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)
},
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)
},
CssRule::StartingStyle(ref arc) => {
arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)
},
CssRule::LayerBlock(_) | CssRule::LayerStatement(_) => 0,
CssRule::Scope(ref rule) => {
rule.unconditional_shallow_size_of(ops) + rule.size_of(guard, ops)
},
CssRule::PositionTry(ref lock) => {
lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops)
},
CssRule::NestedDeclarations(ref lock) => {
lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops)
},
}
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, Eq, FromPrimitive, PartialEq)]
#[repr(u8)]
pub enum CssRuleType {
Style = 1,
Import = 3,
Media = 4,
FontFace = 5,
Page = 6,
Keyframes = 7,
Keyframe = 8,
Margin = 9,
Namespace = 10,
CounterStyle = 11,
Supports = 12,
Document = 13,
FontFeatureValues = 14,
LayerBlock = 16,
LayerStatement = 17,
Container = 18,
FontPaletteValues = 19,
Property = 20,
Scope = 21,
StartingStyle = 22,
PositionTry = 23,
NestedDeclarations = 24,
}
impl CssRuleType {
#[inline]
pub const fn bit(self) -> u32 {
1 << self as u32
}
}
#[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 {
pub const IMPORTANT_FORBIDDEN: Self = Self(CssRuleType::PositionTry.bit() | CssRuleType::Keyframe.bit());
#[inline]
pub fn contains(self, ty: CssRuleType) -> bool {
self.0 & ty.bit() != 0
}
#[inline]
pub fn bits(self) -> u32 {
self.0
}
#[inline]
pub fn from_bits(bits: u32) -> Self {
Self(bits)
}
#[inline]
pub fn is_empty(self) -> bool {
self.0 == 0
}
#[inline]
pub fn insert(&mut self, ty: CssRuleType) {
self.0 |= ty.bit()
}
#[inline]
pub fn intersects(self, other: Self) -> bool {
self.0 & other.0 != 0
}
}
#[allow(missing_docs)]
pub enum RulesMutateError {
Syntax,
IndexSize,
HierarchyRequest,
InvalidState,
}
impl CssRule {
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::Margin(_) => CssRuleType::Margin,
CssRule::Namespace(_) => CssRuleType::Namespace,
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,
CssRule::Scope(_) => CssRuleType::Scope,
CssRule::StartingStyle(_) => CssRuleType::StartingStyle,
CssRule::PositionTry(_) => CssRuleType::PositionTry,
CssRule::NestedDeclarations(_) => CssRuleType::NestedDeclarations,
}
}
pub fn parse(
css: &str,
insert_rule_context: InsertRuleContext,
parent_stylesheet_contents: &StylesheetContents,
shared_lock: &SharedRwLock,
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 mut context = ParserContext::new(
parent_stylesheet_contents.origin,
&url_data,
None,
ParsingMode::DEFAULT,
parent_stylesheet_contents.quirks_mode,
Cow::Borrowed(&*namespaces),
None,
None,
);
context.nesting_context = NestingContext::new(
insert_rule_context.containing_rule_types,
insert_rule_context.parse_relative_rule_type,
);
let state = if !insert_rule_context.containing_rule_types.is_empty() {
State::Body
} else if insert_rule_context.index == 0 {
State::Start
} else {
let index = insert_rule_context.index;
insert_rule_context.max_rule_state_at_index(index - 1)
};
let mut input = ParserInput::new(css);
let mut input = Parser::new(&mut input);
let mut 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(),
first_declaration_block: Default::default(),
wants_first_declaration_block: false,
error_reporting_state: Default::default(),
rules: Default::default(),
};
if input.try_parse(|input| parse_one_rule(input, &mut parser)).is_ok() {
return Ok(parser.rules.pop().unwrap());
}
let error = parser.dom_error.take().unwrap_or(RulesMutateError::Syntax);
if matches!(error, RulesMutateError::Syntax) && parser.can_parse_declarations() {
let declarations = parse_property_declaration_list(&parser.context, &mut input, &[]);
if !declarations.is_empty() {
return Ok(CssRule::NestedDeclarations(Arc::new(parser.shared_lock.wrap(NestedDeclarationsRule {
block: Arc::new(parser.shared_lock.wrap(declarations)),
source_location: input.current_source_location(),
}))));
}
}
Err(error)
}
}
impl DeepCloneWithLock for CssRule {
fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> 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);
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))))
},
CssRule::Container(ref arc) => {
CssRule::Container(Arc::new(arc.deep_clone_with_lock(lock, guard)))
},
CssRule::Media(ref arc) => {
CssRule::Media(Arc::new(arc.deep_clone_with_lock(lock, guard)))
},
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::Keyframes(ref arc) => {
let rule = arc.read_with(guard);
CssRule::Keyframes(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard))))
},
CssRule::Margin(ref arc) => {
CssRule::Margin(Arc::new(arc.deep_clone_with_lock(lock, guard)))
},
CssRule::Supports(ref arc) => {
CssRule::Supports(Arc::new(arc.deep_clone_with_lock(lock, guard)))
},
CssRule::Page(ref arc) => {
let rule = arc.read_with(guard);
CssRule::Page(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard))))
},
CssRule::Property(ref arc) => {
CssRule::Property(arc.clone())
},
CssRule::Document(ref arc) => {
CssRule::Document(Arc::new(arc.deep_clone_with_lock(lock, guard)))
},
CssRule::LayerStatement(ref arc) => CssRule::LayerStatement(arc.clone()),
CssRule::LayerBlock(ref arc) => {
CssRule::LayerBlock(Arc::new(arc.deep_clone_with_lock(lock, guard)))
},
CssRule::Scope(ref arc) => {
CssRule::Scope(Arc::new(arc.deep_clone_with_lock(lock, guard)))
},
CssRule::StartingStyle(ref arc) => {
CssRule::StartingStyle(Arc::new(arc.deep_clone_with_lock(lock, guard)))
},
CssRule::PositionTry(ref arc) => {
let rule = arc.read_with(guard);
CssRule::PositionTry(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard))))
},
CssRule::NestedDeclarations(ref arc) => {
let decls = arc.read_with(guard);
CssRule::NestedDeclarations(Arc::new(lock.wrap(decls.clone())))
},
}
}
}
impl ToCssWithGuard for CssRule {
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::Keyframes(ref lock) => lock.read_with(guard).to_css(guard, dest),
CssRule::Margin(ref rule) => rule.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),
CssRule::Scope(ref rule) => rule.to_css(guard, dest),
CssRule::StartingStyle(ref rule) => rule.to_css(guard, dest),
CssRule::PositionTry(ref lock) => lock.read_with(guard).to_css(guard, dest),
CssRule::NestedDeclarations(ref lock) => lock.read_with(guard).to_css(guard, dest),
}
}
}