CSSImportRule.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. /*
  2. * Copyright (c) 2021, the SerenityOS developers.
  3. * Copyright (c) 2021, Sam Atkins <atkinssj@serenityos.org>
  4. * Copyright (c) 2022-2024, Andreas Kling <andreas@ladybird.org>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/Debug.h>
  9. #include <LibTextCodec/Decoder.h>
  10. #include <LibURL/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/CSS/StyleComputer.h>
  16. #include <LibWeb/DOM/Document.h>
  17. #include <LibWeb/HTML/Window.h>
  18. #include <LibWeb/Loader/ResourceLoader.h>
  19. namespace Web::CSS {
  20. JS_DEFINE_ALLOCATOR(CSSImportRule);
  21. JS::NonnullGCPtr<CSSImportRule> CSSImportRule::create(URL::URL url, DOM::Document& document)
  22. {
  23. auto& realm = document.realm();
  24. return realm.heap().allocate<CSSImportRule>(realm, move(url), document);
  25. }
  26. CSSImportRule::CSSImportRule(URL::URL url, DOM::Document& document)
  27. : CSSRule(document.realm())
  28. , m_url(move(url))
  29. , m_document(document)
  30. {
  31. dbgln_if(CSS_LOADER_DEBUG, "CSSImportRule: Loading import URL: {}", m_url);
  32. auto request = LoadRequest::create_for_url_on_page(m_url, &document.page());
  33. // NOTE: Mark this rule as delaying the document load event *before* calling set_resource()
  34. // as it may trigger a synchronous resource_did_load() callback.
  35. m_document_load_event_delayer.emplace(document);
  36. set_resource(ResourceLoader::the().load_resource(Resource::Type::Generic, request));
  37. }
  38. void CSSImportRule::initialize(JS::Realm& realm)
  39. {
  40. Base::initialize(realm);
  41. WEB_SET_PROTOTYPE_FOR_INTERFACE(CSSImportRule);
  42. }
  43. void CSSImportRule::visit_edges(Cell::Visitor& visitor)
  44. {
  45. Base::visit_edges(visitor);
  46. visitor.visit(m_document);
  47. visitor.visit(m_style_sheet);
  48. }
  49. // https://www.w3.org/TR/cssom/#serialize-a-css-rule
  50. String CSSImportRule::serialized() const
  51. {
  52. StringBuilder builder;
  53. // The result of concatenating the following:
  54. // 1. The string "@import" followed by a single SPACE (U+0020).
  55. builder.append("@import "sv);
  56. // 2. The result of performing serialize a URL on the rule’s location.
  57. serialize_a_url(builder, MUST(m_url.to_string()));
  58. // 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.
  59. // 4. The string ";", i.e., SEMICOLON (U+003B).
  60. builder.append(';');
  61. return MUST(builder.to_string());
  62. }
  63. void CSSImportRule::resource_did_fail()
  64. {
  65. dbgln_if(CSS_LOADER_DEBUG, "CSSImportRule: Resource did fail. URL: {}", resource()->url());
  66. m_document_load_event_delayer.clear();
  67. }
  68. void CSSImportRule::resource_did_load()
  69. {
  70. VERIFY(resource());
  71. if (!m_document)
  72. return;
  73. m_document_load_event_delayer.clear();
  74. if (!resource()->has_encoded_data()) {
  75. dbgln_if(CSS_LOADER_DEBUG, "CSSImportRule: Resource did load, no encoded data. URL: {}", resource()->url());
  76. } else {
  77. dbgln_if(CSS_LOADER_DEBUG, "CSSImportRule: Resource did load, has encoded data. URL: {}", resource()->url());
  78. }
  79. // FIXME: The fallback here should be the `environment encoding` of the importing style sheet.
  80. auto encoding = resource()->encoding().value_or("utf-8");
  81. auto maybe_decoder = TextCodec::decoder_for(encoding);
  82. if (!maybe_decoder.has_value()) {
  83. dbgln("CSSImportRule: Failed to decode CSS file: {} Unsupported encoding: {}", resource()->url(), encoding);
  84. return;
  85. }
  86. auto& decoder = maybe_decoder.release_value();
  87. auto decoded_or_error = TextCodec::convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark(decoder, resource()->encoded_data());
  88. if (decoded_or_error.is_error()) {
  89. dbgln("CSSImportRule: Failed to decode CSS file: {} Encoding was: {}", resource()->url(), encoding);
  90. return;
  91. }
  92. auto decoded = decoded_or_error.release_value();
  93. auto* sheet = parse_css_stylesheet(CSS::Parser::ParsingContext(*m_document, resource()->url()), decoded, resource()->url());
  94. if (!sheet) {
  95. dbgln_if(CSS_LOADER_DEBUG, "CSSImportRule: Failed to parse stylesheet: {}", resource()->url());
  96. return;
  97. }
  98. m_style_sheet = sheet;
  99. m_style_sheet->set_owner_css_rule(this);
  100. m_document->style_computer().invalidate_rule_cache();
  101. m_document->style_computer().load_fonts_from_sheet(*m_style_sheet);
  102. m_document->invalidate_style(DOM::StyleInvalidationReason::CSSImportRule);
  103. }
  104. }