diff options
author | Andreas Kling <kling@serenityos.org> | 2021-09-29 23:46:16 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-09-30 00:00:55 +0200 |
commit | d462a6720afe721b6821ce14d5b7802d1890b61f (patch) | |
tree | 6b599eacee6ad58cd297f126213430388c0aff68 /Userland/Libraries/LibWeb | |
parent | 6cda24097b5f3311ceb9f3ea224eb3cb643d8cc3 (diff) | |
download | serenity-d462a6720afe721b6821ce14d5b7802d1890b61f.zip |
LibWeb: Reimplement the <style> element following the spec
We now follow the "update a style block" algorithm from the HTML spec
instead of using the ad-hoc CSSLoader mechanism.
This necessitated improving our StyleSheet and CSSStyleSheet classes as
well, so that's baked into this commit.
Diffstat (limited to 'Userland/Libraries/LibWeb')
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/CSSStyleSheet.h | 5 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/StyleSheet.h | 28 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/StyleSheet.idl | 6 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/StyleSheetList.cpp | 6 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/StyleSheetList.h | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/HTMLStyleElement.cpp | 132 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/HTMLStyleElement.h | 8 |
7 files changed, 163 insertions, 24 deletions
diff --git a/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.h b/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.h index 9afaadce50..0528ffb49a 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.h +++ b/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.h @@ -27,6 +27,8 @@ public: virtual ~CSSStyleSheet() override; + void set_owner_css_rule(CSSRule* rule) { m_owner_css_rule = rule; } + virtual String type() const override { return "text/css"; } CSSRuleList const& rules() const { return m_rules; } @@ -76,6 +78,9 @@ private: explicit CSSStyleSheet(NonnullRefPtrVector<CSSRule>); NonnullRefPtr<CSSRuleList> m_rules; + + // FIXME: Use WeakPtr. + CSSRule* m_owner_css_rule { nullptr }; }; } diff --git a/Userland/Libraries/LibWeb/CSS/StyleSheet.h b/Userland/Libraries/LibWeb/CSS/StyleSheet.h index a78c7b5258..36f9e7a610 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleSheet.h +++ b/Userland/Libraries/LibWeb/CSS/StyleSheet.h @@ -26,11 +26,39 @@ public: DOM::Element* owner_node() { return m_owner_node; } void set_owner_node(DOM::Element*); + String title() const { return m_title; } + void set_title(String title) { m_title = move(title); } + + void set_type(String type) { m_type_string = move(type); } + void set_media(String media) { m_media_string = move(media); } + + bool is_alternate() const { return m_alternate; } + void set_alternate(bool alternate) { m_alternate = alternate; } + + void set_origin_clean(bool origin_clean) { m_origin_clean = origin_clean; } + + bool disabled() const { return m_disabled; } + void set_disabled(bool disabled) { m_disabled = disabled; } + + CSSStyleSheet* parent_style_sheet() { return m_parent_style_sheet; } + void set_parent_css_style_sheet(CSSStyleSheet* sheet) { m_parent_style_sheet = sheet; } + protected: StyleSheet() = default; private: WeakPtr<DOM::Element> m_owner_node; + + // FIXME: Use WeakPtr. + CSSStyleSheet* m_parent_style_sheet { nullptr }; + + String m_title; + String m_type_string; + String m_media_string; + + bool m_disabled { false }; + bool m_alternate { false }; + bool m_origin_clean { true }; }; } diff --git a/Userland/Libraries/LibWeb/CSS/StyleSheet.idl b/Userland/Libraries/LibWeb/CSS/StyleSheet.idl index 20cfcfd8c1..9faecc36d7 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleSheet.idl +++ b/Userland/Libraries/LibWeb/CSS/StyleSheet.idl @@ -4,9 +4,9 @@ interface StyleSheet { readonly attribute CSSOMString type; // readonly attribute USVString? href; - // readonly attribute CSSStyleSheet? parentStyleSheet; - // readonly attribute DOMString? title; + readonly attribute CSSStyleSheet? parentStyleSheet; + readonly attribute DOMString? title; // [SameObject, PutForwards=mediaText] readonly attribute MediaList media; - // attribute boolean disabled; + attribute boolean disabled; }; diff --git a/Userland/Libraries/LibWeb/CSS/StyleSheetList.cpp b/Userland/Libraries/LibWeb/CSS/StyleSheetList.cpp index 8337be16c3..cf074746c9 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleSheetList.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleSheetList.cpp @@ -10,9 +10,15 @@ namespace Web::CSS { void StyleSheetList::add_sheet(NonnullRefPtr<CSSStyleSheet> sheet) { + VERIFY(!m_sheets.contains_slow(sheet)); m_sheets.append(move(sheet)); } +void StyleSheetList::remove_sheet(CSSStyleSheet& sheet) +{ + m_sheets.remove_first_matching([&](auto& entry) { return &*entry == &sheet; }); +} + StyleSheetList::StyleSheetList(DOM::Document& document) : m_document(document) { diff --git a/Userland/Libraries/LibWeb/CSS/StyleSheetList.h b/Userland/Libraries/LibWeb/CSS/StyleSheetList.h index 04ccf51ebb..15e7e8b417 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleSheetList.h +++ b/Userland/Libraries/LibWeb/CSS/StyleSheetList.h @@ -26,6 +26,8 @@ public: } void add_sheet(NonnullRefPtr<CSSStyleSheet>); + void remove_sheet(CSSStyleSheet&); + const NonnullRefPtrVector<CSSStyleSheet>& sheets() const { return m_sheets; } RefPtr<CSSStyleSheet> item(size_t index) const diff --git a/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.cpp index 379b1ed0a2..4ff31bd113 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.cpp @@ -5,20 +5,15 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include <AK/StringBuilder.h> +#include <LibWeb/CSS/Parser/Parser.h> #include <LibWeb/DOM/Document.h> -#include <LibWeb/DOM/Text.h> #include <LibWeb/HTML/HTMLStyleElement.h> namespace Web::HTML { HTMLStyleElement::HTMLStyleElement(DOM::Document& document, QualifiedName qualified_name) : HTMLElement(document, move(qualified_name)) - , m_css_loader(*this) { - m_css_loader.on_load = [&] { - document.update_style(); - }; } HTMLStyleElement::~HTMLStyleElement() @@ -27,25 +22,126 @@ HTMLStyleElement::~HTMLStyleElement() void HTMLStyleElement::children_changed() { - StringBuilder builder; - for_each_child([&](auto& child) { - if (is<DOM::Text>(child)) - builder.append(verify_cast<DOM::Text>(child).text_content()); - }); - m_css_loader.load_from_text(builder.to_string()); - - if (auto sheet = m_css_loader.style_sheet()) - document().style_sheets().add_sheet(sheet.release_nonnull()); + update_a_style_block(); HTMLElement::children_changed(); } void HTMLStyleElement::removed_from(Node* old_parent) { - if (m_css_loader.style_sheet()) { - // FIXME: Remove the sheet from the document - } + update_a_style_block(); return HTMLElement::removed_from(old_parent); } +// https://drafts.csswg.org/cssom/#remove-a-css-style-sheet +static void remove_a_css_style_sheet(DOM::Document& document, NonnullRefPtr<CSS::CSSStyleSheet> sheet) +{ + VERIFY(sheet.ptr()); + + // 1. Remove the CSS style sheet from the list of document or shadow root CSS style sheets. + document.style_sheets().remove_sheet(sheet); + + // 2. Set the CSS style sheetโs parent CSS style sheet, owner node and owner CSS rule to null. + sheet->set_parent_css_style_sheet(nullptr); + sheet->set_owner_node(nullptr); + sheet->set_owner_css_rule(nullptr); +} + +// https://drafts.csswg.org/cssom/#add-a-css-style-sheet +static void add_a_css_style_sheet(DOM::Document& document, NonnullRefPtr<CSS::CSSStyleSheet> sheet) +{ + // 1. Add the CSS style sheet to the list of document or shadow root CSS style sheets at the appropriate location. The remainder of these steps deal with the disabled flag. + document.style_sheets().add_sheet(sheet); + + // 2. If the disabled flag is set, then return. + if (sheet->disabled()) + return; + + // FIXME: 3. If the title is not the empty string, the alternate flag is unset, and preferred CSS style sheet set name is the empty string change the preferred CSS style sheet set name to the title. + + // FIXME: 4. If any of the following is true, then unset the disabled flag and return: + // The title is the empty string. + // The last CSS style sheet set name is null and the title is a case-sensitive match for the preferred CSS style sheet set name. + // The title is a case-sensitive match for the last CSS style sheet set name. + + // FIXME: 5. Set the disabled flag. +} + +// https://drafts.csswg.org/cssom/#create-a-css-style-sheet +static void create_a_css_style_sheet(DOM::Document& document, String type, DOM::Element* owner_node, String media, String title, bool alternate, bool origin_clean, void* location, CSS::CSSStyleSheet* parent_style_sheet, CSS::CSSRule* owner_rule, NonnullRefPtr<CSS::CSSStyleSheet> sheet) +{ + // 1. Create a new CSS style sheet object and set its properties as specified. + // FIXME: We receive `sheet` from the caller already. This is weird. + + sheet->set_parent_css_style_sheet(parent_style_sheet); + sheet->set_owner_css_rule(owner_rule); + sheet->set_owner_node(owner_node); + sheet->set_type(move(type)); + sheet->set_media(move(media)); + sheet->set_title(move(title)); + sheet->set_alternate(alternate); + sheet->set_origin_clean(origin_clean); + (void)location; + + // 2. Then run the add a CSS style sheet steps for the newly created CSS style sheet. + add_a_css_style_sheet(document, move(sheet)); +} + +// The user agent must run the "update a style block" algorithm whenever one of the following conditions occur: +// FIXME: The element is popped off the stack of open elements of an HTML parser or XML parser. +// +// NOTE: This is basically done by children_changed() today: +// The element's children changed steps run. +// +// NOTE: This is basically done by removed_from() today: +// The element is not on the stack of open elements of an HTML parser or XML parser, and it becomes connected or disconnected. +// +// https://html.spec.whatwg.org/multipage/semantics.html#update-a-style-block +void HTMLStyleElement::update_a_style_block() +{ + // 1. Let element be the style element. + // 2. If element has an associated CSS style sheet, remove the CSS style sheet in question. + + if (m_associated_css_style_sheet) { + remove_a_css_style_sheet(document(), *m_associated_css_style_sheet); + + // FIXME: This should probably be handled by StyleSheet::set_owner_node(). + m_associated_css_style_sheet = nullptr; + } + + // 3. If element is not connected, then return. + if (!is_connected()) + return; + + // 4. If element's type attribute is present and its value is neither the empty string nor an ASCII case-insensitive match for "text/css", then return. + auto type_attribute = attribute(HTML::AttributeNames::type); + if (!type_attribute.is_null() && !type_attribute.is_empty() && !type_attribute.equals_ignoring_case("text/css"sv)) + return; + + // FIXME: 5. If the Should element's inline behavior be blocked by Content Security Policy? algorithm returns "Blocked" when executed upon the style element, "style", and the style element's child text content, then return. [CSP] + + // FIXME: This is a bit awkward, as the spec doesn't actually tell us when to parse the CSS text, + // so we just do it here and pass the parsed sheet to create_a_css_style_sheet(). + auto sheet = parse_css(CSS::ParsingContext(document()), text_content()); + if (!sheet) + return; + + // FIXME: This should probably be handled by StyleSheet::set_owner_node(). + m_associated_css_style_sheet = sheet; + + // 6. Create a CSS style sheet with the following properties... + create_a_css_style_sheet( + document(), + "text/css"sv, + this, + attribute(HTML::AttributeNames::media), + in_a_document_tree() ? attribute(HTML::AttributeNames::title) : String::empty(), + false, + true, + nullptr, + nullptr, + nullptr, + sheet.release_nonnull()); +} + } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.h b/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.h index 8d42cb8986..9643e33ad3 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> * Copyright (c) 2021, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause @@ -8,7 +8,6 @@ #pragma once #include <LibWeb/HTML/HTMLElement.h> -#include <LibWeb/Loader/CSSLoader.h> namespace Web::HTML { @@ -22,8 +21,11 @@ public: virtual void children_changed() override; virtual void removed_from(Node*) override; + void update_a_style_block(); + private: - CSSLoader m_css_loader; + // https://drafts.csswg.org/cssom/#associated-css-style-sheet + RefPtr<CSS::CSSStyleSheet> m_associated_css_style_sheet; }; } |