Serenity Operating System
at master 133 lines 4.7 kB view raw
1/* 2 * Copyright (c) 2019-2022, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <LibWeb/Bindings/CSSStyleSheetPrototype.h> 8#include <LibWeb/Bindings/Intrinsics.h> 9#include <LibWeb/CSS/CSSStyleSheet.h> 10#include <LibWeb/CSS/Parser/Parser.h> 11#include <LibWeb/CSS/StyleSheetList.h> 12#include <LibWeb/DOM/Document.h> 13#include <LibWeb/WebIDL/ExceptionOr.h> 14 15namespace Web::CSS { 16 17WebIDL::ExceptionOr<JS::NonnullGCPtr<CSSStyleSheet>> CSSStyleSheet::create(JS::Realm& realm, CSSRuleList& rules, MediaList& media, Optional<AK::URL> location) 18{ 19 return MUST_OR_THROW_OOM(realm.heap().allocate<CSSStyleSheet>(realm, realm, rules, media, move(location))); 20} 21 22CSSStyleSheet::CSSStyleSheet(JS::Realm& realm, CSSRuleList& rules, MediaList& media, Optional<AK::URL> location) 23 : StyleSheet(realm, media) 24 , m_rules(&rules) 25{ 26 if (location.has_value()) 27 set_location(location->to_deprecated_string()); 28 29 for (auto& rule : *m_rules) 30 rule.set_parent_style_sheet(this); 31} 32 33JS::ThrowCompletionOr<void> CSSStyleSheet::initialize(JS::Realm& realm) 34{ 35 MUST_OR_THROW_OOM(Base::initialize(realm)); 36 set_prototype(&Bindings::ensure_web_prototype<Bindings::CSSStyleSheetPrototype>(realm, "CSSStyleSheet")); 37 38 return {}; 39} 40 41void CSSStyleSheet::visit_edges(Cell::Visitor& visitor) 42{ 43 Base::visit_edges(visitor); 44 visitor.visit(m_style_sheet_list.ptr()); 45 visitor.visit(m_rules); 46 visitor.visit(m_owner_css_rule); 47} 48 49// https://www.w3.org/TR/cssom/#dom-cssstylesheet-insertrule 50WebIDL::ExceptionOr<unsigned> CSSStyleSheet::insert_rule(StringView rule, unsigned index) 51{ 52 // FIXME: 1. If the origin-clean flag is unset, throw a SecurityError exception. 53 54 // FIXME: 2. If the disallow modification flag is set, throw a NotAllowedError DOMException. 55 56 // 3. Let parsed rule be the return value of invoking parse a rule with rule. 57 auto context = m_style_sheet_list ? CSS::Parser::ParsingContext { m_style_sheet_list->document() } : CSS::Parser::ParsingContext { realm() }; 58 auto parsed_rule = parse_css_rule(context, rule); 59 60 // 4. If parsed rule is a syntax error, return parsed rule. 61 if (!parsed_rule) 62 return WebIDL::SyntaxError::create(realm(), "Unable to parse CSS rule."); 63 64 // FIXME: 5. If parsed rule is an @import rule, and the constructed flag is set, throw a SyntaxError DOMException. 65 66 // 6. Return the result of invoking insert a CSS rule rule in the CSS rules at index. 67 auto result = m_rules->insert_a_css_rule(parsed_rule, index); 68 69 if (!result.is_exception()) { 70 // NOTE: The spec doesn't say where to set the parent style sheet, so we'll do it here. 71 parsed_rule->set_parent_style_sheet(this); 72 73 if (m_style_sheet_list) { 74 m_style_sheet_list->document().style_computer().invalidate_rule_cache(); 75 m_style_sheet_list->document().invalidate_style(); 76 } 77 } 78 79 return result; 80} 81 82// https://www.w3.org/TR/cssom/#dom-cssstylesheet-deleterule 83WebIDL::ExceptionOr<void> CSSStyleSheet::delete_rule(unsigned index) 84{ 85 // FIXME: 1. If the origin-clean flag is unset, throw a SecurityError exception. 86 87 // FIXME: 2. If the disallow modification flag is set, throw a NotAllowedError DOMException. 88 89 // 3. Remove a CSS rule in the CSS rules at index. 90 auto result = m_rules->remove_a_css_rule(index); 91 if (!result.is_exception()) { 92 if (m_style_sheet_list) { 93 m_style_sheet_list->document().style_computer().invalidate_rule_cache(); 94 m_style_sheet_list->document().invalidate_style(); 95 } 96 } 97 return result; 98} 99 100// https://www.w3.org/TR/cssom/#dom-cssstylesheet-removerule 101WebIDL::ExceptionOr<void> CSSStyleSheet::remove_rule(unsigned index) 102{ 103 // The removeRule(index) method must run the same steps as deleteRule(). 104 return delete_rule(index); 105} 106 107void CSSStyleSheet::for_each_effective_style_rule(Function<void(CSSStyleRule const&)> const& callback) const 108{ 109 if (m_media.matches()) { 110 m_rules->for_each_effective_style_rule(callback); 111 } 112} 113 114bool CSSStyleSheet::evaluate_media_queries(HTML::Window const& window) 115{ 116 bool any_media_queries_changed_match_state = false; 117 118 bool did_match = m_media.matches(); 119 bool now_matches = m_media.evaluate(window); 120 if (did_match != now_matches) 121 any_media_queries_changed_match_state = true; 122 if (now_matches && m_rules->evaluate_media_queries(window)) 123 any_media_queries_changed_match_state = true; 124 125 return any_media_queries_changed_match_state; 126} 127 128void CSSStyleSheet::set_style_sheet_list(Badge<StyleSheetList>, StyleSheetList* list) 129{ 130 m_style_sheet_list = list; 131} 132 133}