DocumentLoading.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
  4. * Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/Debug.h>
  9. #include <AK/LexicalPath.h>
  10. #include <LibGemini/Document.h>
  11. #include <LibGfx/ImageFormats/ImageDecoder.h>
  12. #include <LibMarkdown/Document.h>
  13. #include <LibTextCodec/Decoder.h>
  14. #include <LibWeb/DOM/Document.h>
  15. #include <LibWeb/DOM/DocumentLoading.h>
  16. #include <LibWeb/HTML/HTMLHeadElement.h>
  17. #include <LibWeb/HTML/Navigable.h>
  18. #include <LibWeb/HTML/NavigationParams.h>
  19. #include <LibWeb/HTML/Parser/HTMLEncodingDetection.h>
  20. #include <LibWeb/HTML/Parser/HTMLParser.h>
  21. #include <LibWeb/Loader/GeneratedPagesLoader.h>
  22. #include <LibWeb/Namespace.h>
  23. #include <LibWeb/Platform/ImageCodecPlugin.h>
  24. #include <LibWeb/XML/XMLDocumentBuilder.h>
  25. namespace Web {
  26. static bool build_markdown_document(DOM::Document& document, ByteBuffer const& data)
  27. {
  28. auto markdown_document = Markdown::Document::parse(data);
  29. if (!markdown_document)
  30. return false;
  31. auto extra_head_contents = R"~~~(
  32. <style>
  33. .zoomable {
  34. cursor: zoom-in;
  35. max-width: 100%;
  36. }
  37. .zoomable.zoomed-in {
  38. cursor: zoom-out;
  39. max-width: none;
  40. }
  41. </style>
  42. <script>
  43. function imageClickEventListener(event) {
  44. let image = event.target;
  45. if (image.classList.contains("zoomable")) {
  46. image.classList.toggle("zoomed-in");
  47. }
  48. }
  49. function processImages() {
  50. let images = document.querySelectorAll("img");
  51. let windowWidth = window.innerWidth;
  52. images.forEach((image) => {
  53. if (image.naturalWidth > windowWidth) {
  54. image.classList.add("zoomable");
  55. } else {
  56. image.classList.remove("zoomable");
  57. image.classList.remove("zoomed-in");
  58. }
  59. image.addEventListener("click", imageClickEventListener);
  60. });
  61. }
  62. document.addEventListener("load", () => {
  63. processImages();
  64. });
  65. window.addEventListener("resize", () => {
  66. processImages();
  67. });
  68. </script>
  69. )~~~"sv;
  70. auto parser = HTML::HTMLParser::create(document, markdown_document->render_to_html(extra_head_contents), "utf-8");
  71. parser->run(document.url());
  72. return true;
  73. }
  74. static bool build_image_document(DOM::Document& document, ByteBuffer const& data)
  75. {
  76. auto image = Platform::ImageCodecPlugin::the().decode_image(data);
  77. if (!image.has_value() || image->frames.is_empty())
  78. return false;
  79. auto const& frame = image->frames[0];
  80. auto const& bitmap = frame.bitmap;
  81. if (!bitmap)
  82. return false;
  83. auto html_element = DOM::create_element(document, HTML::TagNames::html, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  84. MUST(document.append_child(html_element));
  85. auto head_element = DOM::create_element(document, HTML::TagNames::head, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  86. MUST(html_element->append_child(head_element));
  87. auto title_element = DOM::create_element(document, HTML::TagNames::title, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  88. MUST(head_element->append_child(title_element));
  89. auto basename = LexicalPath::basename(document.url().serialize_path());
  90. auto title_text = document.heap().allocate<DOM::Text>(document.realm(), document, MUST(String::formatted("{} [{}x{}]", basename, bitmap->width(), bitmap->height())));
  91. MUST(title_element->append_child(*title_text));
  92. auto body_element = DOM::create_element(document, HTML::TagNames::body, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  93. MUST(html_element->append_child(body_element));
  94. auto image_element = DOM::create_element(document, HTML::TagNames::img, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  95. MUST(image_element->set_attribute(HTML::AttributeNames::src, MUST(document.url().to_string())));
  96. MUST(body_element->append_child(image_element));
  97. return true;
  98. }
  99. static bool build_gemini_document(DOM::Document& document, ByteBuffer const& data)
  100. {
  101. StringView gemini_data { data };
  102. auto gemini_document = Gemini::Document::parse(gemini_data, document.url());
  103. ByteString html_data = gemini_document->render_to_html();
  104. dbgln_if(GEMINI_DEBUG, "Gemini data:\n\"\"\"{}\"\"\"", gemini_data);
  105. dbgln_if(GEMINI_DEBUG, "Converted to HTML:\n\"\"\"{}\"\"\"", html_data);
  106. auto parser = HTML::HTMLParser::create(document, html_data, "utf-8");
  107. parser->run(document.url());
  108. return true;
  109. }
  110. bool build_xml_document(DOM::Document& document, ByteBuffer const& data, Optional<String> content_encoding)
  111. {
  112. Optional<TextCodec::Decoder&> decoder;
  113. // The actual HTTP headers and other metadata, not the headers as mutated or implied by the algorithms given in this specification,
  114. // are the ones that must be used when determining the character encoding according to the rules given in the above specifications.
  115. if (content_encoding.has_value())
  116. decoder = TextCodec::decoder_for(*content_encoding);
  117. if (!decoder.has_value()) {
  118. auto encoding = HTML::run_encoding_sniffing_algorithm(document, data);
  119. decoder = TextCodec::decoder_for(encoding);
  120. }
  121. VERIFY(decoder.has_value());
  122. // Well-formed XML documents contain only properly encoded characters
  123. if (!decoder->validate(data))
  124. return false;
  125. auto source = decoder->to_utf8(data).release_value_but_fixme_should_propagate_errors();
  126. XML::Parser parser(source, { .resolve_external_resource = resolve_xml_resource });
  127. XMLDocumentBuilder builder { document };
  128. auto result = parser.parse_with_listener(builder);
  129. return !result.is_error() && !builder.has_error();
  130. }
  131. static bool build_video_document(DOM::Document& document)
  132. {
  133. auto html_element = DOM::create_element(document, HTML::TagNames::html, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  134. MUST(document.append_child(html_element));
  135. auto head_element = DOM::create_element(document, HTML::TagNames::head, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  136. MUST(html_element->append_child(head_element));
  137. auto body_element = DOM::create_element(document, HTML::TagNames::body, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  138. MUST(html_element->append_child(body_element));
  139. auto video_element = DOM::create_element(document, HTML::TagNames::video, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  140. MUST(video_element->set_attribute(HTML::AttributeNames::src, MUST(document.url().to_string())));
  141. MUST(video_element->set_attribute(HTML::AttributeNames::autoplay, String {}));
  142. MUST(video_element->set_attribute(HTML::AttributeNames::controls, String {}));
  143. MUST(body_element->append_child(video_element));
  144. return true;
  145. }
  146. static bool build_audio_document(DOM::Document& document)
  147. {
  148. auto html_element = DOM::create_element(document, HTML::TagNames::html, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  149. MUST(document.append_child(html_element));
  150. auto head_element = DOM::create_element(document, HTML::TagNames::head, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  151. MUST(html_element->append_child(head_element));
  152. auto body_element = DOM::create_element(document, HTML::TagNames::body, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  153. MUST(html_element->append_child(body_element));
  154. auto video_element = DOM::create_element(document, HTML::TagNames::audio, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
  155. MUST(video_element->set_attribute(HTML::AttributeNames::src, MUST(document.url().to_string())));
  156. MUST(video_element->set_attribute(HTML::AttributeNames::autoplay, String {}));
  157. MUST(video_element->set_attribute(HTML::AttributeNames::controls, String {}));
  158. MUST(body_element->append_child(video_element));
  159. return true;
  160. }
  161. bool parse_document(DOM::Document& document, ByteBuffer const& data, [[maybe_unused]] Optional<String> content_encoding)
  162. {
  163. auto& mime_type = document.content_type();
  164. if (mime_type.starts_with_bytes("image/"sv))
  165. return build_image_document(document, data);
  166. if (mime_type.starts_with_bytes("video/"sv))
  167. return build_video_document(document);
  168. if (mime_type.starts_with_bytes("audio/"sv))
  169. return build_audio_document(document);
  170. if (mime_type == "text/markdown")
  171. return build_markdown_document(document, data);
  172. if (mime_type == "text/gemini")
  173. return build_gemini_document(document, data);
  174. return false;
  175. }
  176. static bool is_supported_document_mime_type(StringView mime_type)
  177. {
  178. if (mime_type == "text/html")
  179. return true;
  180. if (mime_type.ends_with("+xml"sv) || mime_type.is_one_of("text/xml", "application/xml"))
  181. return true;
  182. if (mime_type.starts_with("image/"sv))
  183. return true;
  184. if (mime_type.starts_with("video/"sv))
  185. return true;
  186. if (mime_type.starts_with("audio/"sv))
  187. return true;
  188. if (mime_type == "text/plain" || mime_type == "application/json")
  189. return true;
  190. if (mime_type == "text/markdown")
  191. return true;
  192. if (mime_type == "text/gemini")
  193. return true;
  194. return false;
  195. }
  196. // https://html.spec.whatwg.org/multipage/document-lifecycle.html#navigate-html
  197. static WebIDL::ExceptionOr<JS::NonnullGCPtr<DOM::Document>> load_html_document(HTML::NavigationParams& navigation_params)
  198. {
  199. // To load an HTML document, given navigation params navigationParams:
  200. // 1. Let document be the result of creating and initializing a Document object given "html", "text/html", and navigationParams.
  201. auto document = TRY(DOM::Document::create_and_initialize(DOM::Document::Type::HTML, "text/html"_string, navigation_params));
  202. // 2. If document's URL is about:blank, then populate with html/head/body given document.
  203. // FIXME: The additional check for a non-empty body fixes issues with loading javascript urls in iframes, which
  204. // default to an "about:blank" url. Is this a spec bug?
  205. if (document->url_string() == "about:blank"_string
  206. && navigation_params.response->body()->length().value_or(0) == 0) {
  207. TRY(document->populate_with_html_head_and_body());
  208. // Nothing else is added to the document, so mark it as loaded.
  209. HTML::HTMLParser::the_end(document);
  210. }
  211. // 3. Otherwise, create an HTML parser and associate it with the document.
  212. // Each task that the networking task source places on the task queue while fetching runs must then fill the
  213. // parser's input byte stream with the fetched bytes and cause the HTML parser to perform the appropriate
  214. // processing of the input stream.
  215. // The first task that the networking task source places on the task queue while fetching runs must process link
  216. // headers given document, navigationParams's response, and "media", after the task has been processed by the
  217. // HTML parser.
  218. // Before any script execution occurs, the user agent must wait for scripts may run for the newly-created
  219. // document to be true for document.
  220. // When no more bytes are available, the user agent must queue a global task on the networking task source given
  221. // document's relevant global object to have the parser to process the implied EOF character, which eventually
  222. // causes a load event to be fired.
  223. else {
  224. // FIXME: Parse as we receive the document data, instead of waiting for the whole document to be fetched first.
  225. auto process_body = [document, url = navigation_params.response->url().value()](ByteBuffer data) {
  226. auto parser = HTML::HTMLParser::create_with_uncertain_encoding(document, data);
  227. parser->run(url);
  228. };
  229. auto process_body_error = [](auto) {
  230. dbgln("FIXME: Load html page with an error if read of body failed.");
  231. };
  232. auto& realm = document->realm();
  233. TRY(navigation_params.response->body()->fully_read(realm, move(process_body), move(process_body_error), JS::NonnullGCPtr { realm.global_object() }));
  234. }
  235. // 4. Return document.
  236. return document;
  237. }
  238. // https://html.spec.whatwg.org/multipage/document-lifecycle.html#read-xml
  239. static WebIDL::ExceptionOr<JS::NonnullGCPtr<DOM::Document>> load_xml_document(HTML::NavigationParams& navigation_params, MimeSniff::MimeType type)
  240. {
  241. // When faced with displaying an XML file inline, provided navigation params navigationParams and a string type, user agents
  242. // must follow the requirements defined in XML and Namespaces in XML, XML Media Types, DOM, and other relevant specifications
  243. // to create and initialize a Document object document, given "xml", type, and navigationParams, and return that Document.
  244. // They must also create a corresponding XML parser. [XML] [XMLNS] [RFC7303] [DOM]
  245. //
  246. // Note: At the time of writing, the XML specification community had not actually yet specified how XML and the DOM interact.
  247. //
  248. // The first task that the networking task source places on the task queue while fetching runs must process link headers
  249. // given document, navigationParams's response, and "media", after the task has been processed by the XML parser.
  250. //
  251. // The actual HTTP headers and other metadata, not the headers as mutated or implied by the algorithms given in this
  252. // specification, are the ones that must be used when determining the character encoding according to the rules given in the
  253. // above specifications. Once the character encoding is established, the document's character encoding must be set to that
  254. // character encoding.
  255. //
  256. // Before any script execution occurs, the user agent must wait for scripts may run for the newly-created document to be
  257. // true for the newly-created Document.
  258. //
  259. // Once parsing is complete, the user agent must set document's during-loading navigation ID for WebDriver BiDi to null.
  260. //
  261. // Note: For HTML documents this is reset when parsing is complete, after firing the load event.
  262. //
  263. // Error messages from the parse process (e.g., XML namespace well-formedness errors) may be reported inline by mutating
  264. // the Document.
  265. // FIXME: Actually follow the spec! This is just the ad-hoc code we had before, modified somewhat.
  266. auto document = TRY(DOM::Document::create_and_initialize(DOM::Document::Type::XML, "application/xhtml+xml"_string, navigation_params));
  267. Optional<String> content_encoding;
  268. if (auto maybe_encoding = type.parameters().get("charset"sv); maybe_encoding.has_value())
  269. content_encoding = maybe_encoding.value();
  270. auto process_body = [document, url = navigation_params.response->url().value(), content_encoding = move(content_encoding)](ByteBuffer data) {
  271. Optional<TextCodec::Decoder&> decoder;
  272. // The actual HTTP headers and other metadata, not the headers as mutated or implied by the algorithms given in this specification,
  273. // are the ones that must be used when determining the character encoding according to the rules given in the above specifications.
  274. if (content_encoding.has_value())
  275. decoder = TextCodec::decoder_for(*content_encoding);
  276. if (!decoder.has_value()) {
  277. auto encoding = HTML::run_encoding_sniffing_algorithm(document, data);
  278. decoder = TextCodec::decoder_for(encoding);
  279. }
  280. VERIFY(decoder.has_value());
  281. // Well-formed XML documents contain only properly encoded characters
  282. if (!decoder->validate(data)) {
  283. // FIXME: Insert error message into the document.
  284. dbgln("XML Document contains improperly-encoded characters");
  285. return;
  286. }
  287. auto source = decoder->to_utf8(data);
  288. if (source.is_error()) {
  289. // FIXME: Insert error message into the document.
  290. dbgln("Failed to decode XML document: {}", source.error());
  291. return;
  292. }
  293. XML::Parser parser(source.value(), { .resolve_external_resource = resolve_xml_resource });
  294. XMLDocumentBuilder builder { document };
  295. auto result = parser.parse_with_listener(builder);
  296. if (result.is_error()) {
  297. // FIXME: Insert error message into the document.
  298. dbgln("Failed to parse XML document: {}", result.error());
  299. }
  300. };
  301. auto process_body_error = [](auto) {
  302. dbgln("FIXME: Load html page with an error if read of body failed.");
  303. };
  304. auto& realm = document->realm();
  305. TRY(navigation_params.response->body()->fully_read(realm, move(process_body), move(process_body_error), JS::NonnullGCPtr { realm.global_object() }));
  306. return document;
  307. }
  308. // https://html.spec.whatwg.org/multipage/document-lifecycle.html#navigate-text
  309. static WebIDL::ExceptionOr<JS::NonnullGCPtr<DOM::Document>> load_text_document(HTML::NavigationParams& navigation_params, MimeSniff::MimeType type)
  310. {
  311. // To load a text document, given a navigation params navigationParams and a string type:
  312. // 1. Let document be the result of creating and initializing a Document object given "html", type, and navigationParams.
  313. auto document = TRY(DOM::Document::create_and_initialize(DOM::Document::Type::XML, type.essence(), navigation_params));
  314. // FIXME: 2. Set document's parser cannot change the mode flag to true.
  315. // 3. Set document's mode to "no-quirks".
  316. document->set_quirks_mode(DOM::QuirksMode::No);
  317. // 4. Create an HTML parser and associate it with the document. Act as if the tokenizer had emitted a start tag token
  318. // with the tag name "pre" followed by a single U+000A LINE FEED (LF) character, and switch the HTML parser's tokenizer
  319. // to the PLAINTEXT state. Each task that the networking task source places on the task queue while fetching runs must
  320. // then fill the parser's input byte stream with the fetched bytes and cause the HTML parser to perform the appropriate
  321. // processing of the input stream.
  322. // document's encoding must be set to the character encoding used to decode the document during parsing.
  323. // The first task that the networking task source places on the task queue while fetching runs must process link
  324. // headers given document, navigationParams's response, and "media", after the task has been processed by the HTML parser.
  325. // Before any script execution occurs, the user agent must wait for scripts may run for the newly-created document to be
  326. // true for document.
  327. // When no more bytes are available, the user agent must queue a global task on the networking task source given
  328. // document's relevant global object to have the parser to process the implied EOF character, which eventually causes a
  329. // load event to be fired.
  330. // FIXME: Parse as we receive the document data, instead of waiting for the whole document to be fetched first.
  331. auto process_body = [document, url = navigation_params.response->url().value()](ByteBuffer data) {
  332. auto encoding = run_encoding_sniffing_algorithm(document, data);
  333. dbgln_if(HTML_PARSER_DEBUG, "The encoding sniffing algorithm returned encoding '{}'", encoding);
  334. auto parser = HTML::HTMLParser::create_for_scripting(document);
  335. parser->tokenizer().update_insertion_point();
  336. parser->tokenizer().insert_input_at_insertion_point("<pre>\n"sv);
  337. parser->run();
  338. parser->tokenizer().switch_to(HTML::HTMLTokenizer::State::PLAINTEXT);
  339. parser->tokenizer().insert_input_at_insertion_point(data);
  340. parser->tokenizer().insert_eof();
  341. parser->run(url);
  342. document->set_encoding(MUST(String::from_byte_string(encoding)));
  343. // 5. User agents may add content to the head element of document, e.g., linking to a style sheet, providing
  344. // script, or giving the document a title.
  345. auto title = MUST(String::from_byte_string(LexicalPath::basename(url.to_byte_string())));
  346. auto title_element = MUST(DOM::create_element(document, HTML::TagNames::title, Namespace::HTML));
  347. MUST(document->head()->append_child(title_element));
  348. auto title_text = document->heap().allocate<DOM::Text>(document->realm(), document, title);
  349. MUST(title_element->append_child(*title_text));
  350. };
  351. auto process_body_error = [](auto) {
  352. dbgln("FIXME: Load html page with an error if read of body failed.");
  353. };
  354. auto& realm = document->realm();
  355. TRY(navigation_params.response->body()->fully_read(realm, move(process_body), move(process_body_error), JS::NonnullGCPtr { realm.global_object() }));
  356. // 6. Return document.
  357. return document;
  358. }
  359. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#loading-a-document
  360. JS::GCPtr<DOM::Document> load_document(HTML::NavigationParams navigation_params)
  361. {
  362. // To load a document given navigation params navigationParams, source snapshot params sourceSnapshotParams,
  363. // and origin initiatorOrigin, perform the following steps. They return a Document or null.
  364. // 1. Let type be the computed type of navigationParams's response.
  365. auto extracted_mime_type = navigation_params.response->header_list()->extract_mime_type().release_value_but_fixme_should_propagate_errors();
  366. if (!extracted_mime_type.has_value())
  367. return nullptr;
  368. auto type = extracted_mime_type.release_value();
  369. VERIFY(navigation_params.response->body());
  370. // 2. If the user agent has been configured to process resources of the given type using some mechanism other than
  371. // rendering the content in a navigable, then skip this step.
  372. // Otherwise, if the type is one of the following types:
  373. // -> an HTML MIME type
  374. if (type.is_html()) {
  375. // Return the result of loading an HTML document, given navigationParams.
  376. return load_html_document(navigation_params).release_value_but_fixme_should_propagate_errors();
  377. }
  378. // -> an XML MIME type that is not an explicitly supported XML MIME type
  379. // FIXME: that is not an explicitly supported XML MIME type
  380. if (type.is_xml()) {
  381. // Return the result of loading an XML document given navigationParams and type.
  382. return load_xml_document(navigation_params, type).release_value_but_fixme_should_propagate_errors();
  383. }
  384. // -> a JavaScript MIME type
  385. // -> a JSON MIME type that is not an explicitly supported JSON MIME type
  386. // -> "text/css"
  387. // -> "text/plain"
  388. // -> "text/vtt"
  389. if (type.is_javascript()
  390. || type.is_json()
  391. || type.essence() == "text/css"_string
  392. || type.essence() == "text/plain"_string
  393. || type.essence() == "text/vtt"_string) {
  394. // Return the result of loading a text document given navigationParams and type.
  395. return load_text_document(navigation_params, type).release_value_but_fixme_should_propagate_errors();
  396. }
  397. // -> "multipart/x-mixed-replace"
  398. if (type.essence() == "multipart/x-mixed-replace"_string) {
  399. // FIXME: Return the result of loading a multipart/x-mixed-replace document, given navigationParams,
  400. // sourceSnapshotParams, and initiatorOrigin.
  401. }
  402. // -> A supported image, video, or audio type
  403. if (type.is_image()
  404. || type.is_audio_or_video()) {
  405. // FIXME: Return the result of loading a media document given navigationParams and type.
  406. }
  407. // -> "application/pdf"
  408. // -> "text/pdf"
  409. if (type.essence() == "application/pdf"_string
  410. || type.essence() == "text/pdf"_string) {
  411. // FIXME: If the user agent's PDF viewer supported is true, return the result of creating a document for inline
  412. // content that doesn't have a DOM given navigationParams's navigable.
  413. }
  414. // Otherwise, proceed onward.
  415. // FIXME: 3. If, given type, the new resource is to be handled by displaying some sort of inline content, e.g., a
  416. // native rendering of the content or an error message because the specified type is not supported, then
  417. // return the result of creating a document for inline content that doesn't have a DOM given navigationParams's
  418. // navigable, navigationParams's id, and navigationParams's navigation timing type.
  419. // FIXME: 4. Otherwise, the document's type is such that the resource will not affect navigationParams's navigable,
  420. // e.g., because the resource is to be handed to an external application or because it is an unknown type
  421. // that will be processed as a download. Hand-off to external software given navigationParams's response,
  422. // navigationParams's navigable, navigationParams's final sandboxing flag set, sourceSnapshotParams's has
  423. // transient activation, and initiatorOrigin.
  424. // FIXME: Start of old, ad-hoc code
  425. if (!is_supported_document_mime_type(type.essence()))
  426. return nullptr;
  427. auto document = DOM::Document::create_and_initialize(DOM::Document::Type::HTML, "text/html"_string, navigation_params).release_value_but_fixme_should_propagate_errors();
  428. document->set_content_type(type.essence());
  429. auto& realm = document->realm();
  430. if (navigation_params.response->body()) {
  431. Optional<String> content_encoding = type.parameters().get("charset"sv);
  432. auto process_body = [document, url = navigation_params.response->url().value(), encoding = move(content_encoding)](ByteBuffer bytes) {
  433. if (parse_document(*document, bytes, move(encoding)))
  434. return;
  435. document->remove_all_children(true);
  436. auto error_html = load_error_page(url).release_value_but_fixme_should_propagate_errors();
  437. auto parser = HTML::HTMLParser::create(document, error_html, "utf-8");
  438. document->set_url(AK::URL("about:error"));
  439. parser->run();
  440. };
  441. auto process_body_error = [](auto) {
  442. dbgln("FIXME: Load html page with an error if read of body failed.");
  443. };
  444. navigation_params.response->body()->fully_read(
  445. realm,
  446. move(process_body),
  447. move(process_body_error),
  448. JS::NonnullGCPtr { realm.global_object() })
  449. .release_value_but_fixme_should_propagate_errors();
  450. }
  451. return document;
  452. // FIXME: End of old, ad-hoc code
  453. // 5. Return null.
  454. return nullptr;
  455. }
  456. // https://html.spec.whatwg.org/multipage/document-lifecycle.html#read-ua-inline
  457. JS::GCPtr<DOM::Document> create_document_for_inline_content(JS::GCPtr<HTML::Navigable> navigable, Optional<String> navigation_id, StringView content_html)
  458. {
  459. auto& vm = navigable->vm();
  460. // 1. Let origin be a new opaque origin.
  461. HTML::Origin origin {};
  462. // 2. Let coop be a new cross-origin opener policy.
  463. auto coop = HTML::CrossOriginOpenerPolicy {};
  464. // 3. Let coopEnforcementResult be a new cross-origin opener policy enforcement result with
  465. // url: response's URL
  466. // origin: origin
  467. // cross-origin opener policy: coop
  468. HTML::CrossOriginOpenerPolicyEnforcementResult coop_enforcement_result {
  469. .url = AK::URL("about:error"), // AD-HOC
  470. .origin = origin,
  471. .cross_origin_opener_policy = coop
  472. };
  473. // 4. Let navigationParams be a new navigation params with
  474. // id: navigationId
  475. // navigable: navigable
  476. // request: null
  477. // response: a new response
  478. // origin: origin
  479. // fetch controller: null
  480. // commit early hints: null
  481. // COOP enforcement result: coopEnforcementResult
  482. // reserved environment: null
  483. // policy container: a new policy container
  484. // final sandboxing flag set: an empty set
  485. // cross-origin opener policy: coop
  486. // FIXME: navigation timing type: navTimingType
  487. // about base URL: null
  488. auto response = Fetch::Infrastructure::Response::create(vm);
  489. response->url_list().append(AK::URL("about:error")); // AD-HOC: https://github.com/whatwg/html/issues/9122
  490. HTML::NavigationParams navigation_params {
  491. .id = navigation_id,
  492. .navigable = navigable,
  493. .request = {},
  494. .response = *response,
  495. .fetch_controller = nullptr,
  496. .commit_early_hints = nullptr,
  497. .coop_enforcement_result = move(coop_enforcement_result),
  498. .reserved_environment = {},
  499. .origin = move(origin),
  500. .policy_container = HTML::PolicyContainer {},
  501. .final_sandboxing_flag_set = HTML::SandboxingFlagSet {},
  502. .cross_origin_opener_policy = move(coop),
  503. .about_base_url = {},
  504. };
  505. // 5. Let document be the result of creating and initializing a Document object given "html", "text/html", and navigationParams.
  506. auto document = DOM::Document::create_and_initialize(DOM::Document::Type::HTML, "text/html"_string, navigation_params).release_value_but_fixme_should_propagate_errors();
  507. // 6. Either associate document with a custom rendering that is not rendered using the normal Document rendering rules, or mutate document until it represents the content the
  508. // user agent wants to render.
  509. auto parser = HTML::HTMLParser::create(document, content_html, "utf-8");
  510. document->set_url(AK::URL("about:error"));
  511. parser->run();
  512. // 7. Return document.
  513. return document;
  514. }
  515. }