mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 04:09:13 +00:00
LibWeb: Don't construct stylesheet when modifying CSSStyleSheet
rules
Some checks are pending
CI / Lagom (arm64, Sanitizer_CI, false, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (x86_64, Fuzzers_CI, false, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, false, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, true, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (arm64, macos-15, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (x86_64, ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
Some checks are pending
CI / Lagom (arm64, Sanitizer_CI, false, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (x86_64, Fuzzers_CI, false, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, false, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, true, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (arm64, macos-15, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (x86_64, ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
Previously, `CSSStyleSheet.replace()` and `CSSStyleSheet.replaceSync()` parsed the given CSS text into a temporary stylesheet object, from which a list of rules was extracted. Doing this had the unintended side-effect that a fetch request would be started if the given CSS text referenced any external resources. This fetch request would cause a crash, since the temporary stylesheet object didn't set the constructed flag, or constructor document. We now parse the given CSS text as a list of rules without constructing a temporary stylesheet.
This commit is contained in:
parent
955f6fc9cb
commit
ca200142e9
Notes:
github-actions[bot]
2025-04-16 21:04:15 +00:00
Author: https://github.com/tcl3
Commit: ca200142e9
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4371
Reviewed-by: https://github.com/AtkinsSJ ✅
5 changed files with 95 additions and 6 deletions
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2022, Andreas Kling <andreas@ladybird.org>
|
* Copyright (c) 2019-2022, Andreas Kling <andreas@ladybird.org>
|
||||||
* Copyright (c) 2022-2024, Sam Atkins <sam@ladybird.org>
|
* Copyright (c) 2022-2024, Sam Atkins <sam@ladybird.org>
|
||||||
* Copyright (c) 2024, Tim Ledbetter <timledbetter@gmail.com>
|
* Copyright (c) 2024-2025, Tim Ledbetter <tim.ledbetter@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -207,8 +207,7 @@ GC::Ref<WebIDL::Promise> CSSStyleSheet::replace(String text)
|
||||||
HTML::TemporaryExecutionContext execution_context { realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes };
|
HTML::TemporaryExecutionContext execution_context { realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes };
|
||||||
|
|
||||||
// 1. Let rules be the result of running parse a stylesheet’s contents from text.
|
// 1. Let rules be the result of running parse a stylesheet’s contents from text.
|
||||||
auto parsed_stylesheet = parse_css_stylesheet(make_parsing_params(), text);
|
auto rules = CSS::Parser::Parser::create(make_parsing_params(), text).parse_as_stylesheet_contents();
|
||||||
auto& rules = parsed_stylesheet->rules();
|
|
||||||
|
|
||||||
// 2. If rules contains one or more @import rules, remove those rules from rules.
|
// 2. If rules contains one or more @import rules, remove those rules from rules.
|
||||||
GC::RootVector<GC::Ref<CSSRule>> rules_without_import(realm.heap());
|
GC::RootVector<GC::Ref<CSSRule>> rules_without_import(realm.heap());
|
||||||
|
@ -240,8 +239,7 @@ WebIDL::ExceptionOr<void> CSSStyleSheet::replace_sync(StringView text)
|
||||||
return WebIDL::NotAllowedError::create(realm(), "Can't call replaceSync() on non-modifiable stylesheets"_string);
|
return WebIDL::NotAllowedError::create(realm(), "Can't call replaceSync() on non-modifiable stylesheets"_string);
|
||||||
|
|
||||||
// 2. Let rules be the result of running parse a stylesheet’s contents from text.
|
// 2. Let rules be the result of running parse a stylesheet’s contents from text.
|
||||||
auto parsed_stylesheet = parse_css_stylesheet(make_parsing_params(), text);
|
auto rules = CSS::Parser::Parser::create(make_parsing_params(), text).parse_as_stylesheet_contents();
|
||||||
auto& rules = parsed_stylesheet->rules();
|
|
||||||
|
|
||||||
// 3. If rules contains one or more @import rules, remove those rules from rules.
|
// 3. If rules contains one or more @import rules, remove those rules from rules.
|
||||||
GC::RootVector<GC::Ref<CSSRule>> rules_without_import(realm().heap());
|
GC::RootVector<GC::Ref<CSSRule>> rules_without_import(realm().heap());
|
||||||
|
@ -250,7 +248,7 @@ WebIDL::ExceptionOr<void> CSSStyleSheet::replace_sync(StringView text)
|
||||||
rules_without_import.append(rule);
|
rules_without_import.append(rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4.Set sheet’s CSS rules to rules.
|
// 4. Set sheet’s CSS rules to rules.
|
||||||
m_rules->set_rules({}, rules_without_import);
|
m_rules->set_rules({}, rules_without_import);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -118,6 +118,22 @@ Vector<Rule> Parser::parse_a_stylesheets_contents(TokenStream<T>& input)
|
||||||
return consume_a_stylesheets_contents(input);
|
return consume_a_stylesheets_contents(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GC::RootVector<GC::Ref<CSSRule>> Parser::parse_as_stylesheet_contents()
|
||||||
|
{
|
||||||
|
auto raw_rules = parse_a_stylesheets_contents(m_token_stream);
|
||||||
|
GC::RootVector<GC::Ref<CSSRule>> rules(realm().heap());
|
||||||
|
for (auto const& raw_rule : raw_rules) {
|
||||||
|
auto rule = convert_to_rule(raw_rule, Nested::No);
|
||||||
|
if (!rule) {
|
||||||
|
log_parse_error();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rules.append(*rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-syntax/#parse-a-css-stylesheet
|
// https://drafts.csswg.org/css-syntax/#parse-a-css-stylesheet
|
||||||
GC::Ref<CSS::CSSStyleSheet> Parser::parse_as_css_stylesheet(Optional<::URL::URL> location, Vector<NonnullRefPtr<MediaQuery>> media_query_list)
|
GC::Ref<CSS::CSSStyleSheet> Parser::parse_as_css_stylesheet(Optional<::URL::URL> location, Vector<NonnullRefPtr<MediaQuery>> media_query_list)
|
||||||
{
|
{
|
||||||
|
|
|
@ -100,6 +100,7 @@ public:
|
||||||
Vector<Descriptor> parse_as_list_of_descriptors(AtRuleID);
|
Vector<Descriptor> parse_as_list_of_descriptors(AtRuleID);
|
||||||
CSSRule* parse_as_css_rule();
|
CSSRule* parse_as_css_rule();
|
||||||
Optional<StyleProperty> parse_as_supports_condition();
|
Optional<StyleProperty> parse_as_supports_condition();
|
||||||
|
GC::RootVector<GC::Ref<CSSRule>> parse_as_stylesheet_contents();
|
||||||
|
|
||||||
enum class SelectorParsingMode {
|
enum class SelectorParsingMode {
|
||||||
Standard,
|
Standard,
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 3 tests
|
||||||
|
|
||||||
|
3 Pass
|
||||||
|
Pass Constructing sheet with custom base URL ueses that URL for CSS rules
|
||||||
|
Pass Constructing sheet with relative URL adds to the constructor document's base URL
|
||||||
|
Pass Constructing sheet with invalid base URL throws a NotAllowedError
|
|
@ -0,0 +1,66 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<title>CSSStyleSheet baseURL</title>
|
||||||
|
<link rel="author" title="Erik Nordin" href="mailto:enordin@mozilla.com">
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/cssom-1/#dom-cssstylesheetinit-baseurl">
|
||||||
|
<div id="target"></div>
|
||||||
|
<script src='../../resources/testharness.js'></script>
|
||||||
|
<script src='../../resources/testharnessreport.js'></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
function currentLocation() {
|
||||||
|
const sections = location.href.split("/")
|
||||||
|
sections.pop();
|
||||||
|
return sections.join("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
test(() => {
|
||||||
|
const span = document.createElement("span");
|
||||||
|
target.appendChild(span);
|
||||||
|
span.attachShadow({ mode: "open" })
|
||||||
|
const shadowDiv = document.createElement("div");
|
||||||
|
span.shadowRoot.appendChild(shadowDiv);
|
||||||
|
|
||||||
|
const fileName = "example.png"
|
||||||
|
const baseURL = `${location.origin}/custom/path/`;
|
||||||
|
const fullURL = `${baseURL}${fileName}`;
|
||||||
|
|
||||||
|
const sheet = new CSSStyleSheet({ baseURL });
|
||||||
|
span.shadowRoot.adoptedStyleSheets = [sheet];
|
||||||
|
|
||||||
|
sheet.replaceSync(`* { background-image: url("${fileName}"); }`);
|
||||||
|
const styleFromRelative = getComputedStyle(shadowDiv).backgroundImage;
|
||||||
|
|
||||||
|
sheet.replaceSync(`* { background-image: url("${fullURL}"); }`);
|
||||||
|
const styleFromFull = getComputedStyle(shadowDiv).backgroundImage;
|
||||||
|
|
||||||
|
assert_equals(styleFromRelative, styleFromFull);
|
||||||
|
}, "Constructing sheet with custom base URL ueses that URL for CSS rules");
|
||||||
|
|
||||||
|
test(() => {
|
||||||
|
const span = document.createElement("span");
|
||||||
|
target.appendChild(span);
|
||||||
|
span.attachShadow({ mode: "open" })
|
||||||
|
const shadowDiv = document.createElement("div");
|
||||||
|
span.shadowRoot.appendChild(shadowDiv);
|
||||||
|
|
||||||
|
const fileName = "example.png"
|
||||||
|
const baseURL = "custom/path/";
|
||||||
|
const fullURL = `${currentLocation()}/${baseURL}${fileName}`;
|
||||||
|
|
||||||
|
const sheet = new CSSStyleSheet({ baseURL });
|
||||||
|
span.shadowRoot.adoptedStyleSheets = [sheet];
|
||||||
|
|
||||||
|
sheet.replaceSync(`* { background-image: url("${fileName}"); }`);
|
||||||
|
const styleFromRelative = getComputedStyle(shadowDiv).backgroundImage;
|
||||||
|
|
||||||
|
sheet.replaceSync(`* { background-image: url("${fullURL}"); }`);
|
||||||
|
const styleFromFull = getComputedStyle(shadowDiv).backgroundImage;
|
||||||
|
|
||||||
|
assert_equals(styleFromRelative, styleFromFull);
|
||||||
|
}, "Constructing sheet with relative URL adds to the constructor document's base URL");
|
||||||
|
|
||||||
|
test(() => {
|
||||||
|
assert_throws_dom("NotAllowedError", () => { new CSSStyleSheet({ baseURL: "https://test:test/"}) });
|
||||||
|
}, "Constructing sheet with invalid base URL throws a NotAllowedError");
|
||||||
|
|
||||||
|
</script>
|
Loading…
Add table
Add a link
Reference in a new issue