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