summaryrefslogtreecommitdiffstats
path: root/servo/components/style/stylesheets/layer_rule.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--servo/components/style/stylesheets/layer_rule.rs239
1 files changed, 239 insertions, 0 deletions
diff --git a/servo/components/style/stylesheets/layer_rule.rs b/servo/components/style/stylesheets/layer_rule.rs
new file mode 100644
index 0000000000..c724fd81f1
--- /dev/null
+++ b/servo/components/style/stylesheets/layer_rule.rs
@@ -0,0 +1,239 @@
+/* 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/. */
+
+//! A [`@layer`][layer] rule.
+//!
+//! [layer]: https://drafts.csswg.org/css-cascade-5/#layering
+
+use crate::parser::{Parse, ParserContext};
+use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
+use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
+use crate::values::AtomIdent;
+
+use super::CssRules;
+
+use cssparser::{Parser, SourceLocation, Token};
+use servo_arc::Arc;
+use smallvec::SmallVec;
+use std::fmt::{self, Write};
+use style_traits::{CssWriter, ParseError, ToCss};
+
+/// The order of a given layer. We use 16 bits so that we can pack LayerOrder
+/// and CascadeLevel in a single 32-bit struct. If we need more bits we can go
+/// back to packing CascadeLevel in a single byte as we did before.
+#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd, Ord)]
+pub struct LayerOrder(u16);
+
+impl LayerOrder {
+ /// The order of the root layer.
+ pub const fn root() -> Self {
+ Self(std::u16::MAX - 1)
+ }
+
+ /// The order of the style attribute layer.
+ pub const fn style_attribute() -> Self {
+ Self(std::u16::MAX)
+ }
+
+ /// Returns whether this layer is for the style attribute, which behaves
+ /// differently in terms of !important, see
+ /// https://github.com/w3c/csswg-drafts/issues/6872
+ ///
+ /// (This is a bit silly, mind-you, but it's needed so that revert-layer
+ /// behaves correctly).
+ #[inline]
+ pub fn is_style_attribute_layer(&self) -> bool {
+ *self == Self::style_attribute()
+ }
+
+ /// The first cascade layer order.
+ pub const fn first() -> Self {
+ Self(0)
+ }
+
+ /// Increment the cascade layer order.
+ #[inline]
+ pub fn inc(&mut self) {
+ if self.0 != std::u16::MAX - 1 {
+ self.0 += 1;
+ }
+ }
+}
+
+/// A `<layer-name>`: https://drafts.csswg.org/css-cascade-5/#typedef-layer-name
+#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
+pub struct LayerName(pub SmallVec<[AtomIdent; 1]>);
+
+impl LayerName {
+ /// Returns an empty layer name (which isn't a valid final state, so caller
+ /// is responsible to fill up the name before use).
+ pub fn new_empty() -> Self {
+ Self(Default::default())
+ }
+
+ /// Returns a synthesized name for an anonymous layer.
+ pub fn new_anonymous() -> Self {
+ use std::sync::atomic::{AtomicUsize, Ordering};
+ static NEXT_ANONYMOUS_LAYER_NAME: AtomicUsize = AtomicUsize::new(0);
+
+ let mut name = SmallVec::new();
+ let next_id = NEXT_ANONYMOUS_LAYER_NAME.fetch_add(1, Ordering::Relaxed);
+ // The parens don't _technically_ prevent conflicts with authors, as
+ // authors could write escaped parens as part of the identifier, I
+ // think, but highly reduces the possibility.
+ name.push(AtomIdent::from(&*format!("-moz-anon-layer({})", next_id)));
+
+ LayerName(name)
+ }
+
+ /// Returns the names of the layers. That is, for a layer like `foo.bar`,
+ /// it'd return [foo, bar].
+ pub fn layer_names(&self) -> &[AtomIdent] {
+ &self.0
+ }
+}
+
+impl Parse for LayerName {
+ fn parse<'i, 't>(
+ _: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self, ParseError<'i>> {
+ let mut result = SmallVec::new();
+ result.push(AtomIdent::from(&**input.expect_ident()?));
+ loop {
+ let next_name = input.try_parse(|input| -> Result<AtomIdent, ParseError<'i>> {
+ match input.next_including_whitespace()? {
+ Token::Delim('.') => {},
+ other => {
+ let t = other.clone();
+ return Err(input.new_unexpected_token_error(t));
+ },
+ }
+
+ let name = match input.next_including_whitespace()? {
+ Token::Ident(ref ident) => ident,
+ other => {
+ let t = other.clone();
+ return Err(input.new_unexpected_token_error(t));
+ },
+ };
+
+ Ok(AtomIdent::from(&**name))
+ });
+
+ match next_name {
+ Ok(name) => result.push(name),
+ Err(..) => break,
+ }
+ }
+ Ok(LayerName(result))
+ }
+}
+
+impl ToCss for LayerName {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: Write,
+ {
+ let mut first = true;
+ for name in self.0.iter() {
+ if !first {
+ dest.write_char('.')?;
+ }
+ first = false;
+ name.to_css(dest)?;
+ }
+ Ok(())
+ }
+}
+
+#[derive(Debug, ToShmem)]
+/// A block `@layer <name>? { ... }`
+/// https://drafts.csswg.org/css-cascade-5/#layer-block
+pub struct LayerBlockRule {
+ /// The layer name, or `None` if anonymous.
+ pub name: Option<LayerName>,
+ /// The nested rules.
+ pub rules: Arc<Locked<CssRules>>,
+ /// The source position where this rule was found.
+ pub source_location: SourceLocation,
+}
+
+impl ToCssWithGuard for LayerBlockRule {
+ fn to_css(
+ &self,
+ guard: &SharedRwLockReadGuard,
+ dest: &mut crate::str::CssStringWriter,
+ ) -> fmt::Result {
+ dest.write_str("@layer")?;
+ if let Some(ref name) = self.name {
+ dest.write_char(' ')?;
+ name.to_css(&mut CssWriter::new(dest))?;
+ }
+ self.rules.read_with(guard).to_css_block(guard, dest)
+ }
+}
+
+impl DeepCloneWithLock for LayerBlockRule {
+ fn deep_clone_with_lock(
+ &self,
+ lock: &SharedRwLock,
+ guard: &SharedRwLockReadGuard,
+ params: &DeepCloneParams,
+ ) -> Self {
+ Self {
+ name: self.name.clone(),
+ rules: Arc::new(
+ lock.wrap(
+ self.rules
+ .read_with(guard)
+ .deep_clone_with_lock(lock, guard, params),
+ ),
+ ),
+ source_location: self.source_location.clone(),
+ }
+ }
+}
+
+/// A statement `@layer <name>, <name>, <name>;`
+///
+/// https://drafts.csswg.org/css-cascade-5/#layer-empty
+#[derive(Clone, Debug, ToShmem)]
+pub struct LayerStatementRule {
+ /// The list of layers to sort.
+ pub names: Vec<LayerName>,
+ /// The source position where this rule was found.
+ pub source_location: SourceLocation,
+}
+
+impl ToCssWithGuard for LayerStatementRule {
+ fn to_css(
+ &self,
+ _: &SharedRwLockReadGuard,
+ dest: &mut crate::str::CssStringWriter,
+ ) -> fmt::Result {
+ let mut writer = CssWriter::new(dest);
+ writer.write_str("@layer ")?;
+ let mut first = true;
+ for name in &*self.names {
+ if !first {
+ writer.write_str(", ")?;
+ }
+ first = false;
+ name.to_css(&mut writer)?;
+ }
+ writer.write_char(';')
+ }
+}
+
+impl DeepCloneWithLock for LayerStatementRule {
+ fn deep_clone_with_lock(
+ &self,
+ _: &SharedRwLock,
+ _: &SharedRwLockReadGuard,
+ _: &DeepCloneParams,
+ ) -> Self {
+ self.clone()
+ }
+}