Document.cpp 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327
  1. /*
  2. * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
  4. * Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
  5. * Copyright (c) 2021, Sam Atkins <atkinssj@serenityos.org>
  6. *
  7. * SPDX-License-Identifier: BSD-2-Clause
  8. */
  9. #include <AK/CharacterTypes.h>
  10. #include <AK/StringBuilder.h>
  11. #include <AK/Utf8View.h>
  12. #include <LibCore/Timer.h>
  13. #include <LibJS/Interpreter.h>
  14. #include <LibJS/Parser.h>
  15. #include <LibJS/Runtime/FunctionObject.h>
  16. #include <LibWeb/Bindings/MainThreadVM.h>
  17. #include <LibWeb/Bindings/WindowObject.h>
  18. #include <LibWeb/CSS/MediaQueryListEvent.h>
  19. #include <LibWeb/CSS/StyleComputer.h>
  20. #include <LibWeb/Cookie/ParsedCookie.h>
  21. #include <LibWeb/DOM/Comment.h>
  22. #include <LibWeb/DOM/CustomEvent.h>
  23. #include <LibWeb/DOM/DOMException.h>
  24. #include <LibWeb/DOM/DOMImplementation.h>
  25. #include <LibWeb/DOM/Document.h>
  26. #include <LibWeb/DOM/DocumentFragment.h>
  27. #include <LibWeb/DOM/DocumentType.h>
  28. #include <LibWeb/DOM/Element.h>
  29. #include <LibWeb/DOM/ElementFactory.h>
  30. #include <LibWeb/DOM/Event.h>
  31. #include <LibWeb/DOM/ExceptionOr.h>
  32. #include <LibWeb/DOM/HTMLCollection.h>
  33. #include <LibWeb/DOM/Range.h>
  34. #include <LibWeb/DOM/ShadowRoot.h>
  35. #include <LibWeb/DOM/Text.h>
  36. #include <LibWeb/DOM/Window.h>
  37. #include <LibWeb/Dump.h>
  38. #include <LibWeb/HTML/AttributeNames.h>
  39. #include <LibWeb/HTML/BrowsingContext.h>
  40. #include <LibWeb/HTML/EventLoop/EventLoop.h>
  41. #include <LibWeb/HTML/EventNames.h>
  42. #include <LibWeb/HTML/HTMLAnchorElement.h>
  43. #include <LibWeb/HTML/HTMLAreaElement.h>
  44. #include <LibWeb/HTML/HTMLBodyElement.h>
  45. #include <LibWeb/HTML/HTMLEmbedElement.h>
  46. #include <LibWeb/HTML/HTMLFormElement.h>
  47. #include <LibWeb/HTML/HTMLFrameSetElement.h>
  48. #include <LibWeb/HTML/HTMLHeadElement.h>
  49. #include <LibWeb/HTML/HTMLHtmlElement.h>
  50. #include <LibWeb/HTML/HTMLIFrameElement.h>
  51. #include <LibWeb/HTML/HTMLImageElement.h>
  52. #include <LibWeb/HTML/HTMLScriptElement.h>
  53. #include <LibWeb/HTML/HTMLTitleElement.h>
  54. #include <LibWeb/HTML/MessageEvent.h>
  55. #include <LibWeb/HTML/Parser/HTMLParser.h>
  56. #include <LibWeb/HTML/Scripting/ExceptionReporter.h>
  57. #include <LibWeb/HTML/Scripting/WindowEnvironmentSettingsObject.h>
  58. #include <LibWeb/Layout/BlockFormattingContext.h>
  59. #include <LibWeb/Layout/InitialContainingBlock.h>
  60. #include <LibWeb/Layout/TreeBuilder.h>
  61. #include <LibWeb/Namespace.h>
  62. #include <LibWeb/Origin.h>
  63. #include <LibWeb/Page/Page.h>
  64. #include <LibWeb/SVG/TagNames.h>
  65. #include <LibWeb/UIEvents/EventNames.h>
  66. #include <LibWeb/UIEvents/FocusEvent.h>
  67. #include <LibWeb/UIEvents/KeyboardEvent.h>
  68. #include <LibWeb/UIEvents/MouseEvent.h>
  69. namespace Web::DOM {
  70. Document::Document(const AK::URL& url)
  71. : ParentNode(*this, NodeType::DOCUMENT_NODE)
  72. , m_style_computer(make<CSS::StyleComputer>(*this))
  73. , m_style_sheets(CSS::StyleSheetList::create(*this))
  74. , m_url(url)
  75. , m_window(Window::create_with_document(*this))
  76. , m_implementation(DOMImplementation::create({}, *this))
  77. , m_history(HTML::History::create(*this))
  78. {
  79. HTML::main_thread_event_loop().register_document({}, *this);
  80. m_style_update_timer = Core::Timer::create_single_shot(0, [this] {
  81. update_style();
  82. });
  83. m_layout_update_timer = Core::Timer::create_single_shot(0, [this] {
  84. force_layout();
  85. });
  86. }
  87. Document::~Document()
  88. {
  89. }
  90. void Document::removed_last_ref()
  91. {
  92. VERIFY(!ref_count());
  93. VERIFY(!m_deletion_has_begun);
  94. if (m_referencing_node_count) {
  95. // The document has reached ref_count==0 but still has nodes keeping it alive.
  96. // At this point, sever all the node links we control.
  97. // If nodes remain elsewhere (e.g JS wrappers), they will keep the document alive.
  98. // NOTE: This makes sure we stay alive across for the duration of the cleanup below.
  99. increment_referencing_node_count();
  100. m_focused_element = nullptr;
  101. m_hovered_node = nullptr;
  102. m_pending_parsing_blocking_script = nullptr;
  103. m_inspected_node = nullptr;
  104. m_scripts_to_execute_when_parsing_has_finished.clear();
  105. m_scripts_to_execute_as_soon_as_possible.clear();
  106. m_associated_inert_template_document = nullptr;
  107. m_interpreter = nullptr;
  108. {
  109. // Gather up all the descendants of this document and prune them from the tree.
  110. // FIXME: This could definitely be more elegant.
  111. NonnullRefPtrVector<Node> descendants;
  112. for_each_in_inclusive_subtree([&](auto& node) {
  113. if (&node != this)
  114. descendants.append(node);
  115. return IterationDecision::Continue;
  116. });
  117. for (auto& node : descendants) {
  118. VERIFY(&node.document() == this);
  119. VERIFY(!node.is_document());
  120. if (node.parent())
  121. node.remove();
  122. }
  123. }
  124. m_in_removed_last_ref = false;
  125. decrement_referencing_node_count();
  126. return;
  127. }
  128. m_in_removed_last_ref = false;
  129. m_deletion_has_begun = true;
  130. HTML::main_thread_event_loop().unregister_document({}, *this);
  131. delete this;
  132. }
  133. // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-document-write
  134. ExceptionOr<void> Document::write(Vector<String> const& strings)
  135. {
  136. // 1. If document is an XML document, then throw an "InvalidStateError" DOMException.
  137. if (doctype() && doctype()->name() == "xml")
  138. return DOM::InvalidStateError::create("write() called on XML document.");
  139. // 2. If document's throw-on-dynamic-markup-insertion counter is greater than 0, then throw an "InvalidStateError" DOMException.
  140. if (m_throw_on_dynamic_markup_insertion_counter > 0)
  141. return DOM::InvalidStateError::create("throw-on-dynamic-markup-insertion-counter greater than zero.");
  142. // 3. If document's active parser was aborted is true, then return.
  143. if (m_active_parser_was_aborted)
  144. return {};
  145. // 4. If the insertion point is undefined, then:
  146. if (!(m_parser && m_parser->tokenizer().is_insertion_point_defined())) {
  147. // 1. If document's unload counter is greater than 0 or document's ignore-destructive-writes counter is greater than 0, then return.
  148. if (m_unload_counter > 0 || m_ignore_destructive_writes_counter > 0)
  149. return {};
  150. // 2. Run the document open steps with document.
  151. open();
  152. }
  153. // 5. Insert input into the input stream just before the insertion point.
  154. StringBuilder builder;
  155. builder.join("", strings);
  156. m_parser->tokenizer().insert_input_at_insertion_point(builder.build());
  157. // 6. If there is no pending parsing-blocking script, have the HTML parser process input, one code point at a time, processing resulting tokens as they are emitted, and stopping when the tokenizer reaches the insertion point or when the processing of the tokenizer is aborted by the tree construction stage (this can happen if a script end tag token is emitted by the tokenizer).
  158. if (!pending_parsing_blocking_script())
  159. m_parser->run();
  160. return {};
  161. }
  162. // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-document-writeln
  163. ExceptionOr<void> Document::writeln(Vector<String> const& strings)
  164. {
  165. // FIXME: No need to allocate a new vector
  166. Vector<String> new_strings;
  167. for (auto const& element : strings) {
  168. new_strings.append(String::formatted("{}\n", element));
  169. }
  170. return write(strings);
  171. }
  172. // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-document-open
  173. ExceptionOr<Document*> Document::open(String const&, String const&)
  174. {
  175. // 1. If document is an XML document, then throw an "InvalidStateError" DOMException exception.
  176. if (doctype() && doctype()->name() == "xml")
  177. return DOM::InvalidStateError::create("open() called on XML document.");
  178. // 2. If document's throw-on-dynamic-markup-insertion counter is greater than 0, then throw an "InvalidStateError" DOMException.
  179. if (m_throw_on_dynamic_markup_insertion_counter > 0)
  180. return DOM::InvalidStateError::create("throw-on-dynamic-markup-insertion-counter greater than zero.");
  181. // FIXME: 3. Let entryDocument be the entry global object's associated Document.
  182. auto& entry_document = *this;
  183. // 4. If document's origin is not same origin to entryDocument's origin, then throw a "SecurityError" DOMException.
  184. if (origin() != entry_document.origin())
  185. return DOM::SecurityError::create("Document.origin() not the same as entryDocument's.");
  186. // 5. If document has an active parser whose script nesting level is greater than 0, then return document.
  187. if (m_parser && m_parser->script_nesting_level() > 0)
  188. return this;
  189. // 6. Similarly, if document's unload counter is greater than 0, then return document.
  190. if (m_unload_counter > 0)
  191. return this;
  192. // 7. If document's active parser was aborted is true, then return document.
  193. if (m_active_parser_was_aborted)
  194. return this;
  195. // FIXME: 8. If document's browsing context is non-null and there is an existing attempt to navigate document's browsing context, then stop document loading given document.
  196. // FIXME: 9. For each shadow-including inclusive descendant node of document, erase all event listeners and handlers given node.
  197. // FIXME 10. If document is the associated Document of document's relevant global object, then erase all event listeners and handlers given document's relevant global object.
  198. // 11. Replace all with null within document, without firing any mutation events.
  199. replace_all(nullptr);
  200. // 12. If document is fully active, then:
  201. if (is_fully_active()) {
  202. // 1. Let newURL be a copy of entryDocument's URL.
  203. auto new_url = entry_document.url();
  204. // 2. If entryDocument is not document, then set newURL's fragment to null.
  205. if (&entry_document != this)
  206. new_url.set_fragment("");
  207. // FIXME: 3. Run the URL and history update steps with document and newURL.
  208. }
  209. // FIXME: 13. Set document's is initial about:blank to false.
  210. // FIXME: 14. If document's iframe load in progress flag is set, then set document's mute iframe load flag.
  211. // 15. Set document to no-quirks mode.
  212. set_quirks_mode(QuirksMode::No);
  213. // 16. Create a new HTML parser and associate it with document. This is a script-created parser (meaning that it can be closed by the document.open() and document.close() methods, and that the tokenizer will wait for an explicit call to document.close() before emitting an end-of-file token). The encoding confidence is irrelevant.
  214. m_parser = make<HTML::HTMLParser>(*this);
  215. // 17. Set the insertion point to point at just before the end of the input stream (which at this point will be empty).
  216. m_parser->tokenizer().update_insertion_point();
  217. // 18. Update the current document readiness of document to "loading".
  218. update_readiness(HTML::DocumentReadyState::Loading);
  219. // 19. Return document.
  220. return this;
  221. }
  222. // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#closing-the-input-stream
  223. ExceptionOr<void> Document::close()
  224. {
  225. // 1. If document is an XML document, then throw an "InvalidStateError" DOMException exception.
  226. if (doctype() && doctype()->name() == "xml")
  227. return DOM::InvalidStateError::create("close() called on XML document.");
  228. // 2. If document's throw-on-dynamic-markup-insertion counter is greater than 0, then throw an "InvalidStateError" DOMException.
  229. if (m_throw_on_dynamic_markup_insertion_counter > 0)
  230. return DOM::InvalidStateError::create("throw-on-dynamic-markup-insertion-counter greater than zero.");
  231. // 3. If there is no script-created parser associated with the document, then return.
  232. if (!m_parser)
  233. return {};
  234. // FIXME: 4. Insert an explicit "EOF" character at the end of the parser's input stream.
  235. m_parser->tokenizer().insert_eof();
  236. // 5. If there is a pending parsing-blocking script, then return.
  237. if (pending_parsing_blocking_script())
  238. return {};
  239. // FIXME: 6. Run the tokenizer, processing resulting tokens as they are emitted, and stopping when the tokenizer reaches the explicit "EOF" character or spins the event loop.
  240. m_parser->run();
  241. return {};
  242. }
  243. Origin Document::origin() const
  244. {
  245. if (!m_url.is_valid())
  246. return {};
  247. return { m_url.protocol(), m_url.host(), m_url.port_or_default() };
  248. }
  249. void Document::set_origin(const Origin& origin)
  250. {
  251. m_url.set_protocol(origin.protocol());
  252. m_url.set_host(origin.host());
  253. m_url.set_port(origin.port());
  254. }
  255. void Document::schedule_style_update()
  256. {
  257. if (m_style_update_timer->is_active())
  258. return;
  259. m_style_update_timer->start();
  260. }
  261. void Document::schedule_layout_update()
  262. {
  263. if (m_layout_update_timer->is_active())
  264. return;
  265. m_layout_update_timer->start();
  266. }
  267. bool Document::is_child_allowed(const Node& node) const
  268. {
  269. switch (node.type()) {
  270. case NodeType::DOCUMENT_NODE:
  271. case NodeType::TEXT_NODE:
  272. return false;
  273. case NodeType::COMMENT_NODE:
  274. return true;
  275. case NodeType::DOCUMENT_TYPE_NODE:
  276. return !first_child_of_type<DocumentType>();
  277. case NodeType::ELEMENT_NODE:
  278. return !first_child_of_type<Element>();
  279. default:
  280. return false;
  281. }
  282. }
  283. Element* Document::document_element()
  284. {
  285. return first_child_of_type<Element>();
  286. }
  287. const Element* Document::document_element() const
  288. {
  289. return first_child_of_type<Element>();
  290. }
  291. HTML::HTMLHtmlElement* Document::html_element()
  292. {
  293. auto* html = document_element();
  294. if (is<HTML::HTMLHtmlElement>(html))
  295. return verify_cast<HTML::HTMLHtmlElement>(html);
  296. return nullptr;
  297. }
  298. HTML::HTMLHeadElement* Document::head()
  299. {
  300. auto* html = html_element();
  301. if (!html)
  302. return nullptr;
  303. return html->first_child_of_type<HTML::HTMLHeadElement>();
  304. }
  305. HTML::HTMLElement* Document::body()
  306. {
  307. auto* html = html_element();
  308. if (!html)
  309. return nullptr;
  310. auto* first_body = html->first_child_of_type<HTML::HTMLBodyElement>();
  311. if (first_body)
  312. return first_body;
  313. auto* first_frameset = html->first_child_of_type<HTML::HTMLFrameSetElement>();
  314. if (first_frameset)
  315. return first_frameset;
  316. return nullptr;
  317. }
  318. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-body
  319. ExceptionOr<void> Document::set_body(HTML::HTMLElement* new_body)
  320. {
  321. if (!is<HTML::HTMLBodyElement>(new_body) && !is<HTML::HTMLFrameSetElement>(new_body))
  322. return DOM::HierarchyRequestError::create("Invalid document body element, must be 'body' or 'frameset'");
  323. auto* existing_body = body();
  324. if (existing_body) {
  325. auto replace_result = existing_body->parent()->replace_child(*new_body, *existing_body);
  326. if (replace_result.is_exception())
  327. return replace_result.exception();
  328. return {};
  329. }
  330. auto* document_element = this->document_element();
  331. if (!document_element)
  332. return DOM::HierarchyRequestError::create("Missing document element");
  333. auto append_result = document_element->append_child(*new_body);
  334. if (append_result.is_exception())
  335. return append_result.exception();
  336. return {};
  337. }
  338. String Document::title() const
  339. {
  340. auto* head_element = head();
  341. if (!head_element)
  342. return {};
  343. auto* title_element = head_element->first_child_of_type<HTML::HTMLTitleElement>();
  344. if (!title_element)
  345. return {};
  346. auto raw_title = title_element->text_content();
  347. StringBuilder builder;
  348. bool last_was_space = false;
  349. for (auto code_point : Utf8View(raw_title)) {
  350. if (is_ascii_space(code_point)) {
  351. last_was_space = true;
  352. } else {
  353. if (last_was_space && !builder.is_empty())
  354. builder.append(' ');
  355. builder.append_code_point(code_point);
  356. last_was_space = false;
  357. }
  358. }
  359. return builder.to_string();
  360. }
  361. void Document::set_title(const String& title)
  362. {
  363. auto* head_element = const_cast<HTML::HTMLHeadElement*>(head());
  364. if (!head_element)
  365. return;
  366. RefPtr<HTML::HTMLTitleElement> title_element = head_element->first_child_of_type<HTML::HTMLTitleElement>();
  367. if (!title_element) {
  368. title_element = static_ptr_cast<HTML::HTMLTitleElement>(create_element(HTML::TagNames::title));
  369. head_element->append_child(*title_element);
  370. }
  371. title_element->remove_all_children(true);
  372. title_element->append_child(adopt_ref(*new Text(*this, title)));
  373. if (auto* page = this->page()) {
  374. if (browsing_context() == &page->top_level_browsing_context())
  375. page->client().page_did_change_title(title);
  376. }
  377. }
  378. void Document::attach_to_browsing_context(Badge<HTML::BrowsingContext>, HTML::BrowsingContext& browsing_context)
  379. {
  380. m_browsing_context = browsing_context;
  381. update_layout();
  382. }
  383. void Document::detach_from_browsing_context(Badge<HTML::BrowsingContext>, HTML::BrowsingContext& browsing_context)
  384. {
  385. VERIFY(&browsing_context == m_browsing_context);
  386. tear_down_layout_tree();
  387. m_browsing_context = nullptr;
  388. }
  389. void Document::tear_down_layout_tree()
  390. {
  391. if (!m_layout_root)
  392. return;
  393. // Gather up all the layout nodes in a vector and detach them from parents
  394. // while the vector keeps them alive.
  395. NonnullRefPtrVector<Layout::Node> layout_nodes;
  396. m_layout_root->for_each_in_inclusive_subtree([&](auto& layout_node) {
  397. layout_nodes.append(layout_node);
  398. return IterationDecision::Continue;
  399. });
  400. for (auto& layout_node : layout_nodes) {
  401. if (layout_node.parent())
  402. layout_node.parent()->remove_child(layout_node);
  403. }
  404. m_layout_root = nullptr;
  405. }
  406. Color Document::background_color(const Palette& palette) const
  407. {
  408. auto default_color = palette.base();
  409. auto* body_element = body();
  410. if (!body_element)
  411. return default_color;
  412. auto* body_layout_node = body_element->layout_node();
  413. if (!body_layout_node)
  414. return default_color;
  415. auto color = body_layout_node->computed_values().background_color();
  416. if (!color.alpha())
  417. return default_color;
  418. return color;
  419. }
  420. Vector<CSS::BackgroundLayerData> const* Document::background_layers() const
  421. {
  422. auto* body_element = body();
  423. if (!body_element)
  424. return {};
  425. auto* body_layout_node = body_element->layout_node();
  426. if (!body_layout_node)
  427. return {};
  428. return &body_layout_node->background_layers();
  429. }
  430. // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#parse-a-url
  431. AK::URL Document::parse_url(String const& url) const
  432. {
  433. // FIXME: Make sure we do this according to spec.
  434. return m_url.complete_url(url);
  435. }
  436. void Document::set_needs_layout()
  437. {
  438. if (m_needs_layout)
  439. return;
  440. m_needs_layout = true;
  441. schedule_layout_update();
  442. }
  443. void Document::force_layout()
  444. {
  445. tear_down_layout_tree();
  446. update_layout();
  447. }
  448. void Document::ensure_layout()
  449. {
  450. if (m_needs_layout || !m_layout_root)
  451. update_layout();
  452. }
  453. void Document::update_layout()
  454. {
  455. if (!m_needs_layout && m_layout_root)
  456. return;
  457. if (!browsing_context())
  458. return;
  459. auto viewport_rect = browsing_context()->viewport_rect();
  460. update_style();
  461. if (!m_layout_root) {
  462. Layout::TreeBuilder tree_builder;
  463. m_layout_root = static_ptr_cast<Layout::InitialContainingBlock>(tree_builder.build(*this));
  464. }
  465. Layout::FormattingState formatting_state;
  466. Layout::BlockFormattingContext root_formatting_context(formatting_state, *m_layout_root, nullptr);
  467. m_layout_root->build_stacking_context_tree();
  468. auto& icb_state = formatting_state.ensure(*m_layout_root);
  469. icb_state.content_width = viewport_rect.width();
  470. icb_state.content_height = viewport_rect.height();
  471. root_formatting_context.run(*m_layout_root, Layout::LayoutMode::Default);
  472. formatting_state.commit();
  473. browsing_context()->set_needs_display();
  474. if (browsing_context()->is_top_level()) {
  475. if (auto* page = this->page())
  476. page->client().page_did_layout();
  477. }
  478. m_needs_layout = false;
  479. m_layout_update_timer->stop();
  480. }
  481. static void update_style_recursively(DOM::Node& node)
  482. {
  483. if (is<Element>(node))
  484. static_cast<Element&>(node).recompute_style();
  485. node.set_needs_style_update(false);
  486. if (node.child_needs_style_update()) {
  487. node.for_each_child([&](auto& child) {
  488. if (child.needs_style_update() || child.child_needs_style_update())
  489. update_style_recursively(child);
  490. return IterationDecision::Continue;
  491. });
  492. }
  493. node.set_child_needs_style_update(false);
  494. }
  495. void Document::update_style()
  496. {
  497. if (!browsing_context())
  498. return;
  499. if (!needs_style_update() && !child_needs_style_update())
  500. return;
  501. update_style_recursively(*this);
  502. m_style_update_timer->stop();
  503. set_needs_layout();
  504. }
  505. void Document::set_link_color(Color color)
  506. {
  507. m_link_color = color;
  508. }
  509. void Document::set_active_link_color(Color color)
  510. {
  511. m_active_link_color = color;
  512. }
  513. void Document::set_visited_link_color(Color color)
  514. {
  515. m_visited_link_color = color;
  516. }
  517. const Layout::InitialContainingBlock* Document::layout_node() const
  518. {
  519. return static_cast<const Layout::InitialContainingBlock*>(Node::layout_node());
  520. }
  521. Layout::InitialContainingBlock* Document::layout_node()
  522. {
  523. return static_cast<Layout::InitialContainingBlock*>(Node::layout_node());
  524. }
  525. void Document::set_inspected_node(Node* node)
  526. {
  527. if (m_inspected_node == node)
  528. return;
  529. if (m_inspected_node && m_inspected_node->layout_node())
  530. m_inspected_node->layout_node()->set_needs_display();
  531. m_inspected_node = node;
  532. if (m_inspected_node && m_inspected_node->layout_node())
  533. m_inspected_node->layout_node()->set_needs_display();
  534. }
  535. void Document::set_hovered_node(Node* node)
  536. {
  537. if (m_hovered_node == node)
  538. return;
  539. RefPtr<Node> old_hovered_node = move(m_hovered_node);
  540. m_hovered_node = node;
  541. invalidate_style();
  542. }
  543. NonnullRefPtr<HTMLCollection> Document::get_elements_by_name(String const& name)
  544. {
  545. return HTMLCollection::create(*this, [name](Element const& element) {
  546. return element.name() == name;
  547. });
  548. }
  549. NonnullRefPtr<HTMLCollection> Document::get_elements_by_class_name(FlyString const& class_name)
  550. {
  551. return HTMLCollection::create(*this, [class_name, quirks_mode = document().in_quirks_mode()](Element const& element) {
  552. return element.has_class(class_name, quirks_mode ? CaseSensitivity::CaseInsensitive : CaseSensitivity::CaseSensitive);
  553. });
  554. }
  555. // https://html.spec.whatwg.org/multipage/obsolete.html#dom-document-applets
  556. NonnullRefPtr<HTMLCollection> Document::applets()
  557. {
  558. // FIXME: This should return the same HTMLCollection object every time,
  559. // but that would cause a reference cycle since HTMLCollection refs the root.
  560. return HTMLCollection::create(*this, [](auto&) { return false; });
  561. }
  562. // https://html.spec.whatwg.org/multipage/obsolete.html#dom-document-anchors
  563. NonnullRefPtr<HTMLCollection> Document::anchors()
  564. {
  565. // FIXME: This should return the same HTMLCollection object every time,
  566. // but that would cause a reference cycle since HTMLCollection refs the root.
  567. return HTMLCollection::create(*this, [](Element const& element) {
  568. return is<HTML::HTMLAnchorElement>(element) && element.has_attribute(HTML::AttributeNames::name);
  569. });
  570. }
  571. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-images
  572. NonnullRefPtr<HTMLCollection> Document::images()
  573. {
  574. // FIXME: This should return the same HTMLCollection object every time,
  575. // but that would cause a reference cycle since HTMLCollection refs the root.
  576. return HTMLCollection::create(*this, [](Element const& element) {
  577. return is<HTML::HTMLImageElement>(element);
  578. });
  579. }
  580. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-embeds
  581. NonnullRefPtr<HTMLCollection> Document::embeds()
  582. {
  583. // FIXME: This should return the same HTMLCollection object every time,
  584. // but that would cause a reference cycle since HTMLCollection refs the root.
  585. return HTMLCollection::create(*this, [](Element const& element) {
  586. return is<HTML::HTMLEmbedElement>(element);
  587. });
  588. }
  589. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-plugins
  590. NonnullRefPtr<HTMLCollection> Document::plugins()
  591. {
  592. return embeds();
  593. }
  594. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-links
  595. NonnullRefPtr<HTMLCollection> Document::links()
  596. {
  597. // FIXME: This should return the same HTMLCollection object every time,
  598. // but that would cause a reference cycle since HTMLCollection refs the root.
  599. return HTMLCollection::create(*this, [](Element const& element) {
  600. return (is<HTML::HTMLAnchorElement>(element) || is<HTML::HTMLAreaElement>(element)) && element.has_attribute(HTML::AttributeNames::href);
  601. });
  602. }
  603. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-forms
  604. NonnullRefPtr<HTMLCollection> Document::forms()
  605. {
  606. // FIXME: This should return the same HTMLCollection object every time,
  607. // but that would cause a reference cycle since HTMLCollection refs the root.
  608. return HTMLCollection::create(*this, [](Element const& element) {
  609. return is<HTML::HTMLFormElement>(element);
  610. });
  611. }
  612. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-scripts
  613. NonnullRefPtr<HTMLCollection> Document::scripts()
  614. {
  615. // FIXME: This should return the same HTMLCollection object every time,
  616. // but that would cause a reference cycle since HTMLCollection refs the root.
  617. return HTMLCollection::create(*this, [](Element const& element) {
  618. return is<HTML::HTMLScriptElement>(element);
  619. });
  620. }
  621. Color Document::link_color() const
  622. {
  623. if (m_link_color.has_value())
  624. return m_link_color.value();
  625. if (!page())
  626. return Color::Blue;
  627. return page()->palette().link();
  628. }
  629. Color Document::active_link_color() const
  630. {
  631. if (m_active_link_color.has_value())
  632. return m_active_link_color.value();
  633. if (!page())
  634. return Color::Red;
  635. return page()->palette().active_link();
  636. }
  637. Color Document::visited_link_color() const
  638. {
  639. if (m_visited_link_color.has_value())
  640. return m_visited_link_color.value();
  641. if (!page())
  642. return Color::Magenta;
  643. return page()->palette().visited_link();
  644. }
  645. // https://html.spec.whatwg.org/multipage/webappapis.html#relevant-settings-object
  646. HTML::EnvironmentSettingsObject& Document::relevant_settings_object()
  647. {
  648. // Then, the relevant settings object for a platform object o is the environment settings object of the relevant Realm for o.
  649. return verify_cast<HTML::EnvironmentSettingsObject>(*realm().host_defined());
  650. }
  651. JS::Realm& Document::realm()
  652. {
  653. return interpreter().realm();
  654. }
  655. JS::Interpreter& Document::interpreter()
  656. {
  657. if (!m_interpreter) {
  658. // FIXME: This is all ad-hoc. It loosely follows steps 6.4 to 6.9 of https://html.spec.whatwg.org/#initialise-the-document-object
  659. auto& vm = Bindings::main_thread_vm();
  660. // https://html.spec.whatwg.org/multipage/webappapis.html#creating-a-new-javascript-realm
  661. // FIXME: Put all this into it's own function that can be used outside of Document.
  662. // 1. Perform InitializeHostDefinedRealm() with the provided customizations for creating the global object and the global this binding.
  663. // FIXME: Use WindowProxy as the global this value.
  664. m_interpreter = JS::Interpreter::create<Bindings::WindowObject>(vm, *m_window);
  665. // 2. Let realm execution context be the running JavaScript execution context.
  666. auto& realm_execution_context = vm.running_execution_context();
  667. // 3. Remove realm execution context from the JavaScript execution context stack.
  668. vm.execution_context_stack().remove_first_matching([&realm_execution_context](auto* execution_context) {
  669. return execution_context == &realm_execution_context;
  670. });
  671. // FIXME: 4. Let realm be realm execution context's Realm component.
  672. // FIXME: 5. Set realm's agent to agent.
  673. // FIXME: 6. If agent's agent cluster's cross-origin isolation mode is "none", then:
  674. // 1. Let global be realm's global object.
  675. // 2. Let status be ! global.[[Delete]]("SharedArrayBuffer").
  676. // 3. Assert: status is true.
  677. // FIXME: 7. Return realm execution context. (Requires being in it's own function as mentioned above)
  678. // == End of "create a JavaScript realm" ==
  679. // FIXME: 6. Let topLevelCreationURL be creationURL.
  680. // FIXME: 7. Let topLevelOrigin be navigationParams's origin.
  681. // FIXME: 8. If browsingContext is not a top-level browsing context, then:
  682. // 1. Let parentEnvironment be browsingContext's container's relevant settings object.
  683. // 2. Set topLevelCreationURL to parentEnvironment's top-level creation URL.
  684. // 3. Set topLevelOrigin to parentEnvironment's top-level origin.
  685. // FIXME: 9. Set up a window environment settings object with creationURL, realm execution context, navigationParams's reserved environment, topLevelCreationURL, and topLevelOrigin.
  686. // (This is missing reserved environment, topLevelCreationURL and topLevelOrigin. It also assumes creationURL is the document's URL, when it's really "navigationParams's response's URL.")
  687. HTML::WindowEnvironmentSettingsObject::setup(m_url, realm_execution_context);
  688. }
  689. return *m_interpreter;
  690. }
  691. JS::Value Document::run_javascript(StringView source, StringView filename)
  692. {
  693. // FIXME: The only user of this function now is javascript: URLs. Refactor them to follow the spec: https://html.spec.whatwg.org/multipage/browsing-the-web.html#javascript-protocol
  694. auto& interpreter = document().interpreter();
  695. auto script_or_error = JS::Script::parse(source, interpreter.realm(), filename);
  696. if (script_or_error.is_error()) {
  697. // FIXME: Add error logging back.
  698. return JS::js_undefined();
  699. }
  700. auto result = interpreter.run(script_or_error.value());
  701. if (result.is_error()) {
  702. // FIXME: I'm sure the spec could tell us something about error propagation here!
  703. HTML::report_exception(result);
  704. return {};
  705. }
  706. return result.value();
  707. }
  708. // https://dom.spec.whatwg.org/#dom-document-createelement
  709. // FIXME: This only implements step 6 of the algorithm and does not take in options.
  710. NonnullRefPtr<Element> Document::create_element(const String& tag_name)
  711. {
  712. // FIXME: Let namespace be the HTML namespace, if this is an HTML document or this’s content type is "application/xhtml+xml", and null otherwise.
  713. return DOM::create_element(*this, tag_name, Namespace::HTML);
  714. }
  715. // https://dom.spec.whatwg.org/#internal-createelementns-steps
  716. // FIXME: This only implements step 4 of the algorithm and does not take in options.
  717. NonnullRefPtr<Element> Document::create_element_ns(const String& namespace_, const String& qualified_name)
  718. {
  719. return DOM::create_element(*this, qualified_name, namespace_);
  720. }
  721. NonnullRefPtr<DocumentFragment> Document::create_document_fragment()
  722. {
  723. return adopt_ref(*new DocumentFragment(*this));
  724. }
  725. NonnullRefPtr<Text> Document::create_text_node(const String& data)
  726. {
  727. return adopt_ref(*new Text(*this, data));
  728. }
  729. NonnullRefPtr<Comment> Document::create_comment(const String& data)
  730. {
  731. return adopt_ref(*new Comment(*this, data));
  732. }
  733. NonnullRefPtr<Range> Document::create_range()
  734. {
  735. return Range::create(*this);
  736. }
  737. // https://dom.spec.whatwg.org/#dom-document-createevent
  738. NonnullRefPtr<Event> Document::create_event(const String& interface)
  739. {
  740. auto interface_lowercase = interface.to_lowercase();
  741. RefPtr<Event> event;
  742. if (interface_lowercase == "beforeunloadevent") {
  743. event = Event::create(""); // FIXME: Create BeforeUnloadEvent
  744. } else if (interface_lowercase == "compositionevent") {
  745. event = Event::create(""); // FIXME: Create CompositionEvent
  746. } else if (interface_lowercase == "customevent") {
  747. event = CustomEvent::create("");
  748. } else if (interface_lowercase == "devicemotionevent") {
  749. event = Event::create(""); // FIXME: Create DeviceMotionEvent
  750. } else if (interface_lowercase == "deviceorientationevent") {
  751. event = Event::create(""); // FIXME: Create DeviceOrientationEvent
  752. } else if (interface_lowercase == "dragevent") {
  753. event = Event::create(""); // FIXME: Create DragEvent
  754. } else if (interface_lowercase.is_one_of("event", "events")) {
  755. event = Event::create("");
  756. } else if (interface_lowercase == "focusevent") {
  757. event = UIEvents::FocusEvent::create("");
  758. } else if (interface_lowercase == "hashchangeevent") {
  759. event = Event::create(""); // FIXME: Create HashChangeEvent
  760. } else if (interface_lowercase == "htmlevents") {
  761. event = Event::create("");
  762. } else if (interface_lowercase == "keyboardevent") {
  763. event = UIEvents::KeyboardEvent::create("");
  764. } else if (interface_lowercase == "messageevent") {
  765. event = HTML::MessageEvent::create("");
  766. } else if (interface_lowercase.is_one_of("mouseevent", "mouseevents")) {
  767. event = UIEvents::MouseEvent::create("", 0, 0, 0, 0);
  768. } else if (interface_lowercase == "storageevent") {
  769. event = Event::create(""); // FIXME: Create StorageEvent
  770. } else if (interface_lowercase == "svgevents") {
  771. event = Event::create("");
  772. } else if (interface_lowercase == "textevent") {
  773. event = Event::create(""); // FIXME: Create CompositionEvent
  774. } else if (interface_lowercase == "touchevent") {
  775. event = Event::create(""); // FIXME: Create TouchEvent
  776. } else if (interface_lowercase.is_one_of("uievent", "uievents")) {
  777. event = UIEvents::UIEvent::create("");
  778. } else {
  779. // FIXME:
  780. // 3. If constructor is null, then throw a "NotSupportedError" DOMException.
  781. // 4. If the interface indicated by constructor is not exposed on the relevant global object of this, then throw a "NotSupportedError" DOMException.
  782. TODO();
  783. }
  784. // Setting type to empty string is handled by each constructor.
  785. // FIXME:
  786. // 7. Initialize event’s timeStamp attribute to a DOMHighResTimeStamp representing the high resolution time from the time origin to now.
  787. event->set_is_trusted(false);
  788. event->set_initialized(false);
  789. return event.release_nonnull();
  790. }
  791. void Document::set_pending_parsing_blocking_script(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement* script)
  792. {
  793. m_pending_parsing_blocking_script = script;
  794. }
  795. NonnullRefPtr<HTML::HTMLScriptElement> Document::take_pending_parsing_blocking_script(Badge<HTML::HTMLParser>)
  796. {
  797. return m_pending_parsing_blocking_script.release_nonnull();
  798. }
  799. void Document::add_script_to_execute_when_parsing_has_finished(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement& script)
  800. {
  801. m_scripts_to_execute_when_parsing_has_finished.append(script);
  802. }
  803. NonnullRefPtrVector<HTML::HTMLScriptElement> Document::take_scripts_to_execute_when_parsing_has_finished(Badge<HTML::HTMLParser>)
  804. {
  805. return move(m_scripts_to_execute_when_parsing_has_finished);
  806. }
  807. void Document::add_script_to_execute_as_soon_as_possible(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement& script)
  808. {
  809. m_scripts_to_execute_as_soon_as_possible.append(script);
  810. }
  811. NonnullRefPtrVector<HTML::HTMLScriptElement> Document::take_scripts_to_execute_as_soon_as_possible(Badge<HTML::HTMLParser>)
  812. {
  813. return move(m_scripts_to_execute_as_soon_as_possible);
  814. }
  815. // https://dom.spec.whatwg.org/#dom-document-importnode
  816. ExceptionOr<NonnullRefPtr<Node>> Document::import_node(NonnullRefPtr<Node> node, bool deep)
  817. {
  818. // 1. If node is a document or shadow root, then throw a "NotSupportedError" DOMException.
  819. if (is<Document>(*node) || is<ShadowRoot>(*node))
  820. return DOM::NotSupportedError::create("Cannot import a document or shadow root.");
  821. // 2. Return a clone of node, with this and the clone children flag set if deep is true.
  822. return node->clone_node(this, deep);
  823. }
  824. // https://dom.spec.whatwg.org/#concept-node-adopt
  825. void Document::adopt_node(Node& node)
  826. {
  827. auto& old_document = node.document();
  828. if (node.parent())
  829. node.remove();
  830. if (&old_document != this) {
  831. // FIXME: This should be shadow-including.
  832. node.for_each_in_inclusive_subtree([&](auto& inclusive_descendant) {
  833. inclusive_descendant.set_document({}, *this);
  834. // FIXME: If inclusiveDescendant is an element, then set the node document of each attribute in inclusiveDescendant’s attribute list to document.
  835. return IterationDecision::Continue;
  836. });
  837. // FIXME: For each inclusiveDescendant in node’s shadow-including inclusive descendants that is custom,
  838. // enqueue a custom element callback reaction with inclusiveDescendant, callback name "adoptedCallback",
  839. // and an argument list containing oldDocument and document.
  840. // FIXME: This should be shadow-including.
  841. node.for_each_in_inclusive_subtree([&](auto& inclusive_descendant) {
  842. inclusive_descendant.adopted_from(old_document);
  843. return IterationDecision::Continue;
  844. });
  845. }
  846. }
  847. // https://dom.spec.whatwg.org/#dom-document-adoptnode
  848. ExceptionOr<NonnullRefPtr<Node>> Document::adopt_node_binding(NonnullRefPtr<Node> node)
  849. {
  850. if (is<Document>(*node))
  851. return DOM::NotSupportedError::create("Cannot adopt a document into a document");
  852. if (is<ShadowRoot>(*node))
  853. return DOM::HierarchyRequestError::create("Cannot adopt a shadow root into a document");
  854. if (is<DocumentFragment>(*node) && verify_cast<DocumentFragment>(*node).host())
  855. return node;
  856. adopt_node(*node);
  857. return node;
  858. }
  859. const DocumentType* Document::doctype() const
  860. {
  861. return first_child_of_type<DocumentType>();
  862. }
  863. const String& Document::compat_mode() const
  864. {
  865. static String back_compat = "BackCompat";
  866. static String css1_compat = "CSS1Compat";
  867. if (m_quirks_mode == QuirksMode::Yes)
  868. return back_compat;
  869. return css1_compat;
  870. }
  871. bool Document::is_editable() const
  872. {
  873. return m_editable;
  874. }
  875. void Document::set_focused_element(Element* element)
  876. {
  877. if (m_focused_element == element)
  878. return;
  879. if (m_focused_element)
  880. m_focused_element->did_lose_focus();
  881. m_focused_element = element;
  882. if (m_focused_element)
  883. m_focused_element->did_receive_focus();
  884. if (m_layout_root)
  885. m_layout_root->set_needs_display();
  886. }
  887. void Document::set_active_element(Element* element)
  888. {
  889. if (m_active_element == element)
  890. return;
  891. m_active_element = element;
  892. if (m_layout_root)
  893. m_layout_root->set_needs_display();
  894. }
  895. String Document::ready_state() const
  896. {
  897. switch (m_readiness) {
  898. case HTML::DocumentReadyState::Loading:
  899. return "loading"sv;
  900. case HTML::DocumentReadyState::Interactive:
  901. return "interactive"sv;
  902. case HTML::DocumentReadyState::Complete:
  903. return "complete"sv;
  904. }
  905. VERIFY_NOT_REACHED();
  906. }
  907. // https://html.spec.whatwg.org/#update-the-current-document-readiness
  908. void Document::update_readiness(HTML::DocumentReadyState readiness_value)
  909. {
  910. // 1. If document's current document readiness equals readinessValue, then return.
  911. if (m_readiness == readiness_value)
  912. return;
  913. // The spec doesn't actually mention updating the current readiness value.
  914. // FIXME: https://github.com/whatwg/html/issues/7120
  915. m_readiness = readiness_value;
  916. // FIXME: 2. If document is associated with an HTML parser, then:
  917. // FIXME: 1. If document is associated with an HTML parser, then:
  918. // FIXME: 2. If readinessValue is "complete", and document's load timing info's DOM complete time is 0, then set document's load timing info's DOM complete time to now.
  919. // FIXME: 3. Otherwise, if readinessValue is "interactive", and document's load timing info's DOM interactive time is 0, then set document's load timing info's DOM interactive time to now.
  920. // 3. Fire an event named readystatechange at document.
  921. dispatch_event(Event::create(HTML::EventNames::readystatechange));
  922. }
  923. Page* Document::page()
  924. {
  925. return m_browsing_context ? m_browsing_context->page() : nullptr;
  926. }
  927. const Page* Document::page() const
  928. {
  929. return m_browsing_context ? m_browsing_context->page() : nullptr;
  930. }
  931. EventTarget* Document::get_parent(const Event& event)
  932. {
  933. if (event.type() == HTML::EventNames::load)
  934. return nullptr;
  935. return &window();
  936. }
  937. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#completely-finish-loading
  938. void Document::completely_finish_loading()
  939. {
  940. // 1. Assert: document's browsing context is non-null.
  941. VERIFY(browsing_context());
  942. // FIXME: 2. Set document's completely loaded time to the current time.
  943. // 3. Let container be document's browsing context's container.
  944. auto* container = browsing_context()->container();
  945. // If container is an iframe element, then queue an element task on the DOM manipulation task source given container to run the iframe load event steps given container.
  946. if (container && is<HTML::HTMLIFrameElement>(*container)) {
  947. container->queue_an_element_task(HTML::Task::Source::DOMManipulation, [container]() mutable {
  948. run_iframe_load_event_steps(static_cast<HTML::HTMLIFrameElement&>(*container));
  949. });
  950. }
  951. // Otherwise, if container is non-null, then queue an element task on the DOM manipulation task source given container to fire an event named load at container.
  952. else if (container) {
  953. container->queue_an_element_task(HTML::Task::Source::DOMManipulation, [container]() mutable {
  954. container->dispatch_event(DOM::Event::create(HTML::EventNames::load));
  955. });
  956. }
  957. }
  958. String Document::cookie(Cookie::Source source)
  959. {
  960. if (auto* page = this->page())
  961. return page->client().page_did_request_cookie(m_url, source);
  962. return {};
  963. }
  964. void Document::set_cookie(String const& cookie_string, Cookie::Source source)
  965. {
  966. auto cookie = Cookie::parse_cookie(cookie_string);
  967. if (!cookie.has_value())
  968. return;
  969. if (auto* page = this->page())
  970. page->client().page_did_set_cookie(m_url, cookie.value(), source);
  971. }
  972. String Document::dump_dom_tree_as_json() const
  973. {
  974. StringBuilder builder;
  975. JsonObjectSerializer json(builder);
  976. serialize_tree_as_json(json);
  977. json.finish();
  978. return builder.to_string();
  979. }
  980. // https://html.spec.whatwg.org/multipage/semantics.html#has-a-style-sheet-that-is-blocking-scripts
  981. bool Document::has_a_style_sheet_that_is_blocking_scripts() const
  982. {
  983. // A Document has a style sheet that is blocking scripts if its script-blocking style sheet counter is greater than 0,
  984. if (m_script_blocking_style_sheet_counter > 0)
  985. return true;
  986. // ...or if that Document has a non-null browsing context whose container document is non-null and has a script-blocking style sheet counter greater than 0.
  987. if (!browsing_context() || !browsing_context()->container_document())
  988. return false;
  989. return browsing_context()->container_document()->m_script_blocking_style_sheet_counter > 0;
  990. }
  991. String Document::referrer() const
  992. {
  993. // FIXME: Return the document's actual referrer.
  994. return "";
  995. }
  996. // https://html.spec.whatwg.org/multipage/browsers.html#fully-active
  997. bool Document::is_fully_active() const
  998. {
  999. // A Document d is said to be fully active when d's browsing context is non-null, d's browsing context's active document is d,
  1000. // and either d's browsing context is a top-level browsing context, or d's browsing context's container document is fully active.
  1001. return browsing_context() && browsing_context()->active_document() == this && (browsing_context()->is_top_level() || browsing_context()->container_document()->is_fully_active());
  1002. }
  1003. // https://html.spec.whatwg.org/multipage/browsers.html#active-document
  1004. bool Document::is_active() const
  1005. {
  1006. // A browsing context's active document is its active window's associated Document.
  1007. return browsing_context() && browsing_context()->active_document() == this;
  1008. }
  1009. // https://html.spec.whatwg.org/multipage/history.html#dom-document-location
  1010. Bindings::LocationObject* Document::location()
  1011. {
  1012. // The Document object's location attribute's getter must return this Document object's relevant global object's Location object,
  1013. // if this Document object is fully active, and null otherwise.
  1014. if (!is_fully_active())
  1015. return nullptr;
  1016. return window().wrapper()->location_object();
  1017. }
  1018. // https://html.spec.whatwg.org/multipage/interaction.html#dom-document-hidden
  1019. bool Document::hidden() const
  1020. {
  1021. return false;
  1022. }
  1023. // https://html.spec.whatwg.org/multipage/interaction.html#dom-document-visibilitystate
  1024. String Document::visibility_state() const
  1025. {
  1026. return hidden() ? "hidden" : "visible";
  1027. }
  1028. // https://drafts.csswg.org/cssom-view/#run-the-resize-steps
  1029. void Document::run_the_resize_steps()
  1030. {
  1031. // 1. If doc’s viewport has had its width or height changed
  1032. // (e.g. as a result of the user resizing the browser window, or changing the page zoom scale factor,
  1033. // or an iframe element’s dimensions are changed) since the last time these steps were run,
  1034. // fire an event named resize at the Window object associated with doc.
  1035. if (!browsing_context())
  1036. return;
  1037. auto viewport_size = browsing_context()->viewport_rect().size();
  1038. if (m_last_viewport_size == viewport_size)
  1039. return;
  1040. m_last_viewport_size = viewport_size;
  1041. dispatch_event(DOM::Event::create(UIEvents::EventNames::resize));
  1042. update_layout();
  1043. }
  1044. void Document::add_media_query_list(NonnullRefPtr<CSS::MediaQueryList>& media_query_list)
  1045. {
  1046. m_media_query_lists.append(media_query_list);
  1047. }
  1048. // https://drafts.csswg.org/cssom-view/#evaluate-media-queries-and-report-changes
  1049. void Document::evaluate_media_queries_and_report_changes()
  1050. {
  1051. // NOTE: Not in the spec, but we take this opportunity to prune null WeakPtrs.
  1052. m_media_query_lists.remove_all_matching([](auto& it) {
  1053. return it.is_null();
  1054. });
  1055. // 1. For each MediaQueryList object target that has doc as its document,
  1056. // in the order they were created, oldest first, run these substeps:
  1057. for (auto& media_query_list_ptr : m_media_query_lists) {
  1058. // 1.1. If target’s matches state has changed since the last time these steps
  1059. // were run, fire an event at target using the MediaQueryListEvent constructor,
  1060. // with its type attribute initialized to change, its isTrusted attribute
  1061. // initialized to true, its media attribute initialized to target’s media,
  1062. // and its matches attribute initialized to target’s matches state.
  1063. if (media_query_list_ptr.is_null())
  1064. continue;
  1065. auto media_query_list = media_query_list_ptr.strong_ref();
  1066. bool did_match = media_query_list->matches();
  1067. bool now_matches = media_query_list->evaluate();
  1068. if (did_match != now_matches) {
  1069. CSS::MediaQueryListEventInit init;
  1070. init.media = media_query_list->media();
  1071. init.matches = now_matches;
  1072. auto event = CSS::MediaQueryListEvent::create(HTML::EventNames::change, init);
  1073. event->set_is_trusted(true);
  1074. media_query_list->dispatch_event(event);
  1075. }
  1076. }
  1077. // Also not in the spec, but this is as good a place as any to evaluate @media rules!
  1078. bool any_media_queries_changed_match_state = false;
  1079. for (auto& style_sheet : style_sheets().sheets()) {
  1080. if (style_sheet.evaluate_media_queries(window()))
  1081. any_media_queries_changed_match_state = true;
  1082. }
  1083. if (any_media_queries_changed_match_state)
  1084. style_computer().invalidate_rule_cache();
  1085. }
  1086. NonnullRefPtr<DOMImplementation> Document::implementation() const
  1087. {
  1088. return *m_implementation;
  1089. }
  1090. bool Document::has_focus() const
  1091. {
  1092. // FIXME: Return whether we actually have focus.
  1093. return true;
  1094. }
  1095. }