Commit 9309bf97 authored by Emilio Cobos Álvarez's avatar Emilio Cobos Álvarez
Browse files

Bug 1699217 - Implement @import layer|layer(<name>). r=boris

This works modulo the existing nested layer order bug. Will be covered
by WPT /css/css-cascade/layer-import.html once the feature is enabled (I
can probably enable it right away for those tests, but I'd rather fix
the obvious bugs first).

Differential Revision: https://phabricator.services.mozilla.com/D124538
parent d6188c9c
Loading
Loading
Loading
Loading
+50 −7
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock};
use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
use crate::str::CssStringWriter;
use crate::stylesheets::{CssRule, Origin, StylesheetInDocument};
use crate::stylesheets::layer_rule::LayerName;
use crate::values::CssUrl;
use cssparser::SourceLocation;
use std::fmt::{self, Write};
@@ -136,6 +137,31 @@ impl DeepCloneWithLock for ImportSheet {
    }
}

/// The layer keyword or function in an import rule.
#[derive(Debug)]
pub struct ImportLayer {
    /// Whether the layer is anonymous.
    pub is_anonymous: bool,
    /// The layer name.
    pub name: LayerName,
}


impl ToCss for ImportLayer {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        if self.is_anonymous {
            dest.write_str("layer")
        } else {
            dest.write_str("layer(")?;
            self.name.to_css(dest)?;
            dest.write_char(')')
        }
    }
}

/// The [`@import`][import] at-rule.
///
/// [import]: https://drafts.csswg.org/css-cascade-3/#at-import
@@ -149,6 +175,9 @@ pub struct ImportRule {
    /// ImportSheet just has stub behavior until it appears.
    pub stylesheet: ImportSheet,

    /// A `layer()` function name.
    pub layer: Option<ImportLayer>,

    /// The line and column of the rule's source code.
    pub source_location: SourceLocation,
}
@@ -171,6 +200,16 @@ impl DeepCloneWithLock for ImportRule {
        ImportRule {
            url: self.url.clone(),
            stylesheet: self.stylesheet.deep_clone_with_lock(lock, guard, params),
            layer: self.layer.as_ref().map(|layer| {
                ImportLayer {
                    is_anonymous: layer.is_anonymous,
                    name: if layer.is_anonymous {
                        LayerName::new_anonymous()
                    } else {
                        layer.name.clone()
                    },
                }
            }),
            source_location: self.source_location.clone(),
        }
    }
@@ -181,14 +220,18 @@ impl ToCssWithGuard for ImportRule {
        dest.write_str("@import ")?;
        self.url.to_css(&mut CssWriter::new(dest))?;

        match self.stylesheet.media(guard) {
            Some(media) if !media.is_empty() => {
                dest.write_str(" ")?;
        if let Some(media) = self.stylesheet.media(guard) {
            if !media.is_empty() {
                dest.write_char(' ')?;
                media.to_css(&mut CssWriter::new(dest))?;
            },
            _ => {},
        };
            }
        }

        if let Some(ref layer) = self.layer {
            dest.write_char(' ')?;
            layer.to_css(&mut CssWriter::new(dest))?;
        }

        dest.write_str(";")
        dest.write_char(';')
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@
use crate::media_queries::MediaList;
use crate::parser::ParserContext;
use crate::shared_lock::{Locked, SharedRwLock};
use crate::stylesheets::import_rule::ImportRule;
use crate::stylesheets::import_rule::{ImportRule, ImportLayer};
use crate::values::CssUrl;
use cssparser::SourceLocation;
use servo_arc::Arc;
@@ -25,5 +25,6 @@ pub trait StylesheetLoader {
        context: &ParserContext,
        lock: &SharedRwLock,
        media: Arc<Locked<MediaList>>,
        layer: Option<ImportLayer>,
    ) -> Arc<Locked<ImportRule>>;
}
+24 −3
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ use crate::stylesheets::font_feature_values_rule::parse_family_name_list;
use crate::stylesheets::keyframes_rule::parse_keyframe_list;
use crate::stylesheets::stylesheet::Namespaces;
use crate::stylesheets::supports_rule::SupportsCondition;
use crate::stylesheets::import_rule::ImportLayer;
use crate::stylesheets::layer_rule::{LayerName, LayerRuleKind};
use crate::stylesheets::viewport_rule;
use crate::stylesheets::AllowImportRules;
@@ -169,7 +170,7 @@ pub enum AtRulePrelude {
    /// A @document rule, with its conditional.
    Document(DocumentCondition),
    /// A @import rule prelude.
    Import(CssUrl, Arc<Locked<MediaList>>),
    Import(CssUrl, Arc<Locked<MediaList>>, Option<ImportLayer>),
    /// A @namespace rule prelude.
    Namespace(Option<Prefix>, Namespace),
    /// A @layer rule prelude.
@@ -206,10 +207,29 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
                let url_string = input.expect_url_or_string()?.as_ref().to_owned();
                let url = CssUrl::parse_from_string(url_string, &self.context, CorsMode::None);

                let layer = if !static_prefs::pref!("layout.css.cascade-layers.enabled") {
                    None
                } else if input.try_parse(|input| input.expect_ident_matching("layer")).is_ok() {
                    Some(ImportLayer {
                        is_anonymous: true,
                        name: LayerName::new_anonymous(),
                    })
                } else {
                    input.try_parse(|input| {
                        input.expect_function_matching("layer")?;
                        input.parse_nested_block(|input| {
                            LayerName::parse(&self.context, input)
                        }).map(|name| ImportLayer {
                            is_anonymous: false,
                            name,
                        })
                    }).ok()
                };

                let media = MediaList::parse(&self.context, input);
                let media = Arc::new(self.shared_lock.wrap(media));

                return Ok(AtRulePrelude::Import(url, media));
                return Ok(AtRulePrelude::Import(url, media, layer));
            },
            "namespace" => {
                if !self.check_state(State::Namespaces) {
@@ -263,7 +283,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
        start: &ParserState,
    ) -> Result<Self::AtRule, ()>  {
        let rule = match prelude {
            AtRulePrelude::Import(url, media) => {
            AtRulePrelude::Import(url, media, layer) => {
                let loader = self
                    .loader
                    .expect("Expected a stylesheet loader for @import");
@@ -274,6 +294,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
                    &self.context,
                    &self.shared_lock,
                    media,
                    layer,
                );

                self.state = State::Imports;
+22 −14
Original line number Diff line number Diff line
@@ -2354,15 +2354,36 @@ impl CascadeData {
                continue;
            }


            fn maybe_register_layer(data: &mut CascadeData, layer: &LayerName) -> u32 {
                // TODO: Measure what's more common / expensive, if
                // layer.clone() or the double hash lookup in the insert
                // case.
                if let Some(order) = data.layer_order.get(layer) {
                    return *order;
                }
                let order = data.next_layer_order;
                data.layer_order.insert(layer.clone(), order);
                data.next_layer_order += 1;
                order
            }

            let mut layer_names_to_pop = 0;
            let mut children_layer_order = current_layer_order;
            match *rule {
                CssRule::Import(ref lock) => {
                    if rebuild_kind.should_rebuild_invalidation() {
                    let import_rule = lock.read_with(guard);
                    if rebuild_kind.should_rebuild_invalidation() {
                        self.effective_media_query_results
                            .saw_effective(import_rule);
                    }
                    if let Some(ref layer) = import_rule.layer {
                        for name in layer.name.layer_names() {
                            current_layer.0.push(name.clone());
                            children_layer_order = maybe_register_layer(self, &current_layer);
                            layer_names_to_pop += 1;
                        }
                    }

                },
                CssRule::Media(ref lock) => {
@@ -2374,19 +2395,6 @@ impl CascadeData {
                CssRule::Layer(ref lock) => {
                    use crate::stylesheets::layer_rule::LayerRuleKind;

                    fn maybe_register_layer(data: &mut CascadeData, layer: &LayerName) -> u32 {
                        // TODO: Measure what's more common / expensive, if
                        // layer.clone() or the double hash lookup in the insert
                        // case.
                        if let Some(order) = data.layer_order.get(layer) {
                            return *order;
                        }
                        let order = data.next_layer_order;
                        data.layer_order.insert(layer.clone(), order);
                        data.next_layer_order += 1;
                        order
                    }

                    let layer_rule = lock.read_with(guard);
                    match layer_rule.kind {
                        LayerRuleKind::Block { ref name, .. } => {
+7 −3
Original line number Diff line number Diff line
@@ -19,7 +19,7 @@ use style::global_style_data::GLOBAL_STYLE_DATA;
use style::media_queries::MediaList;
use style::parser::ParserContext;
use style::shared_lock::{Locked, SharedRwLock};
use style::stylesheets::import_rule::ImportSheet;
use style::stylesheets::import_rule::{ImportSheet, ImportLayer};
use style::stylesheets::AllowImportRules;
use style::stylesheets::{ImportRule, Origin, StylesheetLoader as StyleStylesheetLoader};
use style::stylesheets::{StylesheetContents, UrlExtraData};
@@ -52,6 +52,7 @@ impl StyleStylesheetLoader for StylesheetLoader {
        _context: &ParserContext,
        lock: &SharedRwLock,
        media: Arc<Locked<MediaList>>,
        layer: Option<ImportLayer>,
    ) -> Arc<Locked<ImportRule>> {
        // After we get this raw pointer ImportRule will be moved into a lock and Arc
        // and so the Arc<Url> pointer inside will also move,
@@ -70,8 +71,9 @@ impl StyleStylesheetLoader for StylesheetLoader {
        let stylesheet = ImportSheet::new(sheet);
        Arc::new(lock.wrap(ImportRule {
            url,
            source_location,
            stylesheet,
            layer,
            source_location,
        }))
    }
}
@@ -159,12 +161,14 @@ impl StyleStylesheetLoader for AsyncStylesheetParser {
        _context: &ParserContext,
        lock: &SharedRwLock,
        media: Arc<Locked<MediaList>>,
        layer: Option<ImportLayer>,
    ) -> Arc<Locked<ImportRule>> {
        let stylesheet = ImportSheet::new_pending(self.origin, self.quirks_mode);
        let rule = Arc::new(lock.wrap(ImportRule {
            url: url.clone(),
            source_location,
            stylesheet,
            layer,
            source_location,
        }));

        unsafe {