DocumentLoading.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <andreas@ladybird.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 <LibGfx/ImageFormats/ImageDecoder.h>
  11. #include <LibTextCodec/Decoder.h>
  12. #include <LibWeb/DOM/Document.h>
  13. #include <LibWeb/DOM/DocumentLoading.h>
  14. #include <LibWeb/HTML/HTMLHeadElement.h>
  15. #include <LibWeb/HTML/Navigable.h>
  16. #include <LibWeb/HTML/NavigationParams.h>
  17. #include <LibWeb/HTML/Parser/HTMLEncodingDetection.h>
  18. #include <LibWeb/HTML/Parser/HTMLParser.h>
  19. #include <LibWeb/Loader/GeneratedPagesLoader.h>
  20. #include <LibWeb/Namespace.h>
  21. #include <LibWeb/Platform/EventLoopPlugin.h>
  22. #include <LibWeb/XML/XMLDocumentBuilder.h>
  23. namespace Web {
  24. // Replaces a document's content with a simple error message.
  25. static void convert_to_xml_error_document(DOM::Document& document, String error_string)
  26. {
  27. auto html_element = MUST(DOM::create_element(document, HTML::TagNames::html, Namespace::HTML));
  28. auto body_element = MUST(DOM::create_element(document, HTML::TagNames::body, Namespace::HTML));
  29. MUST(html_element->append_child(body_element));
  30. MUST(body_element->append_child(document.heap().allocate<DOM::Text>(document.realm(), document, error_string)));
  31. document.remove_all_children();
  32. MUST(document.append_child(html_element));
  33. }
  34. bool build_xml_document(DOM::Document& document, ByteBuffer const& data, Optional<String> content_encoding)
  35. {
  36. Optional<TextCodec::Decoder&> decoder;
  37. // The actual HTTP headers and other metadata, not the headers as mutated or implied by the algorithms given in this specification,
  38. // are the ones that must be used when determining the character encoding according to the rules given in the above specifications.
  39. if (content_encoding.has_value())
  40. decoder = TextCodec::decoder_for(*content_encoding);
  41. if (!decoder.has_value()) {
  42. auto encoding = HTML::run_encoding_sniffing_algorithm(document, data);
  43. decoder = TextCodec::decoder_for(encoding);
  44. }
  45. VERIFY(decoder.has_value());
  46. // Well-formed XML documents contain only properly encoded characters
  47. if (!decoder->validate(data)) {
  48. convert_to_xml_error_document(document, "XML Document contains improperly-encoded characters"_string);
  49. return false;
  50. }
  51. auto source = decoder->to_utf8(data).release_value_but_fixme_should_propagate_errors();
  52. XML::Parser parser(source, { .resolve_external_resource = resolve_xml_resource });
  53. XMLDocumentBuilder builder { document };
  54. auto result = parser.parse_with_listener(builder);
  55. return !result.is_error() && !builder.has_error();
  56. }
  57. // https://html.spec.whatwg.org/multipage/document-lifecycle.html#navigate-html
  58. static WebIDL::ExceptionOr<JS::NonnullGCPtr<DOM::Document>> load_html_document(HTML::NavigationParams const& navigation_params)
  59. {
  60. // To load an HTML document, given navigation params navigationParams:
  61. // 1. Let document be the result of creating and initializing a Document object given "html", "text/html", and navigationParams.
  62. auto document = TRY(DOM::Document::create_and_initialize(DOM::Document::Type::HTML, "text/html"_string, navigation_params));
  63. // 2. If document's URL is about:blank, then populate with html/head/body given document.
  64. // FIXME: The additional check for a non-empty body fixes issues with loading javascript urls in iframes, which
  65. // default to an "about:blank" url. Is this a spec bug?
  66. if (document->url_string() == "about:blank"_string
  67. && navigation_params.response->body()->length().value_or(0) == 0) {
  68. TRY(document->populate_with_html_head_and_body());
  69. // Nothing else is added to the document, so mark it as loaded.
  70. HTML::HTMLParser::the_end(document);
  71. }
  72. // 3. Otherwise, create an HTML parser and associate it with the document.
  73. // Each task that the networking task source places on the task queue while fetching runs must then fill the
  74. // parser's input byte stream with the fetched bytes and cause the HTML parser to perform the appropriate
  75. // processing of the input stream.
  76. // The first task that the networking task source places on the task queue while fetching runs must process link
  77. // headers given document, navigationParams's response, and "media", after the task has been processed by the
  78. // HTML parser.
  79. // Before any script execution occurs, the user agent must wait for scripts may run for the newly-created
  80. // document to be true for document.
  81. // When no more bytes are available, the user agent must queue a global task on the networking task source given
  82. // document's relevant global object to have the parser to process the implied EOF character, which eventually
  83. // causes a load event to be fired.
  84. else {
  85. // FIXME: Parse as we receive the document data, instead of waiting for the whole document to be fetched first.
  86. auto process_body = JS::create_heap_function(document->heap(), [document, url = navigation_params.response->url().value(), mime_type = navigation_params.response->header_list()->extract_mime_type()](ByteBuffer data) {
  87. Platform::EventLoopPlugin::the().deferred_invoke([document = document, data = move(data), url = url, mime_type] {
  88. auto parser = HTML::HTMLParser::create_with_uncertain_encoding(document, data, mime_type);
  89. parser->run(url);
  90. });
  91. });
  92. auto process_body_error = JS::create_heap_function(document->heap(), [](JS::Value) {
  93. dbgln("FIXME: Load html page with an error if read of body failed.");
  94. });
  95. auto& realm = document->realm();
  96. navigation_params.response->body()->fully_read(realm, process_body, process_body_error, JS::NonnullGCPtr { realm.global_object() });
  97. }
  98. // 4. Return document.
  99. return document;
  100. }
  101. // https://html.spec.whatwg.org/multipage/document-lifecycle.html#read-xml
  102. static WebIDL::ExceptionOr<JS::NonnullGCPtr<DOM::Document>> load_xml_document(HTML::NavigationParams const& navigation_params, MimeSniff::MimeType type)
  103. {
  104. // When faced with displaying an XML file inline, provided navigation params navigationParams and a string type, user agents
  105. // must follow the requirements defined in XML and Namespaces in XML, XML Media Types, DOM, and other relevant specifications
  106. // to create and initialize a Document object document, given "xml", type, and navigationParams, and return that Document.
  107. // They must also create a corresponding XML parser. [XML] [XMLNS] [RFC7303] [DOM]
  108. //
  109. // Note: At the time of writing, the XML specification community had not actually yet specified how XML and the DOM interact.
  110. //
  111. // The first task that the networking task source places on the task queue while fetching runs must process link headers
  112. // given document, navigationParams's response, and "media", after the task has been processed by the XML parser.
  113. //
  114. // The actual HTTP headers and other metadata, not the headers as mutated or implied by the algorithms given in this
  115. // specification, are the ones that must be used when determining the character encoding according to the rules given in the
  116. // above specifications. Once the character encoding is established, the document's character encoding must be set to that
  117. // character encoding.
  118. //
  119. // Before any script execution occurs, the user agent must wait for scripts may run for the newly-created document to be
  120. // true for the newly-created Document.
  121. //
  122. // Once parsing is complete, the user agent must set document's during-loading navigation ID for WebDriver BiDi to null.
  123. //
  124. // Note: For HTML documents this is reset when parsing is complete, after firing the load event.
  125. //
  126. // Error messages from the parse process (e.g., XML namespace well-formedness errors) may be reported inline by mutating
  127. // the Document.
  128. // FIXME: Actually follow the spec! This is just the ad-hoc code we had before, modified somewhat.
  129. auto document = TRY(DOM::Document::create_and_initialize(DOM::Document::Type::XML, type.essence(), navigation_params));
  130. Optional<String> content_encoding;
  131. if (auto maybe_encoding = type.parameters().get("charset"sv); maybe_encoding.has_value())
  132. content_encoding = maybe_encoding.value();
  133. auto process_body = JS::create_heap_function(document->heap(), [document, url = navigation_params.response->url().value(), content_encoding = move(content_encoding), mime = type](ByteBuffer data) {
  134. Optional<TextCodec::Decoder&> decoder;
  135. // The actual HTTP headers and other metadata, not the headers as mutated or implied by the algorithms given in this specification,
  136. // are the ones that must be used when determining the character encoding according to the rules given in the above specifications.
  137. if (content_encoding.has_value())
  138. decoder = TextCodec::decoder_for(*content_encoding);
  139. if (!decoder.has_value()) {
  140. auto encoding = HTML::run_encoding_sniffing_algorithm(document, data, mime);
  141. decoder = TextCodec::decoder_for(encoding);
  142. }
  143. VERIFY(decoder.has_value());
  144. // Well-formed XML documents contain only properly encoded characters
  145. if (!decoder->validate(data)) {
  146. // FIXME: Insert error message into the document.
  147. dbgln("XML Document contains improperly-encoded characters");
  148. convert_to_xml_error_document(document, "XML Document contains improperly-encoded characters"_string);
  149. // NOTE: This ensures that the `load` event gets fired for the frame loading this document.
  150. document->completely_finish_loading();
  151. return;
  152. }
  153. auto source = decoder->to_utf8(data);
  154. if (source.is_error()) {
  155. // FIXME: Insert error message into the document.
  156. dbgln("Failed to decode XML document: {}", source.error());
  157. convert_to_xml_error_document(document, MUST(String::formatted("Failed to decode XML document: {}", source.error())));
  158. // NOTE: This ensures that the `load` event gets fired for the frame loading this document.
  159. document->completely_finish_loading();
  160. return;
  161. }
  162. XML::Parser parser(source.value(), { .resolve_external_resource = resolve_xml_resource });
  163. XMLDocumentBuilder builder { document };
  164. auto result = parser.parse_with_listener(builder);
  165. if (result.is_error()) {
  166. // FIXME: Insert error message into the document.
  167. dbgln("Failed to parse XML document: {}", result.error());
  168. convert_to_xml_error_document(document, MUST(String::formatted("Failed to parse XML document: {}", result.error())));
  169. // NOTE: XMLDocumentBuilder ensures that the `load` event gets fired. We don't need to do anything else here.
  170. }
  171. });
  172. auto process_body_error = JS::create_heap_function(document->heap(), [](JS::Value) {
  173. dbgln("FIXME: Load html page with an error if read of body failed.");
  174. });
  175. auto& realm = document->realm();
  176. navigation_params.response->body()->fully_read(realm, process_body, process_body_error, JS::NonnullGCPtr { realm.global_object() });
  177. return document;
  178. }
  179. // https://html.spec.whatwg.org/multipage/document-lifecycle.html#navigate-text
  180. static WebIDL::ExceptionOr<JS::NonnullGCPtr<DOM::Document>> load_text_document(HTML::NavigationParams const& navigation_params, MimeSniff::MimeType type)
  181. {
  182. // To load a text document, given a navigation params navigationParams and a string type:
  183. // 1. Let document be the result of creating and initializing a Document object given "html", type, and navigationParams.
  184. auto document = TRY(DOM::Document::create_and_initialize(DOM::Document::Type::HTML, type.essence(), navigation_params));
  185. // FIXME: 2. Set document's parser cannot change the mode flag to true.
  186. // 3. Set document's mode to "no-quirks".
  187. document->set_quirks_mode(DOM::QuirksMode::No);
  188. // 4. Create an HTML parser and associate it with the document. Act as if the tokenizer had emitted a start tag token
  189. // with the tag name "pre" followed by a single U+000A LINE FEED (LF) character, and switch the HTML parser's tokenizer
  190. // to the PLAINTEXT state. Each task that the networking task source places on the task queue while fetching runs must
  191. // then fill the parser's input byte stream with the fetched bytes and cause the HTML parser to perform the appropriate
  192. // processing of the input stream.
  193. // document's encoding must be set to the character encoding used to decode the document during parsing.
  194. // The first task that the networking task source places on the task queue while fetching runs must process link
  195. // headers given document, navigationParams's response, and "media", after the task has been processed by the HTML parser.
  196. // Before any script execution occurs, the user agent must wait for scripts may run for the newly-created document to be
  197. // true for document.
  198. // When no more bytes are available, the user agent must queue a global task on the networking task source given
  199. // document's relevant global object to have the parser to process the implied EOF character, which eventually causes a
  200. // load event to be fired.
  201. // FIXME: Parse as we receive the document data, instead of waiting for the whole document to be fetched first.
  202. auto process_body = JS::create_heap_function(document->heap(), [document, url = navigation_params.response->url().value(), mime = type](ByteBuffer data) {
  203. auto encoding = run_encoding_sniffing_algorithm(document, data, mime);
  204. dbgln_if(HTML_PARSER_DEBUG, "The encoding sniffing algorithm returned encoding '{}'", encoding);
  205. auto parser = HTML::HTMLParser::create_for_scripting(document);
  206. parser->tokenizer().update_insertion_point();
  207. parser->tokenizer().insert_input_at_insertion_point("<pre>\n"sv);
  208. parser->run();
  209. parser->tokenizer().switch_to(HTML::HTMLTokenizer::State::PLAINTEXT);
  210. parser->tokenizer().insert_input_at_insertion_point(data);
  211. parser->tokenizer().insert_eof();
  212. parser->run(url);
  213. document->set_encoding(MUST(String::from_byte_string(encoding)));
  214. // 5. User agents may add content to the head element of document, e.g., linking to a style sheet, providing
  215. // script, or giving the document a title.
  216. auto title = MUST(String::from_byte_string(LexicalPath::basename(url.to_byte_string())));
  217. auto title_element = MUST(DOM::create_element(document, HTML::TagNames::title, Namespace::HTML));
  218. MUST(document->head()->append_child(title_element));
  219. auto title_text = document->heap().allocate<DOM::Text>(document->realm(), document, title);
  220. MUST(title_element->append_child(*title_text));
  221. });
  222. auto process_body_error = JS::create_heap_function(document->heap(), [](JS::Value) {
  223. dbgln("FIXME: Load html page with an error if read of body failed.");
  224. });
  225. auto& realm = document->realm();
  226. navigation_params.response->body()->fully_read(realm, process_body, process_body_error, JS::NonnullGCPtr { realm.global_object() });
  227. // 6. Return document.
  228. return document;
  229. }
  230. // https://html.spec.whatwg.org/multipage/document-lifecycle.html#navigate-media
  231. static WebIDL::ExceptionOr<JS::NonnullGCPtr<DOM::Document>> load_media_document(HTML::NavigationParams const& navigation_params, MimeSniff::MimeType type)
  232. {
  233. // To load a media document, given navigationParams and a string type:
  234. // 1. Let document be the result of creating and initializing a Document object given "html", type, and navigationParams.
  235. auto document = TRY(DOM::Document::create_and_initialize(DOM::Document::Type::HTML, type.essence(), navigation_params));
  236. // 2. Set document's mode to "no-quirks".
  237. document->set_quirks_mode(DOM::QuirksMode::No);
  238. // 3. Populate with html/head/body given document.
  239. TRY(document->populate_with_html_head_and_body());
  240. // 4. Append an element host element for the media, as described below, to the body element.
  241. // 5. Set the appropriate attribute of the element host element, as described below, to the address of the image,
  242. // video, or audio resource.
  243. // 6. User agents may add content to the head element of document, or attributes to host element, e.g., to link
  244. // to a style sheet, to provide a script, to give the document a title, or to make the media autoplay.
  245. auto insert_title = [](auto& document, auto title) -> WebIDL::ExceptionOr<void> {
  246. auto title_element = TRY(DOM::create_element(document, HTML::TagNames::title, Namespace::HTML));
  247. TRY(document->head()->append_child(title_element));
  248. auto title_text = document->heap().template allocate<DOM::Text>(document->realm(), document, title);
  249. TRY(title_element->append_child(*title_text));
  250. return {};
  251. };
  252. auto style_element = TRY(DOM::create_element(document, HTML::TagNames::style, Namespace::HTML));
  253. style_element->set_text_content(R"~~~(
  254. :root {
  255. background-color: #222;
  256. }
  257. img, video, audio {
  258. position: absolute;
  259. inset: 0;
  260. max-width: 100vw;
  261. max-height: 100vh;
  262. margin: auto;
  263. }
  264. img {
  265. background-color: #fff;
  266. }
  267. )~~~"_string);
  268. TRY(document->head()->append_child(style_element));
  269. auto url_string = document->url_string();
  270. if (type.is_image()) {
  271. auto img_element = TRY(DOM::create_element(document, HTML::TagNames::img, Namespace::HTML));
  272. TRY(img_element->set_attribute(HTML::AttributeNames::src, url_string));
  273. TRY(document->body()->append_child(img_element));
  274. TRY(insert_title(document, MUST(String::from_byte_string(LexicalPath::basename(url_string.to_byte_string())))));
  275. } else if (type.type() == "video"sv) {
  276. auto video_element = TRY(DOM::create_element(document, HTML::TagNames::video, Namespace::HTML));
  277. TRY(video_element->set_attribute(HTML::AttributeNames::src, url_string));
  278. TRY(video_element->set_attribute(HTML::AttributeNames::autoplay, String {}));
  279. TRY(video_element->set_attribute(HTML::AttributeNames::controls, String {}));
  280. TRY(document->body()->append_child(video_element));
  281. TRY(insert_title(document, MUST(String::from_byte_string(LexicalPath::basename(url_string.to_byte_string())))));
  282. } else if (type.type() == "audio"sv) {
  283. auto audio_element = TRY(DOM::create_element(document, HTML::TagNames::audio, Namespace::HTML));
  284. TRY(audio_element->set_attribute(HTML::AttributeNames::src, url_string));
  285. TRY(audio_element->set_attribute(HTML::AttributeNames::autoplay, String {}));
  286. TRY(audio_element->set_attribute(HTML::AttributeNames::controls, String {}));
  287. TRY(document->body()->append_child(audio_element));
  288. TRY(insert_title(document, MUST(String::from_byte_string(LexicalPath::basename(url_string.to_byte_string())))));
  289. } else {
  290. // FIXME: According to https://mimesniff.spec.whatwg.org/#audio-or-video-mime-type we might have to deal with
  291. // "application/ogg" and figure out whether it's audio or video.
  292. VERIFY_NOT_REACHED();
  293. }
  294. // FIXME: 7. Process link headers given document, navigationParams's response, and "media".
  295. // 8. Act as if the user agent had stopped parsing document.
  296. // FIXME: We should not need to force the media file to load before saying that parsing has completed!
  297. // However, if we don't, then we get stuck in HTMLParser::the_end() waiting for the media file to load, which
  298. // never happens.
  299. auto& realm = document->realm();
  300. navigation_params.response->body()->fully_read(
  301. realm,
  302. JS::create_heap_function(document->heap(), [document](ByteBuffer) { HTML::HTMLParser::the_end(document); }),
  303. JS::create_heap_function(document->heap(), [](JS::Value) {}),
  304. JS::NonnullGCPtr { realm.global_object() });
  305. // 9. Return document.
  306. return document;
  307. // The element host element to create for the media is the element given in the table below in the second cell of
  308. // the row whose first cell describes the media. The appropriate attribute to set is the one given by the third cell
  309. // in that same row.
  310. // Type of media | Element for the media | Appropriate attribute
  311. // -------------------------------------------------------------
  312. // Image | img | src
  313. // Video | video | src
  314. // Audio | audio | src
  315. // Before any script execution occurs, the user agent must wait for scripts may run for the newly-created document to
  316. // be true for the Document.
  317. }
  318. bool can_load_document_with_type(MimeSniff::MimeType const& type)
  319. {
  320. if (type.is_html())
  321. return true;
  322. if (type.is_xml())
  323. return true;
  324. if (type.is_javascript()
  325. || type.is_json()
  326. || type.essence() == "text/css"_string
  327. || type.essence() == "text/plain"_string
  328. || type.essence() == "text/vtt"_string) {
  329. return true;
  330. }
  331. if (type.essence() == "multipart/x-mixed-replace"_string)
  332. return true;
  333. if (type.is_image() || type.is_audio_or_video())
  334. return true;
  335. if (type.essence() == "application/pdf"_string || type.essence() == "text/pdf"_string)
  336. return true;
  337. if (type.essence() == "text/markdown"sv)
  338. return true;
  339. return false;
  340. }
  341. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#loading-a-document
  342. JS::GCPtr<DOM::Document> load_document(HTML::NavigationParams const& navigation_params)
  343. {
  344. // To load a document given navigation params navigationParams, source snapshot params sourceSnapshotParams,
  345. // and origin initiatorOrigin, perform the following steps. They return a Document or null.
  346. // 1. Let type be the computed type of navigationParams's response.
  347. auto extracted_mime_type = navigation_params.response->header_list()->extract_mime_type();
  348. if (!extracted_mime_type.has_value())
  349. return nullptr;
  350. auto type = extracted_mime_type.release_value();
  351. VERIFY(navigation_params.response->body());
  352. // 2. If the user agent has been configured to process resources of the given type using some mechanism other than
  353. // rendering the content in a navigable, then skip this step.
  354. // Otherwise, if the type is one of the following types:
  355. // -> an HTML MIME type
  356. if (type.is_html()) {
  357. // Return the result of loading an HTML document, given navigationParams.
  358. return load_html_document(navigation_params).release_value_but_fixme_should_propagate_errors();
  359. }
  360. // -> an XML MIME type that is not an explicitly supported XML MIME type
  361. // FIXME: that is not an explicitly supported XML MIME type
  362. if (type.is_xml()) {
  363. // Return the result of loading an XML document given navigationParams and type.
  364. return load_xml_document(navigation_params, type).release_value_but_fixme_should_propagate_errors();
  365. }
  366. // -> a JavaScript MIME type
  367. // -> a JSON MIME type that is not an explicitly supported JSON MIME type
  368. // -> "text/css"
  369. // -> "text/plain"
  370. // -> "text/vtt"
  371. if (type.is_javascript()
  372. || type.is_json()
  373. || type.essence() == "text/css"_string
  374. || type.essence() == "text/plain"_string
  375. || type.essence() == "text/vtt"_string) {
  376. // Return the result of loading a text document given navigationParams and type.
  377. return load_text_document(navigation_params, type).release_value_but_fixme_should_propagate_errors();
  378. }
  379. // -> "multipart/x-mixed-replace"
  380. if (type.essence() == "multipart/x-mixed-replace"_string) {
  381. // FIXME: Return the result of loading a multipart/x-mixed-replace document, given navigationParams,
  382. // sourceSnapshotParams, and initiatorOrigin.
  383. }
  384. // -> A supported image, video, or audio type
  385. if (type.is_image()
  386. || type.is_audio_or_video()) {
  387. // Return the result of loading a media document given navigationParams and type.
  388. return load_media_document(navigation_params, type).release_value_but_fixme_should_propagate_errors();
  389. }
  390. // -> "application/pdf"
  391. // -> "text/pdf"
  392. if (type.essence() == "application/pdf"_string
  393. || type.essence() == "text/pdf"_string) {
  394. // FIXME: If the user agent's PDF viewer supported is true, return the result of creating a document for inline
  395. // content that doesn't have a DOM given navigationParams's navigable.
  396. }
  397. // Otherwise, proceed onward.
  398. // 3. If, given type, the new resource is to be handled by displaying some sort of inline content, e.g., a
  399. // native rendering of the content or an error message because the specified type is not supported, then
  400. // return the result of creating a document for inline content that doesn't have a DOM given navigationParams's
  401. // navigable, navigationParams's id, and navigationParams's navigation timing type.
  402. // FIXME: 4. Otherwise, the document's type is such that the resource will not affect navigationParams's navigable,
  403. // e.g., because the resource is to be handed to an external application or because it is an unknown type
  404. // that will be processed as a download. Hand-off to external software given navigationParams's response,
  405. // navigationParams's navigable, navigationParams's final sandboxing flag set, sourceSnapshotParams's has
  406. // transient activation, and initiatorOrigin.
  407. // 5. Return null.
  408. return nullptr;
  409. }
  410. }