diff options
author | Oriol Brufau <obrufau@igalia.com> | 2025-04-15 07:05:13 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-04-15 14:05:13 +0000 |
commit | 10f6f50c615eee807ed81356501afb62f12ee6ac (patch) | |
tree | 366f8c270b93555ce738a1906ab8b3940dddebdd | |
parent | 372fd04b23665256b57ea032d0adf62e54f98d2a (diff) | |
download | servo-10f6f50c615eee807ed81356501afb62f12ee6ac.tar.gz servo-10f6f50c615eee807ed81356501afb62f12ee6ac.zip |
script: Implement CSSStyleSheet constructor (#36521)
https://drafts.csswg.org/cssom/#dom-cssstylesheet-cssstylesheet
Testing: covered by WPT
This is part of #36162
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
11 files changed, 127 insertions, 38 deletions
diff --git a/components/script/dom/cssrulelist.rs b/components/script/dom/cssrulelist.rs index 68b27cc92f0..08f194d5521 100644 --- a/components/script/dom/cssrulelist.rs +++ b/components/script/dom/cssrulelist.rs @@ -125,6 +125,11 @@ impl CSSRuleList { let loader = owner .as_ref() .map(|element| StylesheetLoader::for_element(element)); + let allow_import_rules = if self.parent_stylesheet.is_constructed() { + AllowImportRules::No + } else { + AllowImportRules::Yes + }; let new_rule = css_rules .insert_rule( &parent_stylesheet.shared_lock, @@ -134,7 +139,7 @@ impl CSSRuleList { containing_rule_types, parse_relative_rule_type, loader.as_ref().map(|l| l as &dyn StyleStylesheetLoader), - AllowImportRules::Yes, + allow_import_rules, ) .map_err(Convert::convert)?; diff --git a/components/script/dom/cssstylesheet.rs b/components/script/dom/cssstylesheet.rs index e68e8f0f428..421e8f45523 100644 --- a/components/script/dom/cssstylesheet.rs +++ b/components/script/dom/cssstylesheet.rs @@ -5,14 +5,24 @@ use std::cell::Cell; use dom_struct::dom_struct; +use js::rust::HandleObject; use servo_arc::Arc; +use style::media_queries::MediaList as StyleMediaList; use style::shared_lock::SharedRwLock; -use style::stylesheets::{CssRuleTypes, Stylesheet as StyleStyleSheet}; +use style::stylesheets::{ + AllowImportRules, CssRuleTypes, Origin, Stylesheet as StyleStyleSheet, UrlExtraData, +}; -use crate::dom::bindings::codegen::Bindings::CSSStyleSheetBinding::CSSStyleSheetMethods; +use crate::dom::bindings::codegen::Bindings::CSSStyleSheetBinding::{ + CSSStyleSheetInit, CSSStyleSheetMethods, +}; +use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use crate::dom::bindings::codegen::GenericBindings::CSSRuleListBinding::CSSRuleList_Binding::CSSRuleListMethods; +use crate::dom::bindings::codegen::UnionTypes::MediaListOrString; use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; -use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object}; +use crate::dom::bindings::reflector::{ + DomGlobal, reflect_dom_object, reflect_dom_object_with_proto, +}; use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::bindings::str::DOMString; use crate::dom::cssrulelist::{CSSRuleList, RulesSource}; @@ -32,44 +42,82 @@ pub(crate) struct CSSStyleSheet { #[no_trace] style_stylesheet: Arc<StyleStyleSheet>, origin_clean: Cell<bool>, + is_constructed: bool, } impl CSSStyleSheet { fn new_inherited( - owner: &Element, + owner: Option<&Element>, type_: DOMString, href: Option<DOMString>, title: Option<DOMString>, stylesheet: Arc<StyleStyleSheet>, + is_constructed: bool, ) -> CSSStyleSheet { CSSStyleSheet { stylesheet: StyleSheet::new_inherited(type_, href, title), - owner: MutNullableDom::new(Some(owner)), + owner: MutNullableDom::new(owner), rulelist: MutNullableDom::new(None), style_stylesheet: stylesheet, origin_clean: Cell::new(true), + is_constructed, } } #[cfg_attr(crown, allow(crown::unrooted_must_root))] + #[allow(clippy::too_many_arguments)] pub(crate) fn new( window: &Window, - owner: &Element, + owner: Option<&Element>, type_: DOMString, href: Option<DOMString>, title: Option<DOMString>, stylesheet: Arc<StyleStyleSheet>, + is_constructed: bool, can_gc: CanGc, ) -> DomRoot<CSSStyleSheet> { reflect_dom_object( Box::new(CSSStyleSheet::new_inherited( - owner, type_, href, title, stylesheet, + owner, + type_, + href, + title, + stylesheet, + is_constructed, )), window, can_gc, ) } + #[cfg_attr(crown, allow(crown::unrooted_must_root))] + #[allow(clippy::too_many_arguments)] + fn new_with_proto( + window: &Window, + proto: Option<HandleObject>, + owner: Option<&Element>, + type_: DOMString, + href: Option<DOMString>, + title: Option<DOMString>, + stylesheet: Arc<StyleStyleSheet>, + is_constructed: bool, + can_gc: CanGc, + ) -> DomRoot<CSSStyleSheet> { + reflect_dom_object_with_proto( + Box::new(CSSStyleSheet::new_inherited( + owner, + type_, + href, + title, + stylesheet, + is_constructed, + )), + window, + proto, + can_gc, + ) + } + fn rulelist(&self, can_gc: CanGc) -> DomRoot<CSSRuleList> { self.rulelist.or_init(|| { let rules = self.style_stylesheet.contents.rules.clone(); @@ -123,9 +171,58 @@ impl CSSStyleSheet { can_gc, ) } + + /// <https://drafts.csswg.org/cssom/#concept-css-style-sheet-constructed-flag> + #[inline] + pub(crate) fn is_constructed(&self) -> bool { + self.is_constructed + } } impl CSSStyleSheetMethods<crate::DomTypeHolder> for CSSStyleSheet { + /// <https://drafts.csswg.org/cssom/#dom-cssstylesheet-cssstylesheet> + fn Constructor( + window: &Window, + proto: Option<HandleObject>, + can_gc: CanGc, + options: &CSSStyleSheetInit, + ) -> DomRoot<Self> { + let doc = window.Document(); + let shared_lock = doc.style_shared_lock().clone(); + let media = Arc::new(shared_lock.wrap(match &options.media { + Some(media) => match media { + MediaListOrString::MediaList(media_list) => media_list.clone_media_list(), + MediaListOrString::String(str) => MediaList::parse_media_list(str, window), + }, + None => StyleMediaList::empty(), + })); + let stylesheet = Arc::new(StyleStyleSheet::from_str( + "", + UrlExtraData(window.get_url().get_arc()), + Origin::Author, + media, + shared_lock, + None, + window.css_error_reporter(), + doc.quirks_mode(), + AllowImportRules::No, + )); + if options.disabled { + stylesheet.set_disabled(true); + } + Self::new_with_proto( + window, + proto, + None, // owner + "text/css".into(), + None, // href + None, // title + stylesheet, + true, // is_constructed + can_gc, + ) + } + /// <https://drafts.csswg.org/cssom/#dom-cssstylesheet-cssrules> fn GetCssRules(&self, can_gc: CanGc) -> Fallible<DomRoot<CSSRuleList>> { if !self.origin_clean.get() { diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index 50978ed79ff..969551f98c8 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -182,11 +182,12 @@ impl HTMLLinkElement { self.cssom_stylesheet.or_init(|| { CSSStyleSheet::new( &self.owner_window(), - self.upcast::<Element>(), + Some(self.upcast::<Element>()), "text/css".into(), None, // todo handle location None, // todo handle title sheet, + false, // is_constructed can_gc, ) }) diff --git a/components/script/dom/htmlstyleelement.rs b/components/script/dom/htmlstyleelement.rs index 9e22061fd66..0deb507f283 100644 --- a/components/script/dom/htmlstyleelement.rs +++ b/components/script/dom/htmlstyleelement.rs @@ -151,11 +151,12 @@ impl HTMLStyleElement { self.cssom_stylesheet.or_init(|| { CSSStyleSheet::new( &self.owner_window(), - self.upcast::<Element>(), + Some(self.upcast::<Element>()), "text/css".into(), None, // todo handle location None, // todo handle title sheet, + false, // is_constructed CanGc::note(), ) }) diff --git a/components/script/dom/medialist.rs b/components/script/dom/medialist.rs index e9972ec0e2e..2e9395fde97 100644 --- a/components/script/dom/medialist.rs +++ b/components/script/dom/medialist.rs @@ -106,6 +106,11 @@ impl MediaList { ); MediaQuery::parse(&context, &mut parser) } + + pub(crate) fn clone_media_list(&self) -> StyleMediaList { + let guard = self.shared_lock().read(); + self.media_queries.read_with(&guard).clone() + } } impl MediaListMethods<crate::DomTypeHolder> for MediaList { diff --git a/components/script_bindings/webidls/CSSStyleSheet.webidl b/components/script_bindings/webidls/CSSStyleSheet.webidl index 701a5637eb2..1241b5c2769 100644 --- a/components/script_bindings/webidls/CSSStyleSheet.webidl +++ b/components/script_bindings/webidls/CSSStyleSheet.webidl @@ -5,12 +5,20 @@ // https://drafts.csswg.org/cssom/#the-cssstylesheet-interface [Exposed=Window] interface CSSStyleSheet : StyleSheet { + constructor(optional CSSStyleSheetInit options = {}); + // readonly attribute CSSRule? ownerRule; [Throws, SameObject] readonly attribute CSSRuleList cssRules; [Throws] unsigned long insertRule(DOMString rule, optional unsigned long index = 0); [Throws] undefined deleteRule(unsigned long index); }; +dictionary CSSStyleSheetInit { + // DOMString baseURL = null; + (MediaList or DOMString) media; + boolean disabled = false; +}; + // https://drafts.csswg.org/cssom/#legacy-css-style-sheet-members partial interface CSSStyleSheet { [Throws, SameObject] readonly attribute CSSRuleList rules; diff --git a/tests/wpt/meta/css/css-nesting/nested-declarations-cssom.html.ini b/tests/wpt/meta/css/css-nesting/nested-declarations-cssom.html.ini index b7ffc568ddf..42ccc3a16cc 100644 --- a/tests/wpt/meta/css/css-nesting/nested-declarations-cssom.html.ini +++ b/tests/wpt/meta/css/css-nesting/nested-declarations-cssom.html.ini @@ -26,9 +26,6 @@ [Attempting to insert a CSSNestedDeclaration rule into top-level @media rule] expected: FAIL - [Attempting to insert a CSSNestedDeclaration rule into a stylesheet] - expected: FAIL - [Attempting to insert a CSSNestedDeclaration rule, empty block] expected: FAIL diff --git a/tests/wpt/meta/css/cssom/CSSStyleSheet-constructable-disallow-import.tentative.html.ini b/tests/wpt/meta/css/cssom/CSSStyleSheet-constructable-disallow-import.tentative.html.ini index 99cec16c4ae..ac116f45da3 100644 --- a/tests/wpt/meta/css/cssom/CSSStyleSheet-constructable-disallow-import.tentative.html.ini +++ b/tests/wpt/meta/css/cssom/CSSStyleSheet-constructable-disallow-import.tentative.html.ini @@ -2,12 +2,8 @@ [@import rules are not parsed in CSSStyleSheet.replaceSync] expected: FAIL - [Inserting an @import rule through insertRule on a constructed stylesheet throws an exception] - expected: FAIL - [@import rules are not parsed in CSSStyleSheet.replace] expected: FAIL [@import rules should not trigger any loads.] expected: FAIL - diff --git a/tests/wpt/meta/css/cssom/CSSStyleSheet-constructable.html.ini b/tests/wpt/meta/css/cssom/CSSStyleSheet-constructable.html.ini index 895b75dedf3..e02c46febe6 100644 --- a/tests/wpt/meta/css/cssom/CSSStyleSheet-constructable.html.ini +++ b/tests/wpt/meta/css/cssom/CSSStyleSheet-constructable.html.ini @@ -5,9 +5,6 @@ [new CSSStyleSheet produces empty CSSStyleSheet] expected: FAIL - [title can be set in the CSSStyleSheet constructor] - expected: FAIL - [CSSStyleSheet.replace produces Promise<CSSStyleSheet>] expected: FAIL @@ -56,9 +53,6 @@ [Adopted sheets are ordered after non-adopted sheets in the document] expected: FAIL - [Inserting an @import rule through insertRule on a constructed stylesheet throws an exception] - expected: FAIL - [CSSStyleSheet.replaceSync should not trigger any loads from @import rules] expected: FAIL @@ -74,21 +68,12 @@ [CSSStyleSheet.replace does not reject on failed imports] expected: FAIL - [Cloning a shadow host will not clone shadow root, and also adoptedStyleSheets] - expected: FAIL - - [Importing a shadow host will not copy shadow root, and also adoptedStyleSheets] - expected: FAIL - [Adopting a shadow host will empty adoptedStyleSheets if adopting to a different document] expected: FAIL [Adopting a shadow host's ancestor will empty adoptedStyleSheets if adopting to a different document] expected: FAIL - [Forcing a style update after adding an adopted stylesheet on a disconnected shadow root should not crash.] - expected: FAIL - [Modifying an adopted stylesheet on a disconnected shadow root should not crash.] expected: FAIL diff --git a/tests/wpt/meta/css/cssom/CSSStyleSheet-modify-after-removal.html.ini b/tests/wpt/meta/css/cssom/CSSStyleSheet-modify-after-removal.html.ini deleted file mode 100644 index 9e173013bcb..00000000000 --- a/tests/wpt/meta/css/cssom/CSSStyleSheet-modify-after-removal.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[CSSStyleSheet-modify-after-removal.html] - [Modify constructed sheet from removed iframe] - expected: FAIL diff --git a/tests/wpt/meta/css/cssom/insertRule-across-context.html.ini b/tests/wpt/meta/css/cssom/insertRule-across-context.html.ini deleted file mode 100644 index bbf010a5559..00000000000 --- a/tests/wpt/meta/css/cssom/insertRule-across-context.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[insertRule-across-context.html] - [The constructor of inserted rule object must be from iframe for new CSSStyleSheet()] - expected: FAIL |