Serenity Operating System
1/*
2 * Copyright (c) 2021, the SerenityOS developers.
3 * Copyright (c) 2021, Sam Atkins <atkinssj@serenityos.org>
4 * Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
5 *
6 * SPDX-License-Identifier: BSD-2-Clause
7 */
8
9#include <AK/Debug.h>
10#include <AK/URL.h>
11#include <LibWeb/Bindings/CSSImportRulePrototype.h>
12#include <LibWeb/Bindings/Intrinsics.h>
13#include <LibWeb/CSS/CSSImportRule.h>
14#include <LibWeb/CSS/Parser/Parser.h>
15#include <LibWeb/DOM/Document.h>
16#include <LibWeb/HTML/Window.h>
17#include <LibWeb/Loader/ResourceLoader.h>
18
19namespace Web::CSS {
20
21WebIDL::ExceptionOr<JS::NonnullGCPtr<CSSImportRule>> CSSImportRule::create(AK::URL url, DOM::Document& document)
22{
23 auto& realm = document.realm();
24 return MUST_OR_THROW_OOM(realm.heap().allocate<CSSImportRule>(realm, move(url), document));
25}
26
27CSSImportRule::CSSImportRule(AK::URL url, DOM::Document& document)
28 : CSSRule(document.realm())
29 , m_url(move(url))
30 , m_document(document)
31{
32 dbgln_if(CSS_LOADER_DEBUG, "CSSImportRule: Loading import URL: {}", m_url);
33 auto request = LoadRequest::create_for_url_on_page(m_url, document.page());
34
35 // NOTE: Mark this rule as delaying the document load event *before* calling set_resource()
36 // as it may trigger a synchronous resource_did_load() callback.
37 m_document_load_event_delayer.emplace(document);
38
39 set_resource(ResourceLoader::the().load_resource(Resource::Type::Generic, request));
40}
41
42JS::ThrowCompletionOr<void> CSSImportRule::initialize(JS::Realm& realm)
43{
44 MUST_OR_THROW_OOM(Base::initialize(realm));
45 set_prototype(&Bindings::ensure_web_prototype<Bindings::CSSImportRulePrototype>(realm, "CSSImportRule"));
46
47 return {};
48}
49
50void CSSImportRule::visit_edges(Cell::Visitor& visitor)
51{
52 Base::visit_edges(visitor);
53 visitor.visit(m_document);
54 visitor.visit(m_style_sheet);
55}
56
57// https://www.w3.org/TR/cssom/#serialize-a-css-rule
58DeprecatedString CSSImportRule::serialized() const
59{
60 StringBuilder builder;
61 // The result of concatenating the following:
62
63 // 1. The string "@import" followed by a single SPACE (U+0020).
64 builder.append("@import "sv);
65
66 // 2. The result of performing serialize a URL on the rule’s location.
67 // FIXME: Look into the correctness of this serialization
68 builder.append("url("sv);
69 builder.append(m_url.to_deprecated_string());
70 builder.append(')');
71
72 // FIXME: 3. If the rule’s associated media list is not empty, a single SPACE (U+0020) followed by the result of performing serialize a media query list on the media list.
73
74 // 4. The string ";", i.e., SEMICOLON (U+003B).
75 builder.append(';');
76
77 return builder.to_deprecated_string();
78}
79
80void CSSImportRule::resource_did_fail()
81{
82 dbgln_if(CSS_LOADER_DEBUG, "CSSImportRule: Resource did fail. URL: {}", resource()->url());
83
84 m_document_load_event_delayer.clear();
85}
86
87void CSSImportRule::resource_did_load()
88{
89 VERIFY(resource());
90
91 if (!m_document)
92 return;
93
94 m_document_load_event_delayer.clear();
95
96 if (!resource()->has_encoded_data()) {
97 dbgln_if(CSS_LOADER_DEBUG, "CSSImportRule: Resource did load, no encoded data. URL: {}", resource()->url());
98 } else {
99 dbgln_if(CSS_LOADER_DEBUG, "CSSImportRule: Resource did load, has encoded data. URL: {}", resource()->url());
100 }
101
102 auto* sheet = parse_css_stylesheet(CSS::Parser::ParsingContext(*m_document, resource()->url()), resource()->encoded_data());
103 if (!sheet) {
104 dbgln_if(CSS_LOADER_DEBUG, "CSSImportRule: Failed to parse stylesheet: {}", resource()->url());
105 return;
106 }
107
108 m_style_sheet = sheet;
109
110 m_document->style_computer().invalidate_rule_cache();
111 m_document->invalidate_style();
112}
113
114}