DocumentLoading.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/Debug.h>
  8. #include <AK/LexicalPath.h>
  9. #include <LibGemini/Document.h>
  10. #include <LibGfx/ImageFormats/ImageDecoder.h>
  11. #include <LibMarkdown/Document.h>
  12. #include <LibTextCodec/Decoder.h>
  13. #include <LibWeb/DOM/Document.h>
  14. #include <LibWeb/DOM/DocumentLoading.h>
  15. #include <LibWeb/HTML/NavigationParams.h>
  16. #include <LibWeb/HTML/Parser/HTMLEncodingDetection.h>
  17. #include <LibWeb/HTML/Parser/HTMLParser.h>
  18. #include <LibWeb/Namespace.h>
  19. #include <LibWeb/Platform/ImageCodecPlugin.h>
  20. #include <LibWeb/XML/XMLDocumentBuilder.h>
  21. namespace Web {
  22. static bool build_markdown_document(DOM::Document& document, ByteBuffer const& data)
  23. {
  24. auto markdown_document = Markdown::Document::parse(data);
  25. if (!markdown_document)
  26. return false;
  27. auto extra_head_contents = R"~~~(
  28. <style>
  29. .zoomable {
  30. cursor: zoom-in;
  31. max-width: 100%;
  32. }
  33. .zoomable.zoomed-in {
  34. cursor: zoom-out;
  35. max-width: none;
  36. }
  37. </style>
  38. <script>
  39. function imageClickEventListener(event) {
  40. let image = event.target;
  41. if (image.classList.contains("zoomable")) {
  42. image.classList.toggle("zoomed-in");
  43. }
  44. }
  45. function processImages() {
  46. let images = document.querySelectorAll("img");
  47. let windowWidth = window.innerWidth;
  48. images.forEach((image) => {
  49. if (image.naturalWidth > windowWidth) {
  50. image.classList.add("zoomable");
  51. } else {
  52. image.classList.remove("zoomable");
  53. image.classList.remove("zoomed-in");
  54. }
  55. image.addEventListener("click", imageClickEventListener);
  56. });
  57. }
  58. document.addEventListener("load", () => {
  59. processImages();
  60. });
  61. window.addEventListener("resize", () => {
  62. processImages();
  63. });
  64. </script>
  65. )~~~"sv;
  66. auto parser = HTML::HTMLParser::create(document, markdown_document->render_to_html(extra_head_contents), "utf-8");
  67. parser->run(document.url());
  68. return true;
  69. }
  70. static bool build_text_document(DOM::Document& document, ByteBuffer const& data)
  71. {
  72. auto html_element = DOM::create_element(document, HTML::TagNames::html, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  73. MUST(document.append_child(html_element));
  74. auto head_element = DOM::create_element(document, HTML::TagNames::head, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  75. MUST(html_element->append_child(head_element));
  76. auto title_element = DOM::create_element(document, HTML::TagNames::title, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  77. MUST(head_element->append_child(title_element));
  78. auto title_text = document.create_text_node(document.url().basename());
  79. MUST(title_element->append_child(title_text));
  80. auto body_element = DOM::create_element(document, HTML::TagNames::body, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  81. MUST(html_element->append_child(body_element));
  82. auto pre_element = DOM::create_element(document, HTML::TagNames::pre, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  83. MUST(body_element->append_child(pre_element));
  84. MUST(pre_element->append_child(document.create_text_node(DeprecatedString::copy(data))));
  85. return true;
  86. }
  87. static bool build_image_document(DOM::Document& document, ByteBuffer const& data)
  88. {
  89. auto image = Platform::ImageCodecPlugin::the().decode_image(data);
  90. if (!image.has_value() || image->frames.is_empty())
  91. return false;
  92. auto const& frame = image->frames[0];
  93. auto const& bitmap = frame.bitmap;
  94. if (!bitmap)
  95. return false;
  96. auto html_element = DOM::create_element(document, HTML::TagNames::html, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  97. MUST(document.append_child(html_element));
  98. auto head_element = DOM::create_element(document, HTML::TagNames::head, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  99. MUST(html_element->append_child(head_element));
  100. auto title_element = DOM::create_element(document, HTML::TagNames::title, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  101. MUST(head_element->append_child(title_element));
  102. auto basename = LexicalPath::basename(document.url().serialize_path());
  103. auto title_text = document.heap().allocate<DOM::Text>(document.realm(), document, DeprecatedString::formatted("{} [{}x{}]", basename, bitmap->width(), bitmap->height())).release_allocated_value_but_fixme_should_propagate_errors();
  104. MUST(title_element->append_child(*title_text));
  105. auto body_element = DOM::create_element(document, HTML::TagNames::body, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  106. MUST(html_element->append_child(body_element));
  107. auto image_element = DOM::create_element(document, HTML::TagNames::img, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  108. MUST(image_element->set_attribute(HTML::AttributeNames::src, document.url().to_deprecated_string()));
  109. MUST(body_element->append_child(image_element));
  110. return true;
  111. }
  112. static bool build_gemini_document(DOM::Document& document, ByteBuffer const& data)
  113. {
  114. StringView gemini_data { data };
  115. auto gemini_document = Gemini::Document::parse(gemini_data, document.url());
  116. DeprecatedString html_data = gemini_document->render_to_html();
  117. dbgln_if(GEMINI_DEBUG, "Gemini data:\n\"\"\"{}\"\"\"", gemini_data);
  118. dbgln_if(GEMINI_DEBUG, "Converted to HTML:\n\"\"\"{}\"\"\"", html_data);
  119. auto parser = HTML::HTMLParser::create(document, html_data, "utf-8");
  120. parser->run(document.url());
  121. return true;
  122. }
  123. static bool build_xml_document(DOM::Document& document, ByteBuffer const& data)
  124. {
  125. auto encoding = HTML::run_encoding_sniffing_algorithm(document, data);
  126. auto decoder = TextCodec::decoder_for(encoding);
  127. VERIFY(decoder.has_value());
  128. auto source = decoder->to_utf8(data).release_value_but_fixme_should_propagate_errors();
  129. XML::Parser parser(source, { .resolve_external_resource = resolve_xml_resource });
  130. XMLDocumentBuilder builder { document };
  131. auto result = parser.parse_with_listener(builder);
  132. return !result.is_error() && !builder.has_error();
  133. }
  134. static bool build_video_document(DOM::Document& document)
  135. {
  136. auto html_element = DOM::create_element(document, HTML::TagNames::html, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  137. MUST(document.append_child(html_element));
  138. auto head_element = DOM::create_element(document, HTML::TagNames::head, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  139. MUST(html_element->append_child(head_element));
  140. auto body_element = DOM::create_element(document, HTML::TagNames::body, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  141. MUST(html_element->append_child(body_element));
  142. auto video_element = DOM::create_element(document, HTML::TagNames::video, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  143. MUST(video_element->set_attribute(HTML::AttributeNames::src, document.url().to_deprecated_string()));
  144. MUST(video_element->set_attribute(HTML::AttributeNames::autoplay, DeprecatedString::empty()));
  145. MUST(video_element->set_attribute(HTML::AttributeNames::controls, DeprecatedString::empty()));
  146. MUST(body_element->append_child(video_element));
  147. return true;
  148. }
  149. bool parse_document(DOM::Document& document, ByteBuffer const& data)
  150. {
  151. auto& mime_type = document.content_type();
  152. if (mime_type == "text/html" || mime_type == "image/svg+xml") {
  153. auto parser = HTML::HTMLParser::create_with_uncertain_encoding(document, data);
  154. parser->run(document.url());
  155. return true;
  156. }
  157. if (mime_type.ends_with("+xml"sv) || mime_type.is_one_of("text/xml", "application/xml"))
  158. return build_xml_document(document, data);
  159. if (mime_type.starts_with("image/"sv))
  160. return build_image_document(document, data);
  161. if (mime_type.starts_with("video/"sv))
  162. return build_video_document(document);
  163. if (mime_type == "text/plain" || mime_type == "application/json")
  164. return build_text_document(document, data);
  165. if (mime_type == "text/markdown")
  166. return build_markdown_document(document, data);
  167. if (mime_type == "text/gemini")
  168. return build_gemini_document(document, data);
  169. return false;
  170. }
  171. }