aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOriol Brufau <obrufau@igalia.com>2025-04-15 07:05:13 -0700
committerGitHub <noreply@github.com>2025-04-15 14:05:13 +0000
commit10f6f50c615eee807ed81356501afb62f12ee6ac (patch)
tree366f8c270b93555ce738a1906ab8b3940dddebdd
parent372fd04b23665256b57ea032d0adf62e54f98d2a (diff)
downloadservo-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>
-rw-r--r--components/script/dom/cssrulelist.rs7
-rw-r--r--components/script/dom/cssstylesheet.rs111
-rw-r--r--components/script/dom/htmllinkelement.rs3
-rw-r--r--components/script/dom/htmlstyleelement.rs3
-rw-r--r--components/script/dom/medialist.rs5
-rw-r--r--components/script_bindings/webidls/CSSStyleSheet.webidl8
-rw-r--r--tests/wpt/meta/css/css-nesting/nested-declarations-cssom.html.ini3
-rw-r--r--tests/wpt/meta/css/cssom/CSSStyleSheet-constructable-disallow-import.tentative.html.ini4
-rw-r--r--tests/wpt/meta/css/cssom/CSSStyleSheet-constructable.html.ini15
-rw-r--r--tests/wpt/meta/css/cssom/CSSStyleSheet-modify-after-removal.html.ini3
-rw-r--r--tests/wpt/meta/css/cssom/insertRule-across-context.html.ini3
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