summaryrefslogtreecommitdiffstats
path: root/servo/components/style/stylesheets/import_rule.rs
diff options
context:
space:
mode:
Diffstat (limited to 'servo/components/style/stylesheets/import_rule.rs')
-rw-r--r--servo/components/style/stylesheets/import_rule.rs301
1 files changed, 301 insertions, 0 deletions
diff --git a/servo/components/style/stylesheets/import_rule.rs b/servo/components/style/stylesheets/import_rule.rs
new file mode 100644
index 0000000000..e96134b436
--- /dev/null
+++ b/servo/components/style/stylesheets/import_rule.rs
@@ -0,0 +1,301 @@
+/* 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/. */
+
+//! The [`@import`][import] at-rule.
+//!
+//! [import]: https://drafts.csswg.org/css-cascade-3/#at-import
+
+use crate::media_queries::MediaList;
+use crate::parser::{Parse, ParserContext};
+use crate::shared_lock::{
+ DeepCloneParams, DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard,
+};
+use crate::str::CssStringWriter;
+use crate::stylesheets::{
+ layer_rule::LayerName, supports_rule::SupportsCondition, CssRule, CssRuleType,
+ StylesheetInDocument,
+};
+use crate::values::CssUrl;
+use cssparser::{Parser, SourceLocation};
+use std::fmt::{self, Write};
+use style_traits::{CssWriter, ToCss};
+use to_shmem::{self, SharedMemoryBuilder, ToShmem};
+
+/// A sheet that is held from an import rule.
+#[cfg(feature = "gecko")]
+#[derive(Debug)]
+pub enum ImportSheet {
+ /// A bonafide stylesheet.
+ Sheet(crate::gecko::data::GeckoStyleSheet),
+
+ /// An @import created while parsing off-main-thread, whose Gecko sheet has
+ /// yet to be created and attached.
+ Pending,
+
+ /// An @import created with a false <supports-condition>, so will never be fetched.
+ Refused,
+}
+
+#[cfg(feature = "gecko")]
+impl ImportSheet {
+ /// Creates a new ImportSheet from a GeckoStyleSheet.
+ pub fn new(sheet: crate::gecko::data::GeckoStyleSheet) -> Self {
+ ImportSheet::Sheet(sheet)
+ }
+
+ /// Creates a pending ImportSheet for a load that has not started yet.
+ pub fn new_pending() -> Self {
+ ImportSheet::Pending
+ }
+
+ /// Creates a refused ImportSheet for a load that will not happen.
+ pub fn new_refused() -> Self {
+ ImportSheet::Refused
+ }
+
+ /// Returns a reference to the GeckoStyleSheet in this ImportSheet, if it
+ /// exists.
+ pub fn as_sheet(&self) -> Option<&crate::gecko::data::GeckoStyleSheet> {
+ match *self {
+ ImportSheet::Sheet(ref s) => {
+ debug_assert!(!s.hack_is_null());
+ if s.hack_is_null() {
+ return None;
+ }
+ Some(s)
+ },
+ ImportSheet::Refused | ImportSheet::Pending => None,
+ }
+ }
+
+ /// Returns the media list for this import rule.
+ pub fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
+ self.as_sheet().and_then(|s| s.media(guard))
+ }
+
+ /// Returns the rule list for this import rule.
+ pub fn rules<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] {
+ match self.as_sheet() {
+ Some(s) => s.rules(guard),
+ None => &[],
+ }
+ }
+}
+
+#[cfg(feature = "gecko")]
+impl DeepCloneWithLock for ImportSheet {
+ fn deep_clone_with_lock(
+ &self,
+ _lock: &SharedRwLock,
+ _guard: &SharedRwLockReadGuard,
+ params: &DeepCloneParams,
+ ) -> Self {
+ use crate::gecko::data::GeckoStyleSheet;
+ use crate::gecko_bindings::bindings;
+ match *self {
+ ImportSheet::Sheet(ref s) => {
+ let clone = unsafe {
+ bindings::Gecko_StyleSheet_Clone(s.raw() as *const _, params.reference_sheet)
+ };
+ ImportSheet::Sheet(unsafe { GeckoStyleSheet::from_addrefed(clone) })
+ },
+ ImportSheet::Pending => ImportSheet::Pending,
+ ImportSheet::Refused => ImportSheet::Refused,
+ }
+ }
+}
+
+/// A sheet that is held from an import rule.
+#[cfg(feature = "servo")]
+#[derive(Debug)]
+pub struct ImportSheet(pub ::servo_arc::Arc<crate::stylesheets::Stylesheet>);
+
+#[cfg(feature = "servo")]
+impl ImportSheet {
+ /// Returns the media list for this import rule.
+ pub fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
+ self.0.media(guard)
+ }
+
+ /// Returns the rules for this import rule.
+ pub fn rules<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] {
+ self.0.rules()
+ }
+}
+
+#[cfg(feature = "servo")]
+impl DeepCloneWithLock for ImportSheet {
+ fn deep_clone_with_lock(
+ &self,
+ _lock: &SharedRwLock,
+ _guard: &SharedRwLockReadGuard,
+ _params: &DeepCloneParams,
+ ) -> Self {
+ use servo_arc::Arc;
+
+ ImportSheet(Arc::new((&*self.0).clone()))
+ }
+}
+
+/// The layer specified in an import rule (can be none, anonymous, or named).
+#[derive(Debug, Clone)]
+pub enum ImportLayer {
+ /// No layer specified
+ None,
+
+ /// Anonymous layer (`layer`)
+ Anonymous,
+
+ /// Named layer (`layer(name)`)
+ Named(LayerName),
+}
+
+/// The supports condition in an import rule.
+#[derive(Debug, Clone)]
+pub struct ImportSupportsCondition {
+ /// The supports condition.
+ pub condition: SupportsCondition,
+
+ /// If the import is enabled, from the result of the import condition.
+ pub enabled: bool,
+}
+
+impl ToCss for ImportLayer {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: Write,
+ {
+ match *self {
+ ImportLayer::None => Ok(()),
+ ImportLayer::Anonymous => dest.write_str("layer"),
+ ImportLayer::Named(ref name) => {
+ dest.write_str("layer(")?;
+ name.to_css(dest)?;
+ dest.write_char(')')
+ },
+ }
+ }
+}
+
+/// The [`@import`][import] at-rule.
+///
+/// [import]: https://drafts.csswg.org/css-cascade-3/#at-import
+#[derive(Debug)]
+pub struct ImportRule {
+ /// The `<url>` this `@import` rule is loading.
+ pub url: CssUrl,
+
+ /// The stylesheet is always present. However, in the case of gecko async
+ /// parsing, we don't actually have a Gecko sheet at first, and so the
+ /// ImportSheet just has stub behavior until it appears.
+ pub stylesheet: ImportSheet,
+
+ /// A <supports-condition> for the rule.
+ pub supports: Option<ImportSupportsCondition>,
+
+ /// A `layer()` function name.
+ pub layer: ImportLayer,
+
+ /// The line and column of the rule's source code.
+ pub source_location: SourceLocation,
+}
+
+impl ImportRule {
+ /// Parses the layer() / layer / supports() part of the import header, as per
+ /// https://drafts.csswg.org/css-cascade-5/#at-import:
+ ///
+ /// [ layer | layer(<layer-name>) ]?
+ /// [ supports([ <supports-condition> | <declaration> ]) ]?
+ ///
+ /// We do this here so that the import preloader can look at this without having to parse the
+ /// whole import rule or parse the media query list or what not.
+ pub fn parse_layer_and_supports<'i, 't>(
+ input: &mut Parser<'i, 't>,
+ context: &mut ParserContext,
+ ) -> (ImportLayer, Option<ImportSupportsCondition>) {
+ let layer = if input
+ .try_parse(|input| input.expect_ident_matching("layer"))
+ .is_ok()
+ {
+ ImportLayer::Anonymous
+ } else {
+ input
+ .try_parse(|input| {
+ input.expect_function_matching("layer")?;
+ input
+ .parse_nested_block(|input| LayerName::parse(context, input))
+ .map(|name| ImportLayer::Named(name))
+ })
+ .ok()
+ .unwrap_or(ImportLayer::None)
+ };
+
+ let supports = if !static_prefs::pref!("layout.css.import-supports.enabled") {
+ None
+ } else {
+ input
+ .try_parse(SupportsCondition::parse_for_import)
+ .map(|condition| {
+ let enabled = context
+ .nest_for_rule(CssRuleType::Style, |context| condition.eval(context));
+ ImportSupportsCondition { condition, enabled }
+ })
+ .ok()
+ };
+
+ (layer, supports)
+ }
+}
+
+impl ToShmem for ImportRule {
+ fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
+ Err(String::from(
+ "ToShmem failed for ImportRule: cannot handle imported style sheets",
+ ))
+ }
+}
+
+impl DeepCloneWithLock for ImportRule {
+ fn deep_clone_with_lock(
+ &self,
+ lock: &SharedRwLock,
+ guard: &SharedRwLockReadGuard,
+ params: &DeepCloneParams,
+ ) -> Self {
+ ImportRule {
+ url: self.url.clone(),
+ stylesheet: self.stylesheet.deep_clone_with_lock(lock, guard, params),
+ supports: self.supports.clone(),
+ layer: self.layer.clone(),
+ source_location: self.source_location.clone(),
+ }
+ }
+}
+
+impl ToCssWithGuard for ImportRule {
+ fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
+ dest.write_str("@import ")?;
+ self.url.to_css(&mut CssWriter::new(dest))?;
+
+ if !matches!(self.layer, ImportLayer::None) {
+ dest.write_char(' ')?;
+ self.layer.to_css(&mut CssWriter::new(dest))?;
+ }
+
+ if let Some(ref supports) = self.supports {
+ dest.write_str(" supports(")?;
+ supports.condition.to_css(&mut CssWriter::new(dest))?;
+ dest.write_char(')')?;
+ }
+
+ if let Some(media) = self.stylesheet.media(guard) {
+ if !media.is_empty() {
+ dest.write_char(' ')?;
+ media.to_css(&mut CssWriter::new(dest))?;
+ }
+ }
+
+ dest.write_char(';')
+ }
+}