mirror of
https://github.com/RGBCube/serenity
synced 2025-07-14 20:17:35 +00:00
LibWeb: Implement CSSStyleSheet.replace()
This method asynchronously replaces the content of the given stylesheet with the content passed to it. An exception is thrown if this method is used by a stylesheet not created with the `CSSStyleSheet()` constructor.
This commit is contained in:
parent
d209d5a84f
commit
81c67d34eb
5 changed files with 118 additions and 3 deletions
7
Tests/LibWeb/Text/expected/css/CSSStyleSheet-replace.txt
Normal file
7
Tests/LibWeb/Text/expected/css/CSSStyleSheet-replace.txt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Exception thrown when calling replace() on non-constructed stylesheet: NotAllowedError
|
||||||
|
Number of CSS rules after replace(): 2
|
||||||
|
Rule: .test { font-size: 14px; }
|
||||||
|
Rule: .test2 { font-size: 16px; }
|
||||||
|
cssRules returns the same object before and after replace(): true
|
||||||
|
@import rule should be not appear below:
|
||||||
|
Rule: .test { padding: 100px; }
|
50
Tests/LibWeb/Text/input/css/CSSStyleSheet-replace.html
Normal file
50
Tests/LibWeb/Text/input/css/CSSStyleSheet-replace.html
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<style>
|
||||||
|
.test {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="../include.js"></script>
|
||||||
|
<script>
|
||||||
|
asyncTest(async done => {
|
||||||
|
const newStyle = `
|
||||||
|
.test {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.test2 {
|
||||||
|
font-size: 16px;
|
||||||
|
}`;
|
||||||
|
const newStyle2 = `.test {
|
||||||
|
padding: 100px;
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const nonConstructedSheet = document.styleSheets[0];
|
||||||
|
try {
|
||||||
|
await nonConstructedSheet.replace(newStyle);
|
||||||
|
println("FAIL");
|
||||||
|
} catch (e) {
|
||||||
|
println(`Exception thrown when calling replace() on non-constructed stylesheet: ${e.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sheet = new CSSStyleSheet();
|
||||||
|
const cssRules = sheet.cssRules;
|
||||||
|
|
||||||
|
await sheet.replace(newStyle);
|
||||||
|
println(`Number of CSS rules after replace(): ${sheet.cssRules.length}`);
|
||||||
|
for (const rule of sheet.cssRules) {
|
||||||
|
println(`Rule: ${rule.cssText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cssRulesAfterReplace = sheet.cssRules;
|
||||||
|
println(`cssRules returns the same object before and after replace(): ${cssRules === cssRulesAfterReplace}`);
|
||||||
|
|
||||||
|
const importRule = `@import url("test.css");`;
|
||||||
|
await sheet.replace(`${newStyle2} ${importRule}`);
|
||||||
|
|
||||||
|
println(`@import rule should be not appear below:`);
|
||||||
|
for (const rule of sheet.cssRules) {
|
||||||
|
println(`Rule: ${rule.cssText}`);
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -13,6 +13,7 @@
|
||||||
#include <LibWeb/CSS/StyleSheetList.h>
|
#include <LibWeb/CSS/StyleSheetList.h>
|
||||||
#include <LibWeb/DOM/Document.h>
|
#include <LibWeb/DOM/Document.h>
|
||||||
#include <LibWeb/HTML/Window.h>
|
#include <LibWeb/HTML/Window.h>
|
||||||
|
#include <LibWeb/Platform/EventLoopPlugin.h>
|
||||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||||
|
|
||||||
namespace Web::CSS {
|
namespace Web::CSS {
|
||||||
|
@ -130,7 +131,9 @@ WebIDL::ExceptionOr<unsigned> CSSStyleSheet::insert_rule(StringView rule, unsign
|
||||||
{
|
{
|
||||||
// FIXME: 1. If the origin-clean flag is unset, throw a SecurityError exception.
|
// FIXME: 1. If the origin-clean flag is unset, throw a SecurityError exception.
|
||||||
|
|
||||||
// FIXME: 2. If the disallow modification flag is set, throw a NotAllowedError DOMException.
|
// If the disallow modification flag is set, throw a NotAllowedError DOMException.
|
||||||
|
if (disallow_modification())
|
||||||
|
return WebIDL::NotAllowedError::create(realm(), "Can't call insert_rule() on non-modifiable stylesheets."_fly_string);
|
||||||
|
|
||||||
// 3. Let parsed rule be the return value of invoking parse a rule with rule.
|
// 3. Let parsed rule be the return value of invoking parse a rule with rule.
|
||||||
auto context = m_style_sheet_list ? CSS::Parser::ParsingContext { m_style_sheet_list->document() } : CSS::Parser::ParsingContext { realm() };
|
auto context = m_style_sheet_list ? CSS::Parser::ParsingContext { m_style_sheet_list->document() } : CSS::Parser::ParsingContext { realm() };
|
||||||
|
@ -165,7 +168,9 @@ WebIDL::ExceptionOr<void> CSSStyleSheet::delete_rule(unsigned index)
|
||||||
{
|
{
|
||||||
// FIXME: 1. If the origin-clean flag is unset, throw a SecurityError exception.
|
// FIXME: 1. If the origin-clean flag is unset, throw a SecurityError exception.
|
||||||
|
|
||||||
// FIXME: 2. If the disallow modification flag is set, throw a NotAllowedError DOMException.
|
// 2. If the disallow modification flag is set, throw a NotAllowedError DOMException.
|
||||||
|
if (disallow_modification())
|
||||||
|
return WebIDL::NotAllowedError::create(realm(), "Can't call delete_rule() on non-modifiable stylesheets."_fly_string);
|
||||||
|
|
||||||
// 3. Remove a CSS rule in the CSS rules at index.
|
// 3. Remove a CSS rule in the CSS rules at index.
|
||||||
auto result = m_rules->remove_a_css_rule(index);
|
auto result = m_rules->remove_a_css_rule(index);
|
||||||
|
@ -178,6 +183,53 @@ WebIDL::ExceptionOr<void> CSSStyleSheet::delete_rule(unsigned index)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/cssom/#dom-cssstylesheet-replace
|
||||||
|
JS::NonnullGCPtr<JS::Promise> CSSStyleSheet::replace(String text)
|
||||||
|
{
|
||||||
|
// 1. Let promise be a promise
|
||||||
|
auto promise = JS::Promise::create(realm());
|
||||||
|
|
||||||
|
// 2. If the constructed flag is not set, or the disallow modification flag is set, reject promise with a NotAllowedError DOMException and return promise.
|
||||||
|
if (!constructed()) {
|
||||||
|
promise->reject(WebIDL::NotAllowedError::create(realm(), "Can't call replace() on non-constructed stylesheets"_fly_string));
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disallow_modification()) {
|
||||||
|
promise->reject(WebIDL::NotAllowedError::create(realm(), "Can't call replace() on non-modifiable stylesheets"_fly_string));
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Set the disallow modification flag.
|
||||||
|
set_disallow_modification(true);
|
||||||
|
|
||||||
|
// 4. In parallel, do these steps:
|
||||||
|
Platform::EventLoopPlugin::the().deferred_invoke([this, text = move(text), promise] {
|
||||||
|
// 1. Let rules be the result of running parse a stylesheet’s contents from text.
|
||||||
|
auto context = m_style_sheet_list ? CSS::Parser::ParsingContext { m_style_sheet_list->document() } : CSS::Parser::ParsingContext { realm() };
|
||||||
|
auto* parsed_stylesheet = parse_css_stylesheet(context, text);
|
||||||
|
auto& rules = parsed_stylesheet->rules();
|
||||||
|
|
||||||
|
// 2. If rules contains one or more @import rules, remove those rules from rules.
|
||||||
|
JS::MarkedVector<JS::NonnullGCPtr<CSSRule>> rules_without_import(realm().heap());
|
||||||
|
for (auto rule : rules) {
|
||||||
|
if (rule->type() != CSSRule::Type::Import)
|
||||||
|
rules_without_import.append(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Set sheet’s CSS rules to rules.
|
||||||
|
m_rules->set_rules({}, rules_without_import);
|
||||||
|
|
||||||
|
// 4. Unset sheet’s disallow modification flag.
|
||||||
|
set_disallow_modification(false);
|
||||||
|
|
||||||
|
// 5. Resolve promise with sheet.
|
||||||
|
promise->fulfill(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/cssom/#dom-cssstylesheet-removerule
|
// https://www.w3.org/TR/cssom/#dom-cssstylesheet-removerule
|
||||||
WebIDL::ExceptionOr<void> CSSStyleSheet::remove_rule(unsigned index)
|
WebIDL::ExceptionOr<void> CSSStyleSheet::remove_rule(unsigned index)
|
||||||
{
|
{
|
||||||
|
|
|
@ -52,6 +52,8 @@ public:
|
||||||
WebIDL::ExceptionOr<void> remove_rule(unsigned index);
|
WebIDL::ExceptionOr<void> remove_rule(unsigned index);
|
||||||
WebIDL::ExceptionOr<void> delete_rule(unsigned index);
|
WebIDL::ExceptionOr<void> delete_rule(unsigned index);
|
||||||
|
|
||||||
|
JS::NonnullGCPtr<JS::Promise> replace(String text);
|
||||||
|
|
||||||
void for_each_effective_style_rule(Function<void(CSSStyleRule const&)> const& callback) const;
|
void for_each_effective_style_rule(Function<void(CSSStyleRule const&)> const& callback) const;
|
||||||
// Returns whether the match state of any media queries changed after evaluation.
|
// Returns whether the match state of any media queries changed after evaluation.
|
||||||
bool evaluate_media_queries(HTML::Window const&);
|
bool evaluate_media_queries(HTML::Window const&);
|
||||||
|
@ -70,6 +72,8 @@ public:
|
||||||
JS::GCPtr<DOM::Document const> constructor_document() const { return m_constructor_document; }
|
JS::GCPtr<DOM::Document const> constructor_document() const { return m_constructor_document; }
|
||||||
void set_constructor_document(JS::GCPtr<DOM::Document const> constructor_document) { m_constructor_document = constructor_document; }
|
void set_constructor_document(JS::GCPtr<DOM::Document const> constructor_document) { m_constructor_document = constructor_document; }
|
||||||
|
|
||||||
|
bool disallow_modification() const { return m_disallow_modification; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CSSStyleSheet(JS::Realm&, CSSRuleList&, MediaList&, Optional<AK::URL> location);
|
CSSStyleSheet(JS::Realm&, CSSRuleList&, MediaList&, Optional<AK::URL> location);
|
||||||
|
|
||||||
|
@ -79,6 +83,7 @@ private:
|
||||||
void recalculate_namespaces();
|
void recalculate_namespaces();
|
||||||
|
|
||||||
void set_constructed(bool constructed) { m_constructed = constructed; }
|
void set_constructed(bool constructed) { m_constructed = constructed; }
|
||||||
|
void set_disallow_modification(bool disallow_modification) { m_disallow_modification = disallow_modification; }
|
||||||
|
|
||||||
JS::GCPtr<CSSRuleList> m_rules;
|
JS::GCPtr<CSSRuleList> m_rules;
|
||||||
JS::GCPtr<CSSNamespaceRule> m_default_namespace_rule;
|
JS::GCPtr<CSSNamespaceRule> m_default_namespace_rule;
|
||||||
|
@ -90,6 +95,7 @@ private:
|
||||||
Optional<AK::URL> m_base_url;
|
Optional<AK::URL> m_base_url;
|
||||||
JS::GCPtr<DOM::Document const> m_constructor_document;
|
JS::GCPtr<DOM::Document const> m_constructor_document;
|
||||||
bool m_constructed { false };
|
bool m_constructed { false };
|
||||||
|
bool m_disallow_modification { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ interface CSSStyleSheet : StyleSheet {
|
||||||
unsigned long insertRule(CSSOMString rule, optional unsigned long index = 0);
|
unsigned long insertRule(CSSOMString rule, optional unsigned long index = 0);
|
||||||
undefined deleteRule(unsigned long index);
|
undefined deleteRule(unsigned long index);
|
||||||
|
|
||||||
// FIXME: Promise<CSSStyleSheet> replace(USVString text);
|
Promise<CSSStyleSheet> replace(USVString text);
|
||||||
// FIXME: undefined replaceSync(USVString text);
|
// FIXME: undefined replaceSync(USVString text);
|
||||||
|
|
||||||
// https://drafts.csswg.org/cssom/#legacy-css-style-sheet-members
|
// https://drafts.csswg.org/cssom/#legacy-css-style-sheet-members
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue