1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 //! Style sheets and their CSS rules. 6 7 mod cascading_at_rule; 8 mod counter_style_rule; 9 mod document_rule; 10 mod font_face_rule; 11 pub mod font_feature_values_rule; 12 pub mod import_rule; 13 pub mod keyframes_rule; 14 mod loader; 15 mod media_rule; 16 mod namespace_rule; 17 pub mod origin; 18 mod page_rule; 19 mod rule_list; 20 mod rule_parser; 21 mod rules_iterator; 22 mod style_rule; 23 mod stylesheet; 24 pub mod supports_rule; 25 pub mod viewport_rule; 26 27 #[cfg(feature = "gecko")] 28 use crate::gecko_bindings::sugar::refptr::RefCounted; 29 #[cfg(feature = "gecko")] 30 use crate::gecko_bindings::{bindings, structs}; 31 use crate::parser::ParserContext; 32 use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked}; 33 use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; 34 use crate::str::CssStringWriter; 35 use cssparser::{parse_one_rule, Parser, ParserInput}; 36 #[cfg(feature = "gecko")] 37 use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; 38 use servo_arc::Arc; 39 use std::fmt; 40 #[cfg(feature = "gecko")] 41 use std::mem::{self, ManuallyDrop}; 42 use style_traits::ParsingMode; 43 #[cfg(feature = "gecko")] 44 use to_shmem::{self, SharedMemoryBuilder, ToShmem}; 45 46 pub use self::counter_style_rule::CounterStyleRule; 47 pub use self::document_rule::DocumentRule; 48 pub use self::font_face_rule::FontFaceRule; 49 pub use self::font_feature_values_rule::FontFeatureValuesRule; 50 pub use self::import_rule::ImportRule; 51 pub use self::keyframes_rule::KeyframesRule; 52 pub use self::loader::StylesheetLoader; 53 pub use self::media_rule::MediaRule; 54 pub use self::namespace_rule::NamespaceRule; 55 pub use self::origin::{Origin, OriginSet, OriginSetIterator, PerOrigin, PerOriginIter}; 56 pub use self::page_rule::PageRule; 57 pub use self::rule_list::{CssRules, CssRulesHelpers}; 58 pub use self::rule_parser::{InsertRuleContext, State, TopLevelRuleParser}; 59 pub use self::rules_iterator::{AllRules, EffectiveRules}; 60 pub use self::rules_iterator::{ 61 EffectiveRulesIterator, NestedRuleIterationCondition, RulesIterator, 62 }; 63 pub use self::style_rule::StyleRule; 64 pub use self::stylesheet::{AllowImportRules, SanitizationData, SanitizationKind}; 65 pub use self::stylesheet::{DocumentStyleSheet, Namespaces, Stylesheet}; 66 pub use self::stylesheet::{StylesheetContents, StylesheetInDocument, UserAgentStylesheets}; 67 pub use self::supports_rule::SupportsRule; 68 pub use self::viewport_rule::ViewportRule; 69 70 /// The CORS mode used for a CSS load. 71 #[repr(u8)] 72 #[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)] 73 pub enum CorsMode { 74 /// No CORS mode, so cross-origin loads can be done. 75 None, 76 /// Anonymous CORS request. 77 Anonymous, 78 } 79 80 /// Extra data that the backend may need to resolve url values. 81 /// 82 /// If the usize's lowest bit is 0, then this is a strong reference to a 83 /// structs::URLExtraData object. 84 /// 85 /// Otherwise, shifting the usize's bits the right by one gives the 86 /// UserAgentStyleSheetID value corresponding to the style sheet whose 87 /// URLExtraData this is, which is stored in URLExtraData_sShared. We don't 88 /// hold a strong reference to that object from here, but we rely on that 89 /// array's objects being held alive until shutdown. 90 /// 91 /// We use this packed representation rather than an enum so that 92 /// `from_ptr_ref` can work. 93 #[cfg(feature = "gecko")] 94 #[derive(PartialEq)] 95 #[repr(C)] 96 pub struct UrlExtraData(usize); 97 98 /// Extra data that the backend may need to resolve url values. 99 #[cfg(not(feature = "gecko"))] 100 pub type UrlExtraData = ::servo_url::ServoUrl; 101 102 #[cfg(feature = "gecko")] 103 impl Clone for UrlExtraData { clone(&self) -> UrlExtraData104 fn clone(&self) -> UrlExtraData { 105 UrlExtraData::new(self.ptr()) 106 } 107 } 108 109 #[cfg(feature = "gecko")] 110 impl Drop for UrlExtraData { drop(&mut self)111 fn drop(&mut self) { 112 // No need to release when we have an index into URLExtraData_sShared. 113 if self.0 & 1 == 0 { 114 unsafe { 115 self.as_ref().release(); 116 } 117 } 118 } 119 } 120 121 #[cfg(feature = "gecko")] 122 impl ToShmem for UrlExtraData { to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self>123 fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> { 124 if self.0 & 1 == 0 { 125 let shared_extra_datas = unsafe { &structs::URLExtraData_sShared }; 126 let self_ptr = self.as_ref() as *const _ as *mut _; 127 let sheet_id = shared_extra_datas 128 .iter() 129 .position(|r| r.mRawPtr == self_ptr); 130 let sheet_id = match sheet_id { 131 Some(id) => id, 132 None => { 133 return Err(String::from( 134 "ToShmem failed for UrlExtraData: expected sheet's URLExtraData to be in \ 135 URLExtraData::sShared", 136 )); 137 }, 138 }; 139 Ok(ManuallyDrop::new(UrlExtraData((sheet_id << 1) | 1))) 140 } else { 141 Ok(ManuallyDrop::new(UrlExtraData(self.0))) 142 } 143 } 144 } 145 146 #[cfg(feature = "gecko")] 147 impl UrlExtraData { 148 /// Create a new UrlExtraData wrapping a pointer to the specified Gecko 149 /// URLExtraData object. new(ptr: *mut structs::URLExtraData) -> UrlExtraData150 pub fn new(ptr: *mut structs::URLExtraData) -> UrlExtraData { 151 unsafe { 152 (*ptr).addref(); 153 } 154 UrlExtraData(ptr as usize) 155 } 156 157 /// True if this URL scheme is chrome. 158 #[inline] chrome_rules_enabled(&self) -> bool159 pub fn chrome_rules_enabled(&self) -> bool { 160 self.as_ref().mChromeRulesEnabled 161 } 162 163 /// Create a reference to this `UrlExtraData` from a reference to pointer. 164 /// 165 /// The pointer must be valid and non null. 166 /// 167 /// This method doesn't touch refcount. 168 #[inline] from_ptr_ref(ptr: &*mut structs::URLExtraData) -> &Self169 pub unsafe fn from_ptr_ref(ptr: &*mut structs::URLExtraData) -> &Self { 170 mem::transmute(ptr) 171 } 172 173 /// Returns a pointer to the Gecko URLExtraData object. ptr(&self) -> *mut structs::URLExtraData174 pub fn ptr(&self) -> *mut structs::URLExtraData { 175 if self.0 & 1 == 0 { 176 self.0 as *mut structs::URLExtraData 177 } else { 178 unsafe { 179 let sheet_id = self.0 >> 1; 180 structs::URLExtraData_sShared[sheet_id].mRawPtr 181 } 182 } 183 } 184 as_ref(&self) -> &structs::URLExtraData185 fn as_ref(&self) -> &structs::URLExtraData { 186 unsafe { &*(self.ptr() as *const structs::URLExtraData) } 187 } 188 } 189 190 #[cfg(feature = "gecko")] 191 impl fmt::Debug for UrlExtraData { fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result192 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 193 macro_rules! define_debug_struct { 194 ($struct_name:ident, $gecko_class:ident, $debug_fn:ident) => { 195 struct $struct_name(*mut structs::$gecko_class); 196 impl fmt::Debug for $struct_name { 197 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 198 use nsstring::nsCString; 199 let mut spec = nsCString::new(); 200 unsafe { 201 bindings::$debug_fn(self.0, &mut spec); 202 } 203 spec.fmt(formatter) 204 } 205 } 206 }; 207 } 208 209 define_debug_struct!(DebugURI, nsIURI, Gecko_nsIURI_Debug); 210 define_debug_struct!( 211 DebugReferrerInfo, 212 nsIReferrerInfo, 213 Gecko_nsIReferrerInfo_Debug 214 ); 215 216 formatter 217 .debug_struct("URLExtraData") 218 .field("chrome_rules_enabled", &self.chrome_rules_enabled()) 219 .field( 220 "base", 221 &DebugURI(self.as_ref().mBaseURI.raw::<structs::nsIURI>()), 222 ) 223 .field( 224 "referrer", 225 &DebugReferrerInfo( 226 self.as_ref() 227 .mReferrerInfo 228 .raw::<structs::nsIReferrerInfo>(), 229 ), 230 ) 231 .finish() 232 } 233 } 234 235 // XXX We probably need to figure out whether we should mark Eq here. 236 // It is currently marked so because properties::UnparsedValue wants Eq. 237 #[cfg(feature = "gecko")] 238 impl Eq for UrlExtraData {} 239 240 /// A CSS rule. 241 /// 242 /// TODO(emilio): Lots of spec links should be around. 243 #[derive(Clone, Debug, ToShmem)] 244 #[allow(missing_docs)] 245 pub enum CssRule { 246 // No Charset here, CSSCharsetRule has been removed from CSSOM 247 // https://drafts.csswg.org/cssom/#changes-from-5-december-2013 248 Namespace(Arc<Locked<NamespaceRule>>), 249 Import(Arc<Locked<ImportRule>>), 250 Style(Arc<Locked<StyleRule>>), 251 Media(Arc<Locked<MediaRule>>), 252 FontFace(Arc<Locked<FontFaceRule>>), 253 FontFeatureValues(Arc<Locked<FontFeatureValuesRule>>), 254 CounterStyle(Arc<Locked<CounterStyleRule>>), 255 Viewport(Arc<Locked<ViewportRule>>), 256 Keyframes(Arc<Locked<KeyframesRule>>), 257 Supports(Arc<Locked<SupportsRule>>), 258 Page(Arc<Locked<PageRule>>), 259 Document(Arc<Locked<DocumentRule>>), 260 } 261 262 impl CssRule { 263 /// Measure heap usage. 264 #[cfg(feature = "gecko")] size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize265 fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { 266 match *self { 267 // Not all fields are currently fully measured. Extra measurement 268 // may be added later. 269 CssRule::Namespace(_) => 0, 270 271 // We don't need to measure ImportRule::stylesheet because we measure 272 // it on the C++ side in the child list of the ServoStyleSheet. 273 CssRule::Import(_) => 0, 274 275 CssRule::Style(ref lock) => { 276 lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops) 277 }, 278 279 CssRule::Media(ref lock) => { 280 lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops) 281 }, 282 283 CssRule::FontFace(_) => 0, 284 CssRule::FontFeatureValues(_) => 0, 285 CssRule::CounterStyle(_) => 0, 286 CssRule::Viewport(_) => 0, 287 CssRule::Keyframes(_) => 0, 288 289 CssRule::Supports(ref lock) => { 290 lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops) 291 }, 292 293 CssRule::Page(ref lock) => { 294 lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops) 295 }, 296 297 CssRule::Document(ref lock) => { 298 lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops) 299 }, 300 } 301 } 302 } 303 304 #[allow(missing_docs)] 305 #[derive(Clone, Copy, Debug, Eq, FromPrimitive, PartialEq)] 306 pub enum CssRuleType { 307 // https://drafts.csswg.org/cssom/#the-cssrule-interface 308 Style = 1, 309 Charset = 2, 310 Import = 3, 311 Media = 4, 312 FontFace = 5, 313 Page = 6, 314 // https://drafts.csswg.org/css-animations-1/#interface-cssrule-idl 315 Keyframes = 7, 316 Keyframe = 8, 317 // https://drafts.csswg.org/cssom/#the-cssrule-interface 318 Margin = 9, 319 Namespace = 10, 320 // https://drafts.csswg.org/css-counter-styles-3/#extentions-to-cssrule-interface 321 CounterStyle = 11, 322 // https://drafts.csswg.org/css-conditional-3/#extentions-to-cssrule-interface 323 Supports = 12, 324 // https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#extentions-to-cssrule-interface 325 Document = 13, 326 // https://drafts.csswg.org/css-fonts-3/#om-fontfeaturevalues 327 FontFeatureValues = 14, 328 // https://drafts.csswg.org/css-device-adapt/#css-rule-interface 329 Viewport = 15, 330 } 331 332 #[allow(missing_docs)] 333 pub enum RulesMutateError { 334 Syntax, 335 IndexSize, 336 HierarchyRequest, 337 InvalidState, 338 } 339 340 impl CssRule { 341 /// Returns the CSSOM rule type of this rule. rule_type(&self) -> CssRuleType342 pub fn rule_type(&self) -> CssRuleType { 343 match *self { 344 CssRule::Style(_) => CssRuleType::Style, 345 CssRule::Import(_) => CssRuleType::Import, 346 CssRule::Media(_) => CssRuleType::Media, 347 CssRule::FontFace(_) => CssRuleType::FontFace, 348 CssRule::FontFeatureValues(_) => CssRuleType::FontFeatureValues, 349 CssRule::CounterStyle(_) => CssRuleType::CounterStyle, 350 CssRule::Keyframes(_) => CssRuleType::Keyframes, 351 CssRule::Namespace(_) => CssRuleType::Namespace, 352 CssRule::Viewport(_) => CssRuleType::Viewport, 353 CssRule::Supports(_) => CssRuleType::Supports, 354 CssRule::Page(_) => CssRuleType::Page, 355 CssRule::Document(_) => CssRuleType::Document, 356 } 357 } 358 rule_state(&self) -> State359 fn rule_state(&self) -> State { 360 match *self { 361 // CssRule::Charset(..) => State::Start, 362 CssRule::Import(..) => State::Imports, 363 CssRule::Namespace(..) => State::Namespaces, 364 _ => State::Body, 365 } 366 } 367 368 /// Parse a CSS rule. 369 /// 370 /// Returns a parsed CSS rule and the final state of the parser. 371 /// 372 /// Input state is None for a nested rule 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>373 pub fn parse( 374 css: &str, 375 insert_rule_context: InsertRuleContext, 376 parent_stylesheet_contents: &StylesheetContents, 377 shared_lock: &SharedRwLock, 378 state: State, 379 loader: Option<&dyn StylesheetLoader>, 380 allow_import_rules: AllowImportRules, 381 ) -> Result<Self, RulesMutateError> { 382 let url_data = parent_stylesheet_contents.url_data.read(); 383 let context = ParserContext::new( 384 parent_stylesheet_contents.origin, 385 &url_data, 386 None, 387 ParsingMode::DEFAULT, 388 parent_stylesheet_contents.quirks_mode, 389 None, 390 None, 391 ); 392 393 let mut input = ParserInput::new(css); 394 let mut input = Parser::new(&mut input); 395 396 let mut guard = parent_stylesheet_contents.namespaces.write(); 397 398 // nested rules are in the body state 399 let mut rule_parser = TopLevelRuleParser { 400 context, 401 shared_lock: &shared_lock, 402 loader, 403 state, 404 dom_error: None, 405 namespaces: &mut *guard, 406 insert_rule_context: Some(insert_rule_context), 407 allow_import_rules, 408 }; 409 410 match parse_one_rule(&mut input, &mut rule_parser) { 411 Ok((_, rule)) => Ok(rule), 412 Err(_) => Err(rule_parser.dom_error.unwrap_or(RulesMutateError::Syntax)), 413 } 414 } 415 } 416 417 impl DeepCloneWithLock for CssRule { 418 /// Deep clones this CssRule. deep_clone_with_lock( &self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard, params: &DeepCloneParams, ) -> CssRule419 fn deep_clone_with_lock( 420 &self, 421 lock: &SharedRwLock, 422 guard: &SharedRwLockReadGuard, 423 params: &DeepCloneParams, 424 ) -> CssRule { 425 match *self { 426 CssRule::Namespace(ref arc) => { 427 let rule = arc.read_with(guard); 428 CssRule::Namespace(Arc::new(lock.wrap(rule.clone()))) 429 }, 430 CssRule::Import(ref arc) => { 431 let rule = arc 432 .read_with(guard) 433 .deep_clone_with_lock(lock, guard, params); 434 CssRule::Import(Arc::new(lock.wrap(rule))) 435 }, 436 CssRule::Style(ref arc) => { 437 let rule = arc.read_with(guard); 438 CssRule::Style(Arc::new( 439 lock.wrap(rule.deep_clone_with_lock(lock, guard, params)), 440 )) 441 }, 442 CssRule::Media(ref arc) => { 443 let rule = arc.read_with(guard); 444 CssRule::Media(Arc::new( 445 lock.wrap(rule.deep_clone_with_lock(lock, guard, params)), 446 )) 447 }, 448 CssRule::FontFace(ref arc) => { 449 let rule = arc.read_with(guard); 450 CssRule::FontFace(Arc::new(lock.wrap(rule.clone()))) 451 }, 452 CssRule::FontFeatureValues(ref arc) => { 453 let rule = arc.read_with(guard); 454 CssRule::FontFeatureValues(Arc::new(lock.wrap(rule.clone()))) 455 }, 456 CssRule::CounterStyle(ref arc) => { 457 let rule = arc.read_with(guard); 458 CssRule::CounterStyle(Arc::new(lock.wrap(rule.clone()))) 459 }, 460 CssRule::Viewport(ref arc) => { 461 let rule = arc.read_with(guard); 462 CssRule::Viewport(Arc::new(lock.wrap(rule.clone()))) 463 }, 464 CssRule::Keyframes(ref arc) => { 465 let rule = arc.read_with(guard); 466 CssRule::Keyframes(Arc::new( 467 lock.wrap(rule.deep_clone_with_lock(lock, guard, params)), 468 )) 469 }, 470 CssRule::Supports(ref arc) => { 471 let rule = arc.read_with(guard); 472 CssRule::Supports(Arc::new( 473 lock.wrap(rule.deep_clone_with_lock(lock, guard, params)), 474 )) 475 }, 476 CssRule::Page(ref arc) => { 477 let rule = arc.read_with(guard); 478 CssRule::Page(Arc::new( 479 lock.wrap(rule.deep_clone_with_lock(lock, guard, params)), 480 )) 481 }, 482 CssRule::Document(ref arc) => { 483 let rule = arc.read_with(guard); 484 CssRule::Document(Arc::new( 485 lock.wrap(rule.deep_clone_with_lock(lock, guard, params)), 486 )) 487 }, 488 } 489 } 490 } 491 492 impl ToCssWithGuard for CssRule { 493 // https://drafts.csswg.org/cssom/#serialize-a-css-rule to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result494 fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { 495 match *self { 496 CssRule::Namespace(ref lock) => lock.read_with(guard).to_css(guard, dest), 497 CssRule::Import(ref lock) => lock.read_with(guard).to_css(guard, dest), 498 CssRule::Style(ref lock) => lock.read_with(guard).to_css(guard, dest), 499 CssRule::FontFace(ref lock) => lock.read_with(guard).to_css(guard, dest), 500 CssRule::FontFeatureValues(ref lock) => lock.read_with(guard).to_css(guard, dest), 501 CssRule::CounterStyle(ref lock) => lock.read_with(guard).to_css(guard, dest), 502 CssRule::Viewport(ref lock) => lock.read_with(guard).to_css(guard, dest), 503 CssRule::Keyframes(ref lock) => lock.read_with(guard).to_css(guard, dest), 504 CssRule::Media(ref lock) => lock.read_with(guard).to_css(guard, dest), 505 CssRule::Supports(ref lock) => lock.read_with(guard).to_css(guard, dest), 506 CssRule::Page(ref lock) => lock.read_with(guard).to_css(guard, dest), 507 CssRule::Document(ref lock) => lock.read_with(guard).to_css(guard, dest), 508 } 509 } 510 } 511