diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp index 5b8cb0a690..40dad4019d 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -239,6 +239,10 @@ void StyleComputer::for_each_stylesheet(CascadeOrigin cascade_origin, Callback c callback(quirks_mode_stylesheet(document())); callback(mathml_stylesheet(document())); } + if (cascade_origin == CascadeOrigin::User) { + if (m_user_style_sheet) + callback(*m_user_style_sheet); + } if (cascade_origin == CascadeOrigin::Author) { for (auto const& sheet : document().style_sheets().sheets()) callback(*sheet); @@ -250,6 +254,8 @@ StyleComputer::RuleCache const& StyleComputer::rule_cache_for_cascade_origin(Cas switch (cascade_origin) { case CascadeOrigin::Author: return *m_author_rule_cache; + case CascadeOrigin::User: + return *m_user_rule_cache; case CascadeOrigin::UserAgent: return *m_user_agent_rule_cache; default: @@ -1720,19 +1726,21 @@ ErrorOr StyleComputer::compute_cascaded_values(StyleProperties& style, DOM MatchingRuleSet matching_rule_set; matching_rule_set.user_agent_rules = collect_matching_rules(element, CascadeOrigin::UserAgent, pseudo_element); sort_matching_rules(matching_rule_set.user_agent_rules); + matching_rule_set.user_rules = collect_matching_rules(element, CascadeOrigin::User, pseudo_element); + sort_matching_rules(matching_rule_set.user_rules); matching_rule_set.author_rules = collect_matching_rules(element, CascadeOrigin::Author, pseudo_element); sort_matching_rules(matching_rule_set.author_rules); if (mode == ComputeStyleMode::CreatePseudoElementStyleIfNeeded) { VERIFY(pseudo_element.has_value()); - if (matching_rule_set.author_rules.is_empty() && matching_rule_set.user_agent_rules.is_empty()) { + if (matching_rule_set.author_rules.is_empty() && matching_rule_set.user_rules.is_empty() && matching_rule_set.user_agent_rules.is_empty()) { did_match_any_pseudo_element_rules = false; return {}; } did_match_any_pseudo_element_rules = true; } - // Then we resolve all the CSS custom properties ("variables") for this element: + // Then we resolve all the CSS custom pr`operties ("variables") for this element: TRY(cascade_custom_properties(element, pseudo_element, matching_rule_set.author_rules)); // Then we apply the declarations from the matched rules in cascade order: @@ -1740,7 +1748,8 @@ ErrorOr StyleComputer::compute_cascaded_values(StyleProperties& style, DOM // Normal user agent declarations cascade_declarations(style, element, pseudo_element, matching_rule_set.user_agent_rules, CascadeOrigin::UserAgent, Important::No); - // FIXME: Normal user declarations + // Normal user declarations + cascade_declarations(style, element, pseudo_element, matching_rule_set.user_rules, CascadeOrigin::User, Important::No); // Author presentational hints (NOTE: The spec doesn't say exactly how to prioritize these.) if (!pseudo_element.has_value()) { @@ -1931,7 +1940,8 @@ ErrorOr StyleComputer::compute_cascaded_values(StyleProperties& style, DOM // Important author declarations cascade_declarations(style, element, pseudo_element, matching_rule_set.author_rules, CascadeOrigin::Author, Important::Yes); - // FIXME: Important user declarations + // Important user declarations + cascade_declarations(style, element, pseudo_element, matching_rule_set.user_rules, CascadeOrigin::User, Important::Yes); // Important user agent declarations cascade_declarations(style, element, pseudo_element, matching_rule_set.user_agent_rules, CascadeOrigin::UserAgent, Important::Yes); @@ -2582,7 +2592,7 @@ bool PropertyDependencyNode::has_cycles() void StyleComputer::build_rule_cache_if_needed() const { - if (m_author_rule_cache && m_user_agent_rule_cache) + if (m_author_rule_cache && m_user_rule_cache && m_user_agent_rule_cache) return; const_cast(*this).build_rule_cache(); } @@ -2752,7 +2762,15 @@ NonnullOwnPtr StyleComputer::make_rule_cache_for_casca void StyleComputer::build_rule_cache() { + // FIXME: How are we sometimes calculating style before the Document has a Page? + if (document().page()) { + if (auto user_style_source = document().page()->user_style(); user_style_source.has_value()) { + m_user_style_sheet = JS::make_handle(parse_css_stylesheet(CSS::Parser::ParsingContext(document()), user_style_source.value())); + } + } + m_author_rule_cache = make_rule_cache_for_cascade_origin(CascadeOrigin::Author); + m_user_rule_cache = make_rule_cache_for_cascade_origin(CascadeOrigin::User); m_user_agent_rule_cache = make_rule_cache_for_cascade_origin(CascadeOrigin::UserAgent); } @@ -2760,6 +2778,12 @@ void StyleComputer::invalidate_rule_cache() { m_author_rule_cache = nullptr; + // NOTE: We could be smarter about keeping the user rule cache, and style sheet. + // Currently we are re-parsing the user style sheet every time we build the caches, + // as it may have changed. + m_user_rule_cache = nullptr; + m_user_style_sheet = nullptr; + // NOTE: It might not be necessary to throw away the UA rule cache. // If we are sure that it's safe, we could keep it as an optimization. m_user_agent_rule_cache = nullptr; diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.h b/Userland/Libraries/LibWeb/CSS/StyleComputer.h index cf2a1c0f74..43b526ab02 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.h +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.h @@ -168,6 +168,7 @@ private: struct MatchingRuleSet { Vector user_agent_rules; + Vector user_rules; Vector author_rules; }; @@ -202,7 +203,9 @@ private: void ensure_animation_timer() const; OwnPtr m_author_rule_cache; + OwnPtr m_user_rule_cache; OwnPtr m_user_agent_rule_cache; + JS::Handle m_user_style_sheet; mutable FontCache m_font_cache; diff --git a/Userland/Libraries/LibWeb/Page/Page.cpp b/Userland/Libraries/LibWeb/Page/Page.cpp index 8df4154628..b3017d87e6 100644 --- a/Userland/Libraries/LibWeb/Page/Page.cpp +++ b/Userland/Libraries/LibWeb/Page/Page.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -372,6 +373,14 @@ JS::GCPtr Page::media_context_menu_element() return static_cast(dom_node); } +void Page::set_user_style(String source) +{ + m_user_style_sheet_source = source; + if (top_level_browsing_context_is_initialized() && top_level_browsing_context().active_document()) { + top_level_browsing_context().active_document()->style_computer().invalidate_rule_cache(); + } +} + } template<> diff --git a/Userland/Libraries/LibWeb/Page/Page.h b/Userland/Libraries/LibWeb/Page/Page.h index a23a09ebba..492cab0967 100644 --- a/Userland/Libraries/LibWeb/Page/Page.h +++ b/Userland/Libraries/LibWeb/Page/Page.h @@ -135,6 +135,9 @@ public: WebIDL::ExceptionOr toggle_media_loop_state(); WebIDL::ExceptionOr toggle_media_controls_state(); + Optional const& user_style() const { return m_user_style_sheet_source; } + void set_user_style(String source); + bool pdf_viewer_supported() const { return m_pdf_viewer_supported; } private: @@ -167,6 +170,8 @@ private: Optional m_media_context_menu_element_id; + Optional m_user_style_sheet_source; + // https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewer-supported // Each user agent has a PDF viewer supported boolean, whose value is implementation-defined (and might vary according to user preferences). // Spec Note: This value also impacts the navigation processing model.