Document.cpp 162 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857
  1. /*
  2. * Copyright (c) 2018-2023, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
  4. * Copyright (c) 2021-2023, Luke Wilde <lukew@serenityos.org>
  5. * Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
  6. *
  7. * SPDX-License-Identifier: BSD-2-Clause
  8. */
  9. #include <AK/CharacterTypes.h>
  10. #include <AK/Debug.h>
  11. #include <AK/GenericLexer.h>
  12. #include <AK/StringBuilder.h>
  13. #include <AK/Utf8View.h>
  14. #include <LibCore/Timer.h>
  15. #include <LibJS/Runtime/Array.h>
  16. #include <LibJS/Runtime/FunctionObject.h>
  17. #include <LibJS/Runtime/NativeFunction.h>
  18. #include <LibWeb/Animations/DocumentTimeline.h>
  19. #include <LibWeb/Bindings/MainThreadVM.h>
  20. #include <LibWeb/CSS/MediaQueryList.h>
  21. #include <LibWeb/CSS/MediaQueryListEvent.h>
  22. #include <LibWeb/CSS/StyleComputer.h>
  23. #include <LibWeb/CSS/SystemColor.h>
  24. #include <LibWeb/CSS/VisualViewport.h>
  25. #include <LibWeb/Cookie/ParsedCookie.h>
  26. #include <LibWeb/DOM/Attr.h>
  27. #include <LibWeb/DOM/Comment.h>
  28. #include <LibWeb/DOM/CustomEvent.h>
  29. #include <LibWeb/DOM/DOMImplementation.h>
  30. #include <LibWeb/DOM/Document.h>
  31. #include <LibWeb/DOM/DocumentFragment.h>
  32. #include <LibWeb/DOM/DocumentObserver.h>
  33. #include <LibWeb/DOM/DocumentType.h>
  34. #include <LibWeb/DOM/Element.h>
  35. #include <LibWeb/DOM/ElementFactory.h>
  36. #include <LibWeb/DOM/Event.h>
  37. #include <LibWeb/DOM/HTMLCollection.h>
  38. #include <LibWeb/DOM/NodeIterator.h>
  39. #include <LibWeb/DOM/ProcessingInstruction.h>
  40. #include <LibWeb/DOM/Range.h>
  41. #include <LibWeb/DOM/ShadowRoot.h>
  42. #include <LibWeb/DOM/Text.h>
  43. #include <LibWeb/DOM/TreeWalker.h>
  44. #include <LibWeb/Dump.h>
  45. #include <LibWeb/HTML/AttributeNames.h>
  46. #include <LibWeb/HTML/BrowsingContext.h>
  47. #include <LibWeb/HTML/CustomElements/CustomElementDefinition.h>
  48. #include <LibWeb/HTML/CustomElements/CustomElementReactionNames.h>
  49. #include <LibWeb/HTML/CustomElements/CustomElementRegistry.h>
  50. #include <LibWeb/HTML/DocumentState.h>
  51. #include <LibWeb/HTML/EventLoop/EventLoop.h>
  52. #include <LibWeb/HTML/EventNames.h>
  53. #include <LibWeb/HTML/Focus.h>
  54. #include <LibWeb/HTML/HTMLAnchorElement.h>
  55. #include <LibWeb/HTML/HTMLAreaElement.h>
  56. #include <LibWeb/HTML/HTMLBaseElement.h>
  57. #include <LibWeb/HTML/HTMLBodyElement.h>
  58. #include <LibWeb/HTML/HTMLDocument.h>
  59. #include <LibWeb/HTML/HTMLEmbedElement.h>
  60. #include <LibWeb/HTML/HTMLFormElement.h>
  61. #include <LibWeb/HTML/HTMLFrameSetElement.h>
  62. #include <LibWeb/HTML/HTMLHeadElement.h>
  63. #include <LibWeb/HTML/HTMLHtmlElement.h>
  64. #include <LibWeb/HTML/HTMLIFrameElement.h>
  65. #include <LibWeb/HTML/HTMLImageElement.h>
  66. #include <LibWeb/HTML/HTMLLinkElement.h>
  67. #include <LibWeb/HTML/HTMLScriptElement.h>
  68. #include <LibWeb/HTML/HTMLTitleElement.h>
  69. #include <LibWeb/HTML/ListOfAvailableImages.h>
  70. #include <LibWeb/HTML/Location.h>
  71. #include <LibWeb/HTML/MessageEvent.h>
  72. #include <LibWeb/HTML/Navigable.h>
  73. #include <LibWeb/HTML/Navigation.h>
  74. #include <LibWeb/HTML/NavigationParams.h>
  75. #include <LibWeb/HTML/Numbers.h>
  76. #include <LibWeb/HTML/Origin.h>
  77. #include <LibWeb/HTML/Parser/HTMLParser.h>
  78. #include <LibWeb/HTML/Scripting/ClassicScript.h>
  79. #include <LibWeb/HTML/Scripting/ExceptionReporter.h>
  80. #include <LibWeb/HTML/Scripting/WindowEnvironmentSettingsObject.h>
  81. #include <LibWeb/HTML/TraversableNavigable.h>
  82. #include <LibWeb/HTML/Window.h>
  83. #include <LibWeb/HTML/WindowProxy.h>
  84. #include <LibWeb/HighResolutionTime/TimeOrigin.h>
  85. #include <LibWeb/Infra/CharacterTypes.h>
  86. #include <LibWeb/Infra/Strings.h>
  87. #include <LibWeb/IntersectionObserver/IntersectionObserver.h>
  88. #include <LibWeb/Layout/BlockFormattingContext.h>
  89. #include <LibWeb/Layout/TreeBuilder.h>
  90. #include <LibWeb/Layout/Viewport.h>
  91. #include <LibWeb/Namespace.h>
  92. #include <LibWeb/Page/Page.h>
  93. #include <LibWeb/Painting/ViewportPaintable.h>
  94. #include <LibWeb/PermissionsPolicy/AutoplayAllowlist.h>
  95. #include <LibWeb/SVG/SVGElement.h>
  96. #include <LibWeb/SVG/SVGTitleElement.h>
  97. #include <LibWeb/SVG/TagNames.h>
  98. #include <LibWeb/Selection/Selection.h>
  99. #include <LibWeb/UIEvents/EventNames.h>
  100. #include <LibWeb/UIEvents/FocusEvent.h>
  101. #include <LibWeb/UIEvents/KeyboardEvent.h>
  102. #include <LibWeb/UIEvents/MouseEvent.h>
  103. #include <LibWeb/WebIDL/AbstractOperations.h>
  104. #include <LibWeb/WebIDL/DOMException.h>
  105. #include <LibWeb/WebIDL/ExceptionOr.h>
  106. namespace Web::DOM {
  107. JS_DEFINE_ALLOCATOR(Document);
  108. // https://html.spec.whatwg.org/multipage/origin.html#obtain-browsing-context-navigation
  109. static JS::NonnullGCPtr<HTML::BrowsingContext> obtain_a_browsing_context_to_use_for_a_navigation_response(
  110. HTML::BrowsingContext& browsing_context,
  111. HTML::SandboxingFlagSet sandbox_flags,
  112. HTML::CrossOriginOpenerPolicy navigation_coop,
  113. HTML::CrossOriginOpenerPolicyEnforcementResult coop_enforcement_result)
  114. {
  115. // 1. If browsingContext is not a top-level browsing context, return browsingContext.
  116. if (!browsing_context.is_top_level())
  117. return browsing_context;
  118. // 2. If coopEnforcementResult's needs a browsing context group switch is false, then:
  119. if (!coop_enforcement_result.needs_a_browsing_context_group_switch) {
  120. // 1. If coopEnforcementResult's would need a browsing context group switch due to report-only is true,
  121. if (coop_enforcement_result.would_need_a_browsing_context_group_switch_due_to_report_only) {
  122. // FIXME: set browsing context's virtual browsing context group ID to a new unique identifier.
  123. }
  124. // 2. Return browsingContext.
  125. return browsing_context;
  126. }
  127. // 3. Let newBrowsingContext be the first return value of creating a new top-level browsing context and document
  128. auto new_browsing_context = HTML::create_a_new_top_level_browsing_context_and_document(browsing_context.page()).release_value_but_fixme_should_propagate_errors().browsing_context;
  129. // FIXME: 4. If navigationCOOP's value is "same-origin-plurs-COEP", then set newBrowsingContext's group's
  130. // cross-origin isolation mode to either "logical" or "concrete". The choice of which is implementation-defined.
  131. // 5. If sandboxFlags is not empty, then:
  132. if (!is_empty(sandbox_flags)) {
  133. // 1. Assert navigationCOOP's value is "unsafe-none".
  134. VERIFY(navigation_coop.value == HTML::CrossOriginOpenerPolicyValue::UnsafeNone);
  135. // 2. Assert: newBrowsingContext's popup sandboxing flag set is empty.
  136. // 3. Set newBrowsingContext's popup sandboxing flag set to a clone of sandboxFlags.
  137. }
  138. // 6. Return newBrowsingContext.
  139. return new_browsing_context;
  140. }
  141. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#initialise-the-document-object
  142. WebIDL::ExceptionOr<JS::NonnullGCPtr<Document>> Document::create_and_initialize(Type type, String content_type, HTML::NavigationParams& navigation_params)
  143. {
  144. // 1. Let browsingContext be navigationParams's navigable's active browsing context.
  145. auto browsing_context = navigation_params.navigable->active_browsing_context();
  146. // 2. Set browsingContext to the result of the obtaining a browsing context to use for a navigation response given browsingContext, navigationParams's final sandboxing flag set,
  147. // navigationParams's cross-origin opener policy, and navigationParams's COOP enforcement result.
  148. browsing_context = obtain_a_browsing_context_to_use_for_a_navigation_response(
  149. *browsing_context,
  150. navigation_params.final_sandboxing_flag_set,
  151. navigation_params.cross_origin_opener_policy,
  152. navigation_params.coop_enforcement_result);
  153. // FIXME: 3. Let permissionsPolicy be the result of creating a permissions policy from a response
  154. // given browsingContext, navigationParams's origin, and navigationParams's response.
  155. // 4. Let creationURL be navigationParams's response's URL.
  156. auto creation_url = navigation_params.response->url();
  157. // 5. If navigationParams's request is non-null, then set creationURL to navigationParams's request's current URL.
  158. if (navigation_params.request) {
  159. creation_url = navigation_params.request->current_url();
  160. }
  161. // 6. Let window be null.
  162. JS::GCPtr<HTML::Window> window;
  163. // 7. If browsingContext's active document's is initial about:blank is true,
  164. // and browsingContext's active document's origin is same origin-domain with navigationParams's origin,
  165. // then set window to browsingContext's active window.
  166. // FIXME: still_on_its_initial_about_blank_document() is not in the spec anymore.
  167. // However, replacing this with the spec-mandated is_initial_about_blank() results in the browsing context
  168. // holding an incorrect active document for the replace from initial about:blank to the real document.
  169. // See #22293 for more details.
  170. if (false
  171. && (browsing_context->active_document() && browsing_context->active_document()->origin().is_same_origin(navigation_params.origin))) {
  172. window = browsing_context->active_window();
  173. }
  174. // 8. Otherwise:
  175. else {
  176. // FIXME: 1. Let oacHeader be the result of getting a structured field value given `Origin-Agent-Cluster` and "item" from response's header list.
  177. // FIXME: 2. Let requestsOAC be true if oacHeader is not null and oacHeader[0] is the boolean true; otherwise false.
  178. [[maybe_unused]] auto requests_oac = false;
  179. // FIXME: 3. If navigationParams's reserved environment is a non-secure context, then set requestsOAC to false.
  180. // FIXME: 4. Let agent be the result of obtaining a similar-origin window agent given navigationParams's origin, browsingContext's group, and requestsOAC.
  181. // 5. Let realm execution context be the result of creating a new JavaScript realm given agent and the following customizations:
  182. auto realm_execution_context = Bindings::create_a_new_javascript_realm(
  183. Bindings::main_thread_vm(),
  184. [&](JS::Realm& realm) -> JS::Object* {
  185. // - For the global object, create a new Window object.
  186. window = HTML::Window::create(realm);
  187. return window;
  188. },
  189. [&](JS::Realm&) -> JS::Object* {
  190. // - For the global this binding, use browsingContext's WindowProxy object.
  191. return browsing_context->window_proxy();
  192. });
  193. // 6. Set window to the global object of realmExecutionContext's Realm component.
  194. window = verify_cast<HTML::Window>(realm_execution_context->realm->global_object());
  195. // 7. Let topLevelCreationURL be creationURL.
  196. auto top_level_creation_url = creation_url;
  197. // 8. Let topLevelOrigin be navigationParams's origin.
  198. auto top_level_origin = navigation_params.origin;
  199. // 9. If navigable's container is not null, then:
  200. if (navigation_params.navigable->container()) {
  201. // 1. Let parentEnvironment be navigable's container's relevant settings object.
  202. auto& parent_environment = HTML::relevant_settings_object(*navigation_params.navigable->container());
  203. // 2. Set topLevelCreationURL to parentEnvironment's top-level creation URL.
  204. top_level_creation_url = parent_environment.top_level_creation_url;
  205. // 3. Set topLevelOrigin to parentEnvironment's top-level origin.
  206. top_level_origin = parent_environment.top_level_origin;
  207. }
  208. // 9. Set up a window environment settings object with creationURL, realm execution context,
  209. // navigationParams's reserved environment, topLevelCreationURL, and topLevelOrigin.
  210. // FIXME: Why do we assume `creation_url` is non-empty here? Is this a spec bug?
  211. // FIXME: Why do we assume `top_level_creation_url` is non-empty here? Is this a spec bug?
  212. HTML::WindowEnvironmentSettingsObject::setup(
  213. browsing_context->page(),
  214. creation_url.value(),
  215. move(realm_execution_context),
  216. navigation_params.reserved_environment.visit(
  217. // FIXME: Environment is virtual. We *really* shouldn't be slicing it here
  218. [](Empty const&) -> Optional<HTML::Environment> { return {}; },
  219. [](HTML::Environment* env) -> Optional<HTML::Environment> { if (env) return *env; return {}; },
  220. [](JS::NonnullGCPtr<HTML::EnvironmentSettingsObject>) -> Optional<HTML::Environment> {
  221. TODO();
  222. }),
  223. top_level_creation_url.value(),
  224. top_level_origin);
  225. }
  226. // FIXME: 9. Let loadTimingInfo be a new document load timing info with its navigation start time set to navigationParams's response's timing info's start time.
  227. // 10. Let document be a new Document, with
  228. // type: type
  229. // content type: contentType
  230. // origin: navigationParams's origin
  231. // browsing context: browsingContext
  232. // policy container: navigationParams's policy container
  233. // FIXME: permissions policy: permissionsPolicy
  234. // active sandboxing flag set: navigationParams's final sandboxing flag set
  235. // FIXME: cross-origin opener policy: navigationParams's cross-origin opener policy
  236. // FIXME: load timing info: loadTimingInfo
  237. // FIXME: was created via cross-origin redirects: navigationParams's response's has cross-origin redirects
  238. // during-loading navigation ID for WebDriver BiDi: navigationParams's id
  239. // URL: creationURL
  240. // current document readiness: "loading"
  241. // about base URL: navigationParams's about base URL
  242. // FIXME: allow declarative shadow roots: true
  243. auto document = HTML::HTMLDocument::create(window->realm());
  244. document->m_type = type;
  245. document->m_content_type = move(content_type);
  246. document->set_origin(navigation_params.origin);
  247. document->set_browsing_context(browsing_context);
  248. document->m_policy_container = navigation_params.policy_container;
  249. document->m_active_sandboxing_flag_set = navigation_params.final_sandboxing_flag_set;
  250. document->m_navigation_id = navigation_params.id;
  251. document->set_url(*creation_url);
  252. document->m_readiness = HTML::DocumentReadyState::Loading;
  253. document->m_about_base_url = navigation_params.about_base_url;
  254. document->m_window = window;
  255. // 11. Set window's associated Document to document.
  256. window->set_associated_document(*document);
  257. // FIXME: 12. Run CSP initialization for a Document given document.
  258. // 13. If navigationParams's request is non-null, then:
  259. if (navigation_params.request) {
  260. // 1. Set document's referrer to the empty string.
  261. document->m_referrer = String {};
  262. // 2. Let referrer be navigationParams's request's referrer.
  263. auto const& referrer = navigation_params.request->referrer();
  264. // 3. If referrer is a URL record, then set document's referrer to the serialization of referrer.
  265. if (referrer.has<AK::URL>()) {
  266. document->m_referrer = MUST(String::from_byte_string(referrer.get<AK::URL>().serialize()));
  267. }
  268. }
  269. // FIXME: 14: If navigationParams's fetch controller is not null, then:
  270. // FIXME: 15. Create the navigation timing entry for document, with navigationParams's response's timing info, redirectCount, navigationParams's navigation timing type, and
  271. // navigationParams's response's service worker timing info.
  272. // FIXME: 16. If navigationParams's response has a `Refresh` header, then:
  273. // FIXME: 17. If navigationParams's commit early hints is not null, then call navigationParams's commit early hints with document.
  274. // FIXME: 18. Process link headers given document, navigationParams's response, and "pre-media".
  275. // 19. Return document.
  276. return document;
  277. }
  278. WebIDL::ExceptionOr<JS::NonnullGCPtr<Document>> Document::construct_impl(JS::Realm& realm)
  279. {
  280. return Document::create(realm);
  281. }
  282. JS::NonnullGCPtr<Document> Document::create(JS::Realm& realm, AK::URL const& url)
  283. {
  284. return realm.heap().allocate<Document>(realm, realm, url);
  285. }
  286. Document::Document(JS::Realm& realm, const AK::URL& url)
  287. : ParentNode(realm, *this, NodeType::DOCUMENT_NODE)
  288. , m_page(Bindings::host_defined_page(realm))
  289. , m_style_computer(make<CSS::StyleComputer>(*this))
  290. , m_url(url)
  291. {
  292. HTML::main_thread_event_loop().register_document({}, *this);
  293. m_style_update_timer = Core::Timer::create_single_shot(0, [this] {
  294. update_style();
  295. }).release_value_but_fixme_should_propagate_errors();
  296. m_layout_update_timer = Core::Timer::create_single_shot(0, [this] {
  297. update_layout();
  298. }).release_value_but_fixme_should_propagate_errors();
  299. }
  300. Document::~Document()
  301. {
  302. HTML::main_thread_event_loop().unregister_document({}, *this);
  303. }
  304. void Document::initialize(JS::Realm& realm)
  305. {
  306. Base::initialize(realm);
  307. set_prototype(&Bindings::ensure_web_prototype<Bindings::DocumentPrototype>(realm, "Document"_fly_string));
  308. m_selection = heap().allocate<Selection::Selection>(realm, realm, *this);
  309. m_list_of_available_images = heap().allocate<HTML::ListOfAvailableImages>(realm);
  310. }
  311. // https://html.spec.whatwg.org/multipage/document-lifecycle.html#populate-with-html/head/body
  312. WebIDL::ExceptionOr<void> Document::populate_with_html_head_and_body()
  313. {
  314. // 1. Let html be the result of creating an element given document, html, and the HTML namespace.
  315. auto html = TRY(DOM::create_element(*this, HTML::TagNames::html, Namespace::HTML));
  316. // 2. Let head be the result of creating an element given document, head, and the HTML namespace.
  317. auto head = TRY(DOM::create_element(*this, HTML::TagNames::head, Namespace::HTML));
  318. // 3. Let body be the result of creating an element given document, body, and the HTML namespace.
  319. auto body = TRY(DOM::create_element(*this, HTML::TagNames::body, Namespace::HTML));
  320. // 4. Append html to document.
  321. TRY(append_child(html));
  322. // 5. Append head to html.
  323. TRY(html->append_child(head));
  324. // 6. Append body to html.
  325. TRY(html->append_child(body));
  326. return {};
  327. }
  328. void Document::visit_edges(Cell::Visitor& visitor)
  329. {
  330. Base::visit_edges(visitor);
  331. visitor.visit(m_page);
  332. visitor.visit(m_window);
  333. visitor.visit(m_layout_root);
  334. visitor.visit(m_style_sheets);
  335. visitor.visit(m_hovered_node);
  336. visitor.visit(m_inspected_node);
  337. visitor.visit(m_active_favicon);
  338. visitor.visit(m_focused_element);
  339. visitor.visit(m_active_element);
  340. visitor.visit(m_target_element);
  341. visitor.visit(m_implementation);
  342. visitor.visit(m_current_script);
  343. visitor.visit(m_associated_inert_template_document);
  344. visitor.visit(m_appropriate_template_contents_owner_document);
  345. visitor.visit(m_pending_parsing_blocking_script);
  346. visitor.visit(m_history);
  347. visitor.visit(m_browsing_context);
  348. visitor.visit(m_applets);
  349. visitor.visit(m_anchors);
  350. visitor.visit(m_images);
  351. visitor.visit(m_embeds);
  352. visitor.visit(m_links);
  353. visitor.visit(m_forms);
  354. visitor.visit(m_scripts);
  355. visitor.visit(m_all);
  356. visitor.visit(m_selection);
  357. visitor.visit(m_first_base_element_with_href_in_tree_order);
  358. visitor.visit(m_parser);
  359. visitor.visit(m_lazy_load_intersection_observer);
  360. visitor.visit(m_visual_viewport);
  361. visitor.visit(m_latest_entry);
  362. visitor.visit(m_default_timeline);
  363. for (auto& script : m_scripts_to_execute_when_parsing_has_finished)
  364. visitor.visit(script);
  365. for (auto& script : m_scripts_to_execute_in_order_as_soon_as_possible)
  366. visitor.visit(script);
  367. for (auto& script : m_scripts_to_execute_as_soon_as_possible)
  368. visitor.visit(script);
  369. for (auto& node_iterator : m_node_iterators)
  370. visitor.visit(node_iterator);
  371. for (auto& document_observer : m_document_observers)
  372. visitor.visit(document_observer);
  373. for (auto& target : m_pending_scroll_event_targets)
  374. visitor.visit(target);
  375. for (auto& target : m_pending_scrollend_event_targets)
  376. visitor.visit(target);
  377. for (auto& observer : m_intersection_observers)
  378. visitor.visit(observer);
  379. for (auto& image : m_shared_image_requests)
  380. visitor.visit(image.value);
  381. for (auto& timeline : m_associated_animation_timelines)
  382. visitor.visit(timeline);
  383. visitor.visit(m_list_of_available_images);
  384. for (auto* form_associated_element : m_form_associated_elements_with_form_attribute)
  385. visitor.visit(form_associated_element->form_associated_element_to_html_element());
  386. }
  387. // https://w3c.github.io/selection-api/#dom-document-getselection
  388. JS::GCPtr<Selection::Selection> Document::get_selection() const
  389. {
  390. // The method must return the selection associated with this if this has an associated browsing context,
  391. // and it must return null otherwise.
  392. if (!browsing_context())
  393. return {};
  394. return m_selection;
  395. }
  396. // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-document-write
  397. WebIDL::ExceptionOr<void> Document::write(Vector<String> const& strings)
  398. {
  399. StringBuilder builder;
  400. builder.join(""sv, strings);
  401. return run_the_document_write_steps(builder.string_view());
  402. }
  403. // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-document-writeln
  404. WebIDL::ExceptionOr<void> Document::writeln(Vector<String> const& strings)
  405. {
  406. StringBuilder builder;
  407. builder.join(""sv, strings);
  408. builder.append("\n"sv);
  409. return run_the_document_write_steps(builder.string_view());
  410. }
  411. // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-write-steps
  412. WebIDL::ExceptionOr<void> Document::run_the_document_write_steps(StringView input)
  413. {
  414. // 1. If document is an XML document, then throw an "InvalidStateError" DOMException.
  415. if (m_type == Type::XML)
  416. return WebIDL::InvalidStateError::create(realm(), "write() called on XML document."_fly_string);
  417. // 2. If document's throw-on-dynamic-markup-insertion counter is greater than 0, then throw an "InvalidStateError" DOMException.
  418. if (m_throw_on_dynamic_markup_insertion_counter > 0)
  419. return WebIDL::InvalidStateError::create(realm(), "throw-on-dynamic-markup-insertion-counter greater than zero."_fly_string);
  420. // 3. If document's active parser was aborted is true, then return.
  421. if (m_active_parser_was_aborted)
  422. return {};
  423. // 4. If the insertion point is undefined, then:
  424. if (!(m_parser && m_parser->tokenizer().is_insertion_point_defined())) {
  425. // 1. If document's unload counter is greater than 0 or document's ignore-destructive-writes counter is greater than 0, then return.
  426. if (m_unload_counter > 0 || m_ignore_destructive_writes_counter > 0)
  427. return {};
  428. // 2. Run the document open steps with document.
  429. TRY(open());
  430. }
  431. // 5. Insert input into the input stream just before the insertion point.
  432. m_parser->tokenizer().insert_input_at_insertion_point(input);
  433. // 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).
  434. if (!pending_parsing_blocking_script())
  435. m_parser->run();
  436. return {};
  437. }
  438. // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-document-open
  439. WebIDL::ExceptionOr<Document*> Document::open(Optional<String> const&, Optional<String> const&)
  440. {
  441. // 1. If document is an XML document, then throw an "InvalidStateError" DOMException exception.
  442. if (m_type == Type::XML)
  443. return WebIDL::InvalidStateError::create(realm(), "open() called on XML document."_fly_string);
  444. // 2. If document's throw-on-dynamic-markup-insertion counter is greater than 0, then throw an "InvalidStateError" DOMException.
  445. if (m_throw_on_dynamic_markup_insertion_counter > 0)
  446. return WebIDL::InvalidStateError::create(realm(), "throw-on-dynamic-markup-insertion-counter greater than zero."_fly_string);
  447. // FIXME: 3. Let entryDocument be the entry global object's associated Document.
  448. auto& entry_document = *this;
  449. // 4. If document's origin is not same origin to entryDocument's origin, then throw a "SecurityError" DOMException.
  450. if (origin() != entry_document.origin())
  451. return WebIDL::SecurityError::create(realm(), "Document.origin() not the same as entryDocument's."_fly_string);
  452. // 5. If document has an active parser whose script nesting level is greater than 0, then return document.
  453. if (m_parser && m_parser->script_nesting_level() > 0)
  454. return this;
  455. // 6. Similarly, if document's unload counter is greater than 0, then return document.
  456. if (m_unload_counter > 0)
  457. return this;
  458. // 7. If document's active parser was aborted is true, then return document.
  459. if (m_active_parser_was_aborted)
  460. return this;
  461. // 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.
  462. // FIXME: 9. For each shadow-including inclusive descendant node of document, erase all event listeners and handlers given node.
  463. // 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.
  464. // 11. Replace all with null within document, without firing any mutation events.
  465. replace_all(nullptr);
  466. // 12. If document is fully active, then:
  467. if (is_fully_active()) {
  468. // 1. Let newURL be a copy of entryDocument's URL.
  469. auto new_url = entry_document.url();
  470. // 2. If entryDocument is not document, then set newURL's fragment to null.
  471. if (&entry_document != this)
  472. new_url.set_fragment({});
  473. // FIXME: 3. Run the URL and history update steps with document and newURL.
  474. }
  475. // 13. Set document's is initial about:blank to false.
  476. set_is_initial_about_blank(false);
  477. // FIXME: 14. If document's iframe load in progress flag is set, then set document's mute iframe load flag.
  478. // 15. Set document to no-quirks mode.
  479. set_quirks_mode(QuirksMode::No);
  480. // 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.
  481. m_parser = HTML::HTMLParser::create_for_scripting(*this);
  482. // 17. Set the insertion point to point at just before the end of the input stream (which at this point will be empty).
  483. m_parser->tokenizer().update_insertion_point();
  484. // 18. Update the current document readiness of document to "loading".
  485. update_readiness(HTML::DocumentReadyState::Loading);
  486. // 19. Return document.
  487. return this;
  488. }
  489. // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-document-open-window
  490. WebIDL::ExceptionOr<JS::GCPtr<HTML::WindowProxy>> Document::open(StringView url, StringView name, StringView features)
  491. {
  492. // 1. If this is not fully active, then throw an "InvalidAccessError" DOMException exception.
  493. if (!is_fully_active())
  494. return WebIDL::InvalidAccessError::create(realm(), "Cannot perform open on a document that isn't fully active."_fly_string);
  495. // 2. Return the result of running the window open steps with url, name, and features.
  496. return window().open_impl(url, name, features);
  497. }
  498. // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#closing-the-input-stream
  499. WebIDL::ExceptionOr<void> Document::close()
  500. {
  501. // 1. If document is an XML document, then throw an "InvalidStateError" DOMException exception.
  502. if (m_type == Type::XML)
  503. return WebIDL::InvalidStateError::create(realm(), "close() called on XML document."_fly_string);
  504. // 2. If document's throw-on-dynamic-markup-insertion counter is greater than 0, then throw an "InvalidStateError" DOMException.
  505. if (m_throw_on_dynamic_markup_insertion_counter > 0)
  506. return WebIDL::InvalidStateError::create(realm(), "throw-on-dynamic-markup-insertion-counter greater than zero."_fly_string);
  507. // 3. If there is no script-created parser associated with the document, then return.
  508. if (!m_parser)
  509. return {};
  510. // FIXME: 4. Insert an explicit "EOF" character at the end of the parser's input stream.
  511. m_parser->tokenizer().insert_eof();
  512. // 5. If there is a pending parsing-blocking script, then return.
  513. if (pending_parsing_blocking_script())
  514. return {};
  515. // 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.
  516. m_parser->run();
  517. return {};
  518. }
  519. HTML::Origin Document::origin() const
  520. {
  521. return m_origin;
  522. }
  523. void Document::set_origin(HTML::Origin const& origin)
  524. {
  525. m_origin = origin;
  526. }
  527. void Document::schedule_style_update()
  528. {
  529. if (m_style_update_timer->is_active())
  530. return;
  531. m_style_update_timer->start();
  532. }
  533. void Document::schedule_layout_update()
  534. {
  535. if (m_layout_update_timer->is_active())
  536. return;
  537. m_layout_update_timer->start();
  538. }
  539. bool Document::is_child_allowed(Node const& node) const
  540. {
  541. switch (node.type()) {
  542. case NodeType::DOCUMENT_NODE:
  543. case NodeType::TEXT_NODE:
  544. return false;
  545. case NodeType::COMMENT_NODE:
  546. return true;
  547. case NodeType::DOCUMENT_TYPE_NODE:
  548. return !first_child_of_type<DocumentType>();
  549. case NodeType::ELEMENT_NODE:
  550. return !first_child_of_type<Element>();
  551. default:
  552. return false;
  553. }
  554. }
  555. Element* Document::document_element()
  556. {
  557. return first_child_of_type<Element>();
  558. }
  559. Element const* Document::document_element() const
  560. {
  561. return first_child_of_type<Element>();
  562. }
  563. // https://html.spec.whatwg.org/multipage/dom.html#the-html-element-2
  564. HTML::HTMLHtmlElement* Document::html_element()
  565. {
  566. // The html element of a document is its document element, if it's an html element, and null otherwise.
  567. auto* html = document_element();
  568. if (is<HTML::HTMLHtmlElement>(html))
  569. return verify_cast<HTML::HTMLHtmlElement>(html);
  570. return nullptr;
  571. }
  572. // https://html.spec.whatwg.org/multipage/dom.html#the-head-element-2
  573. HTML::HTMLHeadElement* Document::head()
  574. {
  575. // The head element of a document is the first head element that is a child of the html element, if there is one,
  576. // or null otherwise.
  577. auto* html = html_element();
  578. if (!html)
  579. return nullptr;
  580. return html->first_child_of_type<HTML::HTMLHeadElement>();
  581. }
  582. // https://html.spec.whatwg.org/multipage/dom.html#the-title-element-2
  583. JS::GCPtr<HTML::HTMLTitleElement> Document::title_element()
  584. {
  585. // The title element of a document is the first title element in the document (in tree order), if there is one, or
  586. // null otherwise.
  587. JS::GCPtr<HTML::HTMLTitleElement> title_element = nullptr;
  588. for_each_in_subtree_of_type<HTML::HTMLTitleElement>([&](auto& title_element_in_tree) {
  589. title_element = title_element_in_tree;
  590. return IterationDecision::Break;
  591. });
  592. return title_element;
  593. }
  594. HTML::HTMLElement* Document::body()
  595. {
  596. auto* html = html_element();
  597. if (!html)
  598. return nullptr;
  599. auto* first_body = html->first_child_of_type<HTML::HTMLBodyElement>();
  600. if (first_body)
  601. return first_body;
  602. auto* first_frameset = html->first_child_of_type<HTML::HTMLFrameSetElement>();
  603. if (first_frameset)
  604. return first_frameset;
  605. return nullptr;
  606. }
  607. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-body
  608. WebIDL::ExceptionOr<void> Document::set_body(HTML::HTMLElement* new_body)
  609. {
  610. if (!is<HTML::HTMLBodyElement>(new_body) && !is<HTML::HTMLFrameSetElement>(new_body))
  611. return WebIDL::HierarchyRequestError::create(realm(), "Invalid document body element, must be 'body' or 'frameset'"_fly_string);
  612. auto* existing_body = body();
  613. if (existing_body) {
  614. (void)TRY(existing_body->parent()->replace_child(*new_body, *existing_body));
  615. return {};
  616. }
  617. auto* document_element = this->document_element();
  618. if (!document_element)
  619. return WebIDL::HierarchyRequestError::create(realm(), "Missing document element"_fly_string);
  620. (void)TRY(document_element->append_child(*new_body));
  621. return {};
  622. }
  623. // https://html.spec.whatwg.org/multipage/dom.html#document.title
  624. String Document::title() const
  625. {
  626. String value;
  627. // 1. If the document element is an SVG svg element, then let value be the child text content of the first SVG title
  628. // element that is a child of the document element.
  629. if (auto const* document_element = this->document_element(); is<SVG::SVGElement>(document_element)) {
  630. if (auto const* title_element = document_element->first_child_of_type<SVG::SVGTitleElement>())
  631. value = title_element->child_text_content();
  632. }
  633. // 2. Otherwise, let value be the child text content of the title element, or the empty string if the title element
  634. // is null.
  635. else if (auto title_element = this->title_element()) {
  636. value = title_element->text_content().value_or(String {});
  637. }
  638. // 3. Strip and collapse ASCII whitespace in value.
  639. auto title = Infra::strip_and_collapse_whitespace(value).release_value_but_fixme_should_propagate_errors();
  640. // 4. Return value.
  641. return title;
  642. }
  643. // https://html.spec.whatwg.org/multipage/dom.html#document.title
  644. WebIDL::ExceptionOr<void> Document::set_title(String const& title)
  645. {
  646. auto* document_element = this->document_element();
  647. // -> If the document element is an SVG svg element
  648. if (is<SVG::SVGElement>(document_element)) {
  649. JS::GCPtr<Element> element;
  650. // 1. If there is an SVG title element that is a child of the document element, let element be the first such
  651. // element.
  652. if (auto* title_element = document_element->first_child_of_type<SVG::SVGTitleElement>()) {
  653. element = title_element;
  654. }
  655. // 2. Otherwise:
  656. else {
  657. // 1. Let element be the result of creating an element given the document element's node document, title,
  658. // and the SVG namespace.
  659. element = TRY(DOM::create_element(*this, HTML::TagNames::title, Namespace::SVG));
  660. // 2. Insert element as the first child of the document element.
  661. document_element->insert_before(*element, nullptr);
  662. }
  663. // 3. String replace all with the given value within element.
  664. element->string_replace_all(title);
  665. }
  666. // -> If the document element is in the HTML namespace
  667. else if (document_element && document_element->namespace_uri() == Namespace::HTML) {
  668. auto title_element = this->title_element();
  669. auto* head_element = this->head();
  670. // 1. If the title element is null and the head element is null, then return.
  671. if (title_element == nullptr && head_element == nullptr)
  672. return {};
  673. JS::GCPtr<Element> element;
  674. // 2. If the title element is non-null, let element be the title element.
  675. if (title_element) {
  676. element = title_element;
  677. }
  678. // 3. Otherwise:
  679. else {
  680. // 1. Let element be the result of creating an element given the document element's node document, title,
  681. // and the HTML namespace.
  682. element = TRY(DOM::create_element(*this, HTML::TagNames::title, Namespace::HTML));
  683. // 2. Append element to the head element.
  684. TRY(head_element->append_child(*element));
  685. }
  686. // 4. String replace all with the given value within element.
  687. element->string_replace_all(title);
  688. }
  689. // -> Otherwise
  690. else {
  691. // Do nothing.
  692. return {};
  693. }
  694. if (browsing_context() == &page().top_level_browsing_context())
  695. page().client().page_did_change_title(title.to_byte_string());
  696. return {};
  697. }
  698. void Document::tear_down_layout_tree()
  699. {
  700. m_layout_root = nullptr;
  701. m_paintable = nullptr;
  702. }
  703. Color Document::background_color() const
  704. {
  705. // CSS2 says we should use the HTML element's background color unless it's transparent...
  706. if (auto* html_element = this->html_element(); html_element && html_element->layout_node()) {
  707. auto color = html_element->layout_node()->computed_values().background_color();
  708. if (color.alpha())
  709. return color;
  710. }
  711. // ...in which case we use the BODY element's background color.
  712. if (auto* body_element = body(); body_element && body_element->layout_node()) {
  713. auto color = body_element->layout_node()->computed_values().background_color();
  714. return color;
  715. }
  716. // By default, the document is transparent.
  717. // The outermost canvas is colored by the PageHost.
  718. return Color::Transparent;
  719. }
  720. Vector<CSS::BackgroundLayerData> const* Document::background_layers() const
  721. {
  722. auto* body_element = body();
  723. if (!body_element)
  724. return {};
  725. auto* body_layout_node = body_element->layout_node();
  726. if (!body_layout_node)
  727. return {};
  728. return &body_layout_node->background_layers();
  729. }
  730. void Document::update_base_element(Badge<HTML::HTMLBaseElement>)
  731. {
  732. JS::GCPtr<HTML::HTMLBaseElement const> base_element;
  733. for_each_in_subtree_of_type<HTML::HTMLBaseElement>([&base_element](HTML::HTMLBaseElement const& base_element_in_tree) {
  734. if (base_element_in_tree.has_attribute(HTML::AttributeNames::href)) {
  735. base_element = &base_element_in_tree;
  736. return IterationDecision::Break;
  737. }
  738. return IterationDecision::Continue;
  739. });
  740. m_first_base_element_with_href_in_tree_order = base_element;
  741. }
  742. JS::GCPtr<HTML::HTMLBaseElement const> Document::first_base_element_with_href_in_tree_order() const
  743. {
  744. return m_first_base_element_with_href_in_tree_order;
  745. }
  746. // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fallback-base-url
  747. AK::URL Document::fallback_base_url() const
  748. {
  749. // 1. If document is an iframe srcdoc document, then:
  750. if (HTML::url_matches_about_srcdoc(m_url)) {
  751. // 1. Assert: document's about base URL is non-null.
  752. VERIFY(m_about_base_url.has_value());
  753. // 2. Return document's about base URL.
  754. return m_about_base_url.value();
  755. }
  756. // 2. If document's URL matches about:blank and document's about base URL is non-null, then return document's about base URL.
  757. if (HTML::url_matches_about_blank(m_url) && m_about_base_url.has_value())
  758. return m_about_base_url.value();
  759. // 3. Return document's URL.
  760. return m_url;
  761. }
  762. // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#document-base-url
  763. AK::URL Document::base_url() const
  764. {
  765. // 1. If there is no base element that has an href attribute in the Document, then return the Document's fallback base URL.
  766. auto base_element = first_base_element_with_href_in_tree_order();
  767. if (!base_element)
  768. return fallback_base_url();
  769. // 2. Otherwise, return the frozen base URL of the first base element in the Document that has an href attribute, in tree order.
  770. return base_element->frozen_base_url();
  771. }
  772. // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#parse-a-url
  773. AK::URL Document::parse_url(StringView url) const
  774. {
  775. // FIXME: Pass in document's character encoding.
  776. return base_url().complete_url(url);
  777. }
  778. void Document::set_needs_layout()
  779. {
  780. if (m_needs_layout)
  781. return;
  782. m_needs_layout = true;
  783. schedule_layout_update();
  784. }
  785. void Document::force_layout()
  786. {
  787. tear_down_layout_tree();
  788. update_layout();
  789. }
  790. void Document::invalidate_layout()
  791. {
  792. tear_down_layout_tree();
  793. schedule_layout_update();
  794. }
  795. static void propagate_overflow_to_viewport(Element& root_element, Layout::Viewport& viewport)
  796. {
  797. // https://drafts.csswg.org/css-overflow-3/#overflow-propagation
  798. // UAs must apply the overflow-* values set on the root element to the viewport
  799. // when the root element’s display value is not none.
  800. auto* overflow_origin_node = root_element.layout_node();
  801. auto& viewport_computed_values = viewport.mutable_computed_values();
  802. // However, when the root element is an [HTML] html element (including XML syntax for HTML)
  803. // whose overflow value is visible (in both axes), and that element has as a child
  804. // a body element whose display value is also not none,
  805. // user agents must instead apply the overflow-* values of the first such child element to the viewport.
  806. if (root_element.is_html_html_element()) {
  807. auto* root_element_layout_node = root_element.layout_node();
  808. auto& root_element_computed_values = root_element_layout_node->mutable_computed_values();
  809. if (root_element_computed_values.overflow_x() == CSS::Overflow::Visible && root_element_computed_values.overflow_y() == CSS::Overflow::Visible) {
  810. auto* body_element = root_element.first_child_of_type<HTML::HTMLBodyElement>();
  811. if (body_element && body_element->layout_node())
  812. overflow_origin_node = body_element->layout_node();
  813. }
  814. }
  815. // NOTE: This is where we assign the chosen overflow values to the viewport.
  816. auto& overflow_origin_computed_values = overflow_origin_node->mutable_computed_values();
  817. viewport_computed_values.set_overflow_x(overflow_origin_computed_values.overflow_x());
  818. viewport_computed_values.set_overflow_y(overflow_origin_computed_values.overflow_y());
  819. // The element from which the value is propagated must then have a used overflow value of visible.
  820. overflow_origin_computed_values.set_overflow_x(CSS::Overflow::Visible);
  821. overflow_origin_computed_values.set_overflow_y(CSS::Overflow::Visible);
  822. }
  823. void Document::update_layout()
  824. {
  825. if (!is_active())
  826. return;
  827. // NOTE: If our parent document needs a relayout, we must do that *first*.
  828. // This is necessary as the parent layout may cause our viewport to change.
  829. if (navigable() && navigable()->container())
  830. navigable()->container()->document().update_layout();
  831. update_style();
  832. if (!m_needs_layout && m_layout_root)
  833. return;
  834. // NOTE: If this is a document hosting <template> contents, layout is unnecessary.
  835. if (m_created_for_appropriate_template_contents)
  836. return;
  837. if (!navigable())
  838. return;
  839. auto viewport_rect = this->viewport_rect();
  840. if (!m_layout_root) {
  841. Layout::TreeBuilder tree_builder;
  842. m_layout_root = verify_cast<Layout::Viewport>(*tree_builder.build(*this));
  843. if (auto* document_element = this->document_element()) {
  844. propagate_overflow_to_viewport(*document_element, *m_layout_root);
  845. }
  846. }
  847. Layout::LayoutState layout_state;
  848. {
  849. Layout::BlockFormattingContext root_formatting_context(layout_state, *m_layout_root, nullptr);
  850. auto& viewport = static_cast<Layout::Viewport&>(*m_layout_root);
  851. auto& viewport_state = layout_state.get_mutable(viewport);
  852. viewport_state.set_content_width(viewport_rect.width());
  853. viewport_state.set_content_height(viewport_rect.height());
  854. if (auto* document_element = this->document_element()) {
  855. VERIFY(document_element->layout_node());
  856. auto& icb_state = layout_state.get_mutable(verify_cast<Layout::NodeWithStyleAndBoxModelMetrics>(*document_element->layout_node()));
  857. icb_state.set_content_width(viewport_rect.width());
  858. }
  859. root_formatting_context.run(
  860. *m_layout_root,
  861. Layout::LayoutMode::Normal,
  862. Layout::AvailableSpace(
  863. Layout::AvailableSize::make_definite(viewport_rect.width()),
  864. Layout::AvailableSize::make_definite(viewport_rect.height())));
  865. }
  866. layout_state.commit(*m_layout_root);
  867. // Broadcast the current viewport rect to any new paintables, so they know whether they're visible or not.
  868. inform_all_viewport_clients_about_the_current_viewport_rect();
  869. navigable()->set_needs_display();
  870. set_needs_to_resolve_paint_only_properties();
  871. if (navigable()->is_traversable()) {
  872. // NOTE: The assignment of scroll frames only needs to occur for traversables because they take care of all
  873. // nested navigable documents.
  874. paintable()->assign_scroll_frames();
  875. paintable()->assign_clip_frames();
  876. page().client().page_did_layout();
  877. }
  878. m_layout_root->recompute_selection_states();
  879. m_needs_layout = false;
  880. m_layout_update_timer->stop();
  881. }
  882. [[nodiscard]] static Element::RequiredInvalidationAfterStyleChange update_style_recursively(Node& node)
  883. {
  884. bool const needs_full_style_update = node.document().needs_full_style_update();
  885. Element::RequiredInvalidationAfterStyleChange invalidation;
  886. // NOTE: If the current node has `display:none`, we can disregard all invalidation
  887. // caused by its children, as they will not be rendered anyway.
  888. // We will still recompute style for the children, though.
  889. bool is_display_none = false;
  890. if (is<Element>(node)) {
  891. invalidation |= static_cast<Element&>(node).recompute_style();
  892. is_display_none = static_cast<Element&>(node).computed_css_values()->display().is_none();
  893. }
  894. node.set_needs_style_update(false);
  895. if (needs_full_style_update || node.child_needs_style_update()) {
  896. if (node.is_element()) {
  897. if (auto* shadow_root = static_cast<DOM::Element&>(node).shadow_root_internal()) {
  898. if (needs_full_style_update || shadow_root->needs_style_update() || shadow_root->child_needs_style_update()) {
  899. auto subtree_invalidation = update_style_recursively(*shadow_root);
  900. if (!is_display_none)
  901. invalidation |= subtree_invalidation;
  902. }
  903. }
  904. }
  905. node.for_each_child([&](auto& child) {
  906. if (needs_full_style_update || child.needs_style_update() || child.child_needs_style_update()) {
  907. auto subtree_invalidation = update_style_recursively(child);
  908. if (!is_display_none)
  909. invalidation |= subtree_invalidation;
  910. }
  911. return IterationDecision::Continue;
  912. });
  913. }
  914. node.set_child_needs_style_update(false);
  915. return invalidation;
  916. }
  917. void Document::update_style()
  918. {
  919. if (!browsing_context())
  920. return;
  921. if (!needs_full_style_update() && !needs_style_update() && !child_needs_style_update())
  922. return;
  923. // NOTE: If this is a document hosting <template> contents, style update is unnecessary.
  924. if (m_created_for_appropriate_template_contents)
  925. return;
  926. // Fetch the viewport rect once, instead of repeatedly, during style computation.
  927. style_computer().set_viewport_rect({}, viewport_rect());
  928. evaluate_media_rules();
  929. auto invalidation = update_style_recursively(*this);
  930. if (invalidation.rebuild_layout_tree) {
  931. invalidate_layout();
  932. } else {
  933. if (invalidation.relayout)
  934. set_needs_layout();
  935. if (invalidation.rebuild_stacking_context_tree)
  936. invalidate_stacking_context_tree();
  937. }
  938. m_needs_full_style_update = false;
  939. m_style_update_timer->stop();
  940. }
  941. void Document::update_paint_and_hit_testing_properties_if_needed()
  942. {
  943. if (!m_needs_to_resolve_paint_only_properties)
  944. return;
  945. m_needs_to_resolve_paint_only_properties = false;
  946. if (auto* paintable = this->paintable())
  947. paintable->resolve_paint_only_properties();
  948. }
  949. void Document::set_link_color(Color color)
  950. {
  951. m_link_color = color;
  952. }
  953. void Document::set_active_link_color(Color color)
  954. {
  955. m_active_link_color = color;
  956. }
  957. void Document::set_visited_link_color(Color color)
  958. {
  959. m_visited_link_color = color;
  960. }
  961. Layout::Viewport const* Document::layout_node() const
  962. {
  963. return static_cast<Layout::Viewport const*>(Node::layout_node());
  964. }
  965. Layout::Viewport* Document::layout_node()
  966. {
  967. return static_cast<Layout::Viewport*>(Node::layout_node());
  968. }
  969. void Document::set_inspected_node(Node* node, Optional<CSS::Selector::PseudoElement::Type> pseudo_element)
  970. {
  971. if (m_inspected_node.ptr() == node && m_inspected_pseudo_element == pseudo_element)
  972. return;
  973. if (auto layout_node = inspected_layout_node(); layout_node && layout_node->paintable())
  974. layout_node->paintable()->set_needs_display();
  975. m_inspected_node = node;
  976. m_inspected_pseudo_element = pseudo_element;
  977. if (auto layout_node = inspected_layout_node(); layout_node && layout_node->paintable())
  978. layout_node->paintable()->set_needs_display();
  979. }
  980. Layout::Node* Document::inspected_layout_node()
  981. {
  982. if (!m_inspected_node)
  983. return nullptr;
  984. if (!m_inspected_pseudo_element.has_value() || !m_inspected_node->is_element())
  985. return m_inspected_node->layout_node();
  986. auto& element = static_cast<Element&>(*m_inspected_node);
  987. return element.get_pseudo_element_node(m_inspected_pseudo_element.value());
  988. }
  989. static Node* find_common_ancestor(Node* a, Node* b)
  990. {
  991. if (!a || !b)
  992. return nullptr;
  993. if (a == b)
  994. return a;
  995. HashTable<Node*> ancestors;
  996. for (auto* node = a; node; node = node->parent_or_shadow_host())
  997. ancestors.set(node);
  998. for (auto* node = b; node; node = node->parent_or_shadow_host()) {
  999. if (ancestors.contains(node))
  1000. return node;
  1001. }
  1002. return nullptr;
  1003. }
  1004. void Document::set_hovered_node(Node* node)
  1005. {
  1006. if (m_hovered_node.ptr() == node)
  1007. return;
  1008. JS::GCPtr<Node> old_hovered_node = move(m_hovered_node);
  1009. m_hovered_node = node;
  1010. auto* common_ancestor = find_common_ancestor(old_hovered_node, m_hovered_node);
  1011. if (common_ancestor)
  1012. common_ancestor->invalidate_style();
  1013. else
  1014. invalidate_style();
  1015. // https://w3c.github.io/uievents/#mouseleave
  1016. if (old_hovered_node && (!m_hovered_node || !m_hovered_node->is_descendant_of(*old_hovered_node))) {
  1017. // FIXME: Check if we need to dispatch these events in a specific order.
  1018. for (auto target = old_hovered_node; target && target.ptr() != common_ancestor; target = target->parent()) {
  1019. // FIXME: Populate the event with mouse coordinates, etc.
  1020. target->dispatch_event(UIEvents::MouseEvent::create(realm(), UIEvents::EventNames::mouseleave));
  1021. }
  1022. }
  1023. // https://w3c.github.io/uievents/#mouseenter
  1024. if (m_hovered_node && (!old_hovered_node || !m_hovered_node->is_ancestor_of(*old_hovered_node))) {
  1025. // FIXME: Check if we need to dispatch these events in a specific order.
  1026. for (auto target = m_hovered_node; target && target.ptr() != common_ancestor; target = target->parent()) {
  1027. // FIXME: Populate the event with mouse coordinates, etc.
  1028. target->dispatch_event(UIEvents::MouseEvent::create(realm(), UIEvents::EventNames::mouseenter));
  1029. }
  1030. }
  1031. }
  1032. JS::NonnullGCPtr<HTMLCollection> Document::get_elements_by_name(FlyString const& name)
  1033. {
  1034. return HTMLCollection::create(*this, HTMLCollection::Scope::Descendants, [name](Element const& element) {
  1035. return element.name() == name;
  1036. });
  1037. }
  1038. JS::NonnullGCPtr<HTMLCollection> Document::get_elements_by_class_name(StringView class_names)
  1039. {
  1040. Vector<FlyString> list_of_class_names;
  1041. for (auto& name : class_names.split_view(' ')) {
  1042. list_of_class_names.append(FlyString::from_utf8(name).release_value_but_fixme_should_propagate_errors());
  1043. }
  1044. return HTMLCollection::create(*this, HTMLCollection::Scope::Descendants, [list_of_class_names = move(list_of_class_names), quirks_mode = document().in_quirks_mode()](Element const& element) {
  1045. for (auto& name : list_of_class_names) {
  1046. if (!element.has_class(name, quirks_mode ? CaseSensitivity::CaseInsensitive : CaseSensitivity::CaseSensitive))
  1047. return false;
  1048. }
  1049. return true;
  1050. });
  1051. }
  1052. // https://html.spec.whatwg.org/multipage/obsolete.html#dom-document-applets
  1053. JS::NonnullGCPtr<HTMLCollection> Document::applets()
  1054. {
  1055. if (!m_applets)
  1056. m_applets = HTMLCollection::create(*this, HTMLCollection::Scope::Descendants, [](auto&) { return false; });
  1057. return *m_applets;
  1058. }
  1059. // https://html.spec.whatwg.org/multipage/obsolete.html#dom-document-anchors
  1060. JS::NonnullGCPtr<HTMLCollection> Document::anchors()
  1061. {
  1062. if (!m_anchors) {
  1063. m_anchors = HTMLCollection::create(*this, HTMLCollection::Scope::Descendants, [](Element const& element) {
  1064. return is<HTML::HTMLAnchorElement>(element) && element.name().has_value();
  1065. });
  1066. }
  1067. return *m_anchors;
  1068. }
  1069. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-images
  1070. JS::NonnullGCPtr<HTMLCollection> Document::images()
  1071. {
  1072. if (!m_images) {
  1073. m_images = HTMLCollection::create(*this, HTMLCollection::Scope::Descendants, [](Element const& element) {
  1074. return is<HTML::HTMLImageElement>(element);
  1075. });
  1076. }
  1077. return *m_images;
  1078. }
  1079. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-embeds
  1080. JS::NonnullGCPtr<HTMLCollection> Document::embeds()
  1081. {
  1082. if (!m_embeds) {
  1083. m_embeds = HTMLCollection::create(*this, HTMLCollection::Scope::Descendants, [](Element const& element) {
  1084. return is<HTML::HTMLEmbedElement>(element);
  1085. });
  1086. }
  1087. return *m_embeds;
  1088. }
  1089. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-plugins
  1090. JS::NonnullGCPtr<HTMLCollection> Document::plugins()
  1091. {
  1092. return embeds();
  1093. }
  1094. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-links
  1095. JS::NonnullGCPtr<HTMLCollection> Document::links()
  1096. {
  1097. if (!m_links) {
  1098. m_links = HTMLCollection::create(*this, HTMLCollection::Scope::Descendants, [](Element const& element) {
  1099. return (is<HTML::HTMLAnchorElement>(element) || is<HTML::HTMLAreaElement>(element)) && element.has_attribute(HTML::AttributeNames::href);
  1100. });
  1101. }
  1102. return *m_links;
  1103. }
  1104. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-forms
  1105. JS::NonnullGCPtr<HTMLCollection> Document::forms()
  1106. {
  1107. if (!m_forms) {
  1108. m_forms = HTMLCollection::create(*this, HTMLCollection::Scope::Descendants, [](Element const& element) {
  1109. return is<HTML::HTMLFormElement>(element);
  1110. });
  1111. }
  1112. return *m_forms;
  1113. }
  1114. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-scripts
  1115. JS::NonnullGCPtr<HTMLCollection> Document::scripts()
  1116. {
  1117. if (!m_scripts) {
  1118. m_scripts = HTMLCollection::create(*this, HTMLCollection::Scope::Descendants, [](Element const& element) {
  1119. return is<HTML::HTMLScriptElement>(element);
  1120. });
  1121. }
  1122. return *m_scripts;
  1123. }
  1124. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-all
  1125. JS::NonnullGCPtr<HTMLCollection> Document::all()
  1126. {
  1127. if (!m_all) {
  1128. m_all = HTMLCollection::create(*this, HTMLCollection::Scope::Descendants, [](Element const&) {
  1129. return true;
  1130. });
  1131. }
  1132. return *m_all;
  1133. }
  1134. Color Document::link_color() const
  1135. {
  1136. if (m_link_color.has_value())
  1137. return m_link_color.value();
  1138. return CSS::SystemColor::link_text();
  1139. }
  1140. Color Document::active_link_color() const
  1141. {
  1142. if (m_active_link_color.has_value())
  1143. return m_active_link_color.value();
  1144. return CSS::SystemColor::active_text();
  1145. }
  1146. Color Document::visited_link_color() const
  1147. {
  1148. if (m_visited_link_color.has_value())
  1149. return m_visited_link_color.value();
  1150. return CSS::SystemColor::visited_text();
  1151. }
  1152. // https://html.spec.whatwg.org/multipage/webappapis.html#relevant-settings-object
  1153. HTML::EnvironmentSettingsObject& Document::relevant_settings_object() const
  1154. {
  1155. // Then, the relevant settings object for a platform object o is the environment settings object of the relevant Realm for o.
  1156. return Bindings::host_defined_environment_settings_object(realm());
  1157. }
  1158. // https://dom.spec.whatwg.org/#dom-document-createelement
  1159. WebIDL::ExceptionOr<JS::NonnullGCPtr<Element>> Document::create_element(String const& a_local_name, Variant<String, ElementCreationOptions> const& options)
  1160. {
  1161. auto local_name = a_local_name.to_byte_string();
  1162. // 1. If localName does not match the Name production, then throw an "InvalidCharacterError" DOMException.
  1163. if (!is_valid_name(a_local_name))
  1164. return WebIDL::InvalidCharacterError::create(realm(), "Invalid character in tag name."_fly_string);
  1165. // 2. If this is an HTML document, then set localName to localName in ASCII lowercase.
  1166. if (document_type() == Type::HTML)
  1167. local_name = local_name.to_lowercase();
  1168. // 3. Let is be null.
  1169. Optional<String> is_value;
  1170. // 4. If options is a dictionary and options["is"] exists, then set is to it.
  1171. if (options.has<ElementCreationOptions>()) {
  1172. auto const& element_creation_options = options.get<ElementCreationOptions>();
  1173. if (element_creation_options.is.has_value())
  1174. is_value = element_creation_options.is.value();
  1175. }
  1176. // 5. Let namespace be the HTML namespace, if this is an HTML document or this’s content type is "application/xhtml+xml"; otherwise null.
  1177. Optional<FlyString> namespace_;
  1178. if (document_type() == Type::HTML || content_type() == "application/xhtml+xml"sv)
  1179. namespace_ = Namespace::HTML;
  1180. // 6. Return the result of creating an element given this, localName, namespace, null, is, and with the synchronous custom elements flag set.
  1181. return TRY(DOM::create_element(*this, MUST(FlyString::from_deprecated_fly_string(local_name)), move(namespace_), {}, move(is_value), true));
  1182. }
  1183. // https://dom.spec.whatwg.org/#dom-document-createelementns
  1184. // https://dom.spec.whatwg.org/#internal-createelementns-steps
  1185. WebIDL::ExceptionOr<JS::NonnullGCPtr<Element>> Document::create_element_ns(Optional<FlyString> const& namespace_, String const& qualified_name, Variant<String, ElementCreationOptions> const& options)
  1186. {
  1187. // 1. Let namespace, prefix, and localName be the result of passing namespace and qualifiedName to validate and extract.
  1188. auto extracted_qualified_name = TRY(validate_and_extract(realm(), namespace_, qualified_name));
  1189. // 2. Let is be null.
  1190. Optional<String> is_value;
  1191. // 3. If options is a dictionary and options["is"] exists, then set is to it.
  1192. if (options.has<ElementCreationOptions>()) {
  1193. auto const& element_creation_options = options.get<ElementCreationOptions>();
  1194. if (element_creation_options.is.has_value())
  1195. is_value = element_creation_options.is.value();
  1196. }
  1197. // 4. Return the result of creating an element given document, localName, namespace, prefix, is, and with the synchronous custom elements flag set.
  1198. return TRY(DOM::create_element(*this, extracted_qualified_name.local_name(), extracted_qualified_name.namespace_(), extracted_qualified_name.prefix(), move(is_value), true));
  1199. }
  1200. JS::NonnullGCPtr<DocumentFragment> Document::create_document_fragment()
  1201. {
  1202. return heap().allocate<DocumentFragment>(realm(), *this);
  1203. }
  1204. JS::NonnullGCPtr<Text> Document::create_text_node(String const& data)
  1205. {
  1206. return heap().allocate<Text>(realm(), *this, data);
  1207. }
  1208. JS::NonnullGCPtr<Comment> Document::create_comment(String const& data)
  1209. {
  1210. return heap().allocate<Comment>(realm(), *this, data);
  1211. }
  1212. // https://dom.spec.whatwg.org/#dom-document-createprocessinginstruction
  1213. WebIDL::ExceptionOr<JS::NonnullGCPtr<ProcessingInstruction>> Document::create_processing_instruction(String const& target, String const& data)
  1214. {
  1215. // FIXME: 1. If target does not match the Name production, then throw an "InvalidCharacterError" DOMException.
  1216. // FIXME: 2. If data contains the string "?>", then throw an "InvalidCharacterError" DOMException.
  1217. // 3. Return a new ProcessingInstruction node, with target set to target, data set to data, and node document set to this.
  1218. return heap().allocate<ProcessingInstruction>(realm(), *this, data, target);
  1219. }
  1220. JS::NonnullGCPtr<Range> Document::create_range()
  1221. {
  1222. return Range::create(*this);
  1223. }
  1224. // https://dom.spec.whatwg.org/#dom-document-createevent
  1225. WebIDL::ExceptionOr<JS::NonnullGCPtr<Event>> Document::create_event(StringView interface)
  1226. {
  1227. auto& realm = this->realm();
  1228. // NOTE: This is named event here, since we do step 5 and 6 as soon as possible for each case.
  1229. // 1. Let constructor be null.
  1230. JS::GCPtr<Event> event;
  1231. // 2. If interface is an ASCII case-insensitive match for any of the strings in the first column in the following table,
  1232. // then set constructor to the interface in the second column on the same row as the matching string:
  1233. if (Infra::is_ascii_case_insensitive_match(interface, "beforeunloadevent"sv)) {
  1234. event = Event::create(realm, FlyString {}); // FIXME: Create BeforeUnloadEvent
  1235. } else if (Infra::is_ascii_case_insensitive_match(interface, "compositionevent"sv)) {
  1236. event = Event::create(realm, FlyString {}); // FIXME: Create CompositionEvent
  1237. } else if (Infra::is_ascii_case_insensitive_match(interface, "customevent"sv)) {
  1238. event = CustomEvent::create(realm, FlyString {});
  1239. } else if (Infra::is_ascii_case_insensitive_match(interface, "devicemotionevent"sv)) {
  1240. event = Event::create(realm, FlyString {}); // FIXME: Create DeviceMotionEvent
  1241. } else if (Infra::is_ascii_case_insensitive_match(interface, "deviceorientationevent"sv)) {
  1242. event = Event::create(realm, FlyString {}); // FIXME: Create DeviceOrientationEvent
  1243. } else if (Infra::is_ascii_case_insensitive_match(interface, "dragevent"sv)) {
  1244. event = Event::create(realm, FlyString {}); // FIXME: Create DragEvent
  1245. } else if (Infra::is_ascii_case_insensitive_match(interface, "event"sv)
  1246. || Infra::is_ascii_case_insensitive_match(interface, "events"sv)) {
  1247. event = Event::create(realm, FlyString {});
  1248. } else if (Infra::is_ascii_case_insensitive_match(interface, "focusevent"sv)) {
  1249. event = UIEvents::FocusEvent::create(realm, FlyString {});
  1250. } else if (Infra::is_ascii_case_insensitive_match(interface, "hashchangeevent"sv)) {
  1251. event = Event::create(realm, FlyString {}); // FIXME: Create HashChangeEvent
  1252. } else if (Infra::is_ascii_case_insensitive_match(interface, "htmlevents"sv)) {
  1253. event = Event::create(realm, FlyString {});
  1254. } else if (Infra::is_ascii_case_insensitive_match(interface, "keyboardevent"sv)) {
  1255. event = UIEvents::KeyboardEvent::create(realm, String {});
  1256. } else if (Infra::is_ascii_case_insensitive_match(interface, "messageevent"sv)) {
  1257. event = HTML::MessageEvent::create(realm, String {});
  1258. } else if (Infra::is_ascii_case_insensitive_match(interface, "mouseevent"sv)
  1259. || Infra::is_ascii_case_insensitive_match(interface, "mouseevents"sv)) {
  1260. event = UIEvents::MouseEvent::create(realm, FlyString {});
  1261. } else if (Infra::is_ascii_case_insensitive_match(interface, "storageevent"sv)) {
  1262. event = Event::create(realm, FlyString {}); // FIXME: Create StorageEvent
  1263. } else if (Infra::is_ascii_case_insensitive_match(interface, "svgevents"sv)) {
  1264. event = Event::create(realm, FlyString {});
  1265. } else if (Infra::is_ascii_case_insensitive_match(interface, "textevent"sv)) {
  1266. event = Event::create(realm, FlyString {}); // FIXME: Create CompositionEvent
  1267. } else if (Infra::is_ascii_case_insensitive_match(interface, "touchevent"sv)) {
  1268. event = Event::create(realm, FlyString {}); // FIXME: Create TouchEvent
  1269. } else if (Infra::is_ascii_case_insensitive_match(interface, "uievent"sv)
  1270. || Infra::is_ascii_case_insensitive_match(interface, "uievents"sv)) {
  1271. event = UIEvents::UIEvent::create(realm, FlyString {});
  1272. }
  1273. // 3. If constructor is null, then throw a "NotSupportedError" DOMException.
  1274. if (!event) {
  1275. return WebIDL::NotSupportedError::create(realm, "No constructor for interface found"_fly_string);
  1276. }
  1277. // FIXME: 4. If the interface indicated by constructor is not exposed on the relevant global object of this, then throw a "NotSupportedError" DOMException.
  1278. // NOTE: These are done in the if-chain above
  1279. // 5. Let event be the result of creating an event given constructor.
  1280. // 6. Initialize event’s type attribute to the empty string.
  1281. // NOTE: This is handled by each constructor.
  1282. // FIXME: 7. Initialize event’s timeStamp attribute to the result of calling current high resolution time with this’s relevant global object.
  1283. // 8. Initialize event’s isTrusted attribute to false.
  1284. event->set_is_trusted(false);
  1285. // 9. Unset event’s initialized flag.
  1286. event->set_initialized(false);
  1287. // 10. Return event.
  1288. return JS::NonnullGCPtr(*event);
  1289. }
  1290. void Document::set_pending_parsing_blocking_script(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement* script)
  1291. {
  1292. m_pending_parsing_blocking_script = script;
  1293. }
  1294. JS::NonnullGCPtr<HTML::HTMLScriptElement> Document::take_pending_parsing_blocking_script(Badge<HTML::HTMLParser>)
  1295. {
  1296. VERIFY(m_pending_parsing_blocking_script);
  1297. auto script = m_pending_parsing_blocking_script;
  1298. m_pending_parsing_blocking_script = nullptr;
  1299. return *script;
  1300. }
  1301. void Document::add_script_to_execute_when_parsing_has_finished(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement& script)
  1302. {
  1303. m_scripts_to_execute_when_parsing_has_finished.append(script);
  1304. }
  1305. Vector<JS::Handle<HTML::HTMLScriptElement>> Document::take_scripts_to_execute_when_parsing_has_finished(Badge<HTML::HTMLParser>)
  1306. {
  1307. Vector<JS::Handle<HTML::HTMLScriptElement>> handles;
  1308. for (auto script : m_scripts_to_execute_when_parsing_has_finished)
  1309. handles.append(JS::make_handle(script));
  1310. m_scripts_to_execute_when_parsing_has_finished.clear();
  1311. return handles;
  1312. }
  1313. void Document::add_script_to_execute_as_soon_as_possible(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement& script)
  1314. {
  1315. m_scripts_to_execute_as_soon_as_possible.append(script);
  1316. }
  1317. Vector<JS::Handle<HTML::HTMLScriptElement>> Document::take_scripts_to_execute_as_soon_as_possible(Badge<HTML::HTMLParser>)
  1318. {
  1319. Vector<JS::Handle<HTML::HTMLScriptElement>> handles;
  1320. for (auto script : m_scripts_to_execute_as_soon_as_possible)
  1321. handles.append(JS::make_handle(script));
  1322. m_scripts_to_execute_as_soon_as_possible.clear();
  1323. return handles;
  1324. }
  1325. void Document::add_script_to_execute_in_order_as_soon_as_possible(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement& script)
  1326. {
  1327. m_scripts_to_execute_in_order_as_soon_as_possible.append(script);
  1328. }
  1329. Vector<JS::Handle<HTML::HTMLScriptElement>> Document::take_scripts_to_execute_in_order_as_soon_as_possible(Badge<HTML::HTMLParser>)
  1330. {
  1331. Vector<JS::Handle<HTML::HTMLScriptElement>> handles;
  1332. for (auto script : m_scripts_to_execute_in_order_as_soon_as_possible)
  1333. handles.append(JS::make_handle(script));
  1334. m_scripts_to_execute_in_order_as_soon_as_possible.clear();
  1335. return handles;
  1336. }
  1337. // https://dom.spec.whatwg.org/#dom-document-importnode
  1338. WebIDL::ExceptionOr<JS::NonnullGCPtr<Node>> Document::import_node(JS::NonnullGCPtr<Node> node, bool deep)
  1339. {
  1340. // 1. If node is a document or shadow root, then throw a "NotSupportedError" DOMException.
  1341. if (is<Document>(*node) || is<ShadowRoot>(*node))
  1342. return WebIDL::NotSupportedError::create(realm(), "Cannot import a document or shadow root."_fly_string);
  1343. // 2. Return a clone of node, with this and the clone children flag set if deep is true.
  1344. return node->clone_node(this, deep);
  1345. }
  1346. // https://dom.spec.whatwg.org/#concept-node-adopt
  1347. void Document::adopt_node(Node& node)
  1348. {
  1349. // 1. Let oldDocument be node’s node document.
  1350. auto& old_document = node.document();
  1351. // 2. If node’s parent is non-null, then remove node.
  1352. if (node.parent())
  1353. node.remove();
  1354. // 3. If document is not oldDocument, then:
  1355. if (&old_document != this) {
  1356. // 1. For each inclusiveDescendant in node’s shadow-including inclusive descendants:
  1357. node.for_each_shadow_including_inclusive_descendant([&](DOM::Node& inclusive_descendant) {
  1358. // 1. Set inclusiveDescendant’s node document to document.
  1359. inclusive_descendant.set_document({}, *this);
  1360. // FIXME: 2. If inclusiveDescendant is an element, then set the node document of each attribute in inclusiveDescendant’s
  1361. // attribute list to document.
  1362. return IterationDecision::Continue;
  1363. });
  1364. // 2. For each inclusiveDescendant in node’s shadow-including inclusive descendants that is custom,
  1365. // enqueue a custom element callback reaction with inclusiveDescendant, callback name "adoptedCallback",
  1366. // and an argument list containing oldDocument and document.
  1367. node.for_each_shadow_including_inclusive_descendant([&](DOM::Node& inclusive_descendant) {
  1368. if (!is<DOM::Element>(inclusive_descendant))
  1369. return IterationDecision::Continue;
  1370. auto& element = static_cast<DOM::Element&>(inclusive_descendant);
  1371. if (element.is_custom()) {
  1372. auto& vm = this->vm();
  1373. JS::MarkedVector<JS::Value> arguments { vm.heap() };
  1374. arguments.append(&old_document);
  1375. arguments.append(this);
  1376. element.enqueue_a_custom_element_callback_reaction(HTML::CustomElementReactionNames::adoptedCallback, move(arguments));
  1377. }
  1378. return IterationDecision::Continue;
  1379. });
  1380. // 3. For each inclusiveDescendant in node’s shadow-including inclusive descendants, in shadow-including tree order,
  1381. // run the adopting steps with inclusiveDescendant and oldDocument.
  1382. node.for_each_shadow_including_inclusive_descendant([&](auto& inclusive_descendant) {
  1383. inclusive_descendant.adopted_from(old_document);
  1384. return IterationDecision::Continue;
  1385. });
  1386. // Transfer NodeIterators rooted at `node` from old_document to this document.
  1387. Vector<NodeIterator&> node_iterators_to_transfer;
  1388. for (auto node_iterator : old_document.m_node_iterators) {
  1389. if (node_iterator->root().ptr() == &node)
  1390. node_iterators_to_transfer.append(*node_iterator);
  1391. }
  1392. for (auto& node_iterator : node_iterators_to_transfer) {
  1393. old_document.m_node_iterators.remove(&node_iterator);
  1394. m_node_iterators.set(&node_iterator);
  1395. }
  1396. }
  1397. }
  1398. // https://dom.spec.whatwg.org/#dom-document-adoptnode
  1399. WebIDL::ExceptionOr<JS::NonnullGCPtr<Node>> Document::adopt_node_binding(JS::NonnullGCPtr<Node> node)
  1400. {
  1401. if (is<Document>(*node))
  1402. return WebIDL::NotSupportedError::create(realm(), "Cannot adopt a document into a document"_fly_string);
  1403. if (is<ShadowRoot>(*node))
  1404. return WebIDL::HierarchyRequestError::create(realm(), "Cannot adopt a shadow root into a document"_fly_string);
  1405. if (is<DocumentFragment>(*node) && verify_cast<DocumentFragment>(*node).host())
  1406. return node;
  1407. adopt_node(*node);
  1408. return node;
  1409. }
  1410. DocumentType const* Document::doctype() const
  1411. {
  1412. return first_child_of_type<DocumentType>();
  1413. }
  1414. String const& Document::compat_mode() const
  1415. {
  1416. static String const back_compat = "BackCompat"_string;
  1417. static String const css1_compat = "CSS1Compat"_string;
  1418. if (m_quirks_mode == QuirksMode::Yes)
  1419. return back_compat;
  1420. return css1_compat;
  1421. }
  1422. bool Document::is_editable() const
  1423. {
  1424. return m_editable;
  1425. }
  1426. void Document::set_focused_element(Element* element)
  1427. {
  1428. if (m_focused_element.ptr() == element)
  1429. return;
  1430. if (m_focused_element) {
  1431. m_focused_element->did_lose_focus();
  1432. m_focused_element->set_needs_style_update(true);
  1433. }
  1434. m_focused_element = element;
  1435. if (m_focused_element) {
  1436. m_focused_element->did_receive_focus();
  1437. m_focused_element->set_needs_style_update(true);
  1438. }
  1439. if (paintable())
  1440. paintable()->set_needs_display();
  1441. // Scroll the viewport if necessary to make the newly focused element visible.
  1442. if (m_focused_element) {
  1443. ScrollIntoViewOptions scroll_options;
  1444. scroll_options.block = Bindings::ScrollLogicalPosition::Nearest;
  1445. scroll_options.inline_ = Bindings::ScrollLogicalPosition::Nearest;
  1446. (void)m_focused_element->scroll_into_view(scroll_options);
  1447. }
  1448. }
  1449. void Document::set_active_element(Element* element)
  1450. {
  1451. if (m_active_element.ptr() == element)
  1452. return;
  1453. m_active_element = element;
  1454. if (paintable())
  1455. paintable()->set_needs_display();
  1456. }
  1457. void Document::set_target_element(Element* element)
  1458. {
  1459. if (m_target_element.ptr() == element)
  1460. return;
  1461. if (m_target_element)
  1462. m_target_element->set_needs_style_update(true);
  1463. m_target_element = element;
  1464. if (m_target_element)
  1465. m_target_element->set_needs_style_update(true);
  1466. if (paintable())
  1467. paintable()->set_needs_display();
  1468. }
  1469. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#the-indicated-part-of-the-document
  1470. Document::IndicatedPart Document::determine_the_indicated_part() const
  1471. {
  1472. // For an HTML document document, the following processing model must be followed to determine its indicated part:
  1473. // 1. Let fragment be document's URL's fragment.
  1474. auto fragment = url().fragment();
  1475. // 2. If fragment is the empty string, then return the special value top of the document.
  1476. if (!fragment.has_value() || fragment->is_empty())
  1477. return Document::TopOfTheDocument {};
  1478. // 3. Let potentialIndicatedElement be the result of finding a potential indicated element given document and fragment.
  1479. auto* potential_indicated_element = find_a_potential_indicated_element(*fragment);
  1480. // 4. If potentialIndicatedElement is not null, then return potentialIndicatedElement.
  1481. if (potential_indicated_element)
  1482. return potential_indicated_element;
  1483. // 5. Let fragmentBytes be the result of percent-decoding fragment.
  1484. // 6. Let decodedFragment be the result of running UTF-8 decode without BOM on fragmentBytes.
  1485. auto decoded_fragment = AK::URL::percent_decode(*fragment);
  1486. // 7. Set potentialIndicatedElement to the result of finding a potential indicated element given document and decodedFragment.
  1487. potential_indicated_element = find_a_potential_indicated_element(MUST(FlyString::from_deprecated_fly_string(decoded_fragment)));
  1488. // 8. If potentialIndicatedElement is not null, then return potentialIndicatedElement.
  1489. if (potential_indicated_element)
  1490. return potential_indicated_element;
  1491. // 9. If decodedFragment is an ASCII case-insensitive match for the string top, then return the top of the document.
  1492. if (Infra::is_ascii_case_insensitive_match(decoded_fragment, "top"sv))
  1493. return Document::TopOfTheDocument {};
  1494. // 10. Return null.
  1495. return nullptr;
  1496. }
  1497. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#find-a-potential-indicated-element
  1498. Element* Document::find_a_potential_indicated_element(FlyString const& fragment) const
  1499. {
  1500. // To find a potential indicated element given a Document document and a string fragment, run these steps:
  1501. // 1. If there is an element in the document tree whose root is document and that has an ID equal to
  1502. // fragment, then return the first such element in tree order.
  1503. if (auto element = get_element_by_id(fragment))
  1504. return const_cast<Element*>(element.ptr());
  1505. // 2. If there is an a element in the document tree whose root is document that has a name attribute
  1506. // whose value is equal to fragment, then return the first such element in tree order.
  1507. Element* element_with_name = nullptr;
  1508. root().for_each_in_subtree_of_type<Element>([&](Element const& element) {
  1509. if (element.name() == fragment) {
  1510. element_with_name = const_cast<Element*>(&element);
  1511. return IterationDecision::Break;
  1512. }
  1513. return IterationDecision::Continue;
  1514. });
  1515. if (element_with_name)
  1516. return element_with_name;
  1517. // 3. Return null.
  1518. return nullptr;
  1519. }
  1520. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#scroll-to-the-fragment-identifier
  1521. void Document::scroll_to_the_fragment()
  1522. {
  1523. // To scroll to the fragment given a Document document:
  1524. // 1. If document's indicated part is null, then set document's target element to null.
  1525. auto indicated_part = determine_the_indicated_part();
  1526. if (indicated_part.has<Element*>() && indicated_part.get<Element*>() == nullptr) {
  1527. set_target_element(nullptr);
  1528. }
  1529. // 2. Otherwise, if document's indicated part is top of the document, then:
  1530. else if (indicated_part.has<TopOfTheDocument>()) {
  1531. // 1. Set document's target element to null.
  1532. set_target_element(nullptr);
  1533. // 2. Scroll to the beginning of the document for document. [CSSOMVIEW]
  1534. scroll_to_the_beginning_of_the_document();
  1535. // 3. Return.
  1536. return;
  1537. }
  1538. // 3. Otherwise:
  1539. else {
  1540. // 1. Assert: document's indicated part is an element.
  1541. VERIFY(indicated_part.has<Element*>());
  1542. // 2. Let target be document's indicated part.
  1543. auto target = indicated_part.get<Element*>();
  1544. // 3. Set document's target element to target.
  1545. set_target_element(target);
  1546. // FIXME: 4. Run the ancestor details revealing algorithm on target.
  1547. // FIXME: 5. Run the ancestor hidden-until-found revealing algorithm on target.
  1548. // 6. Scroll target into view, with behavior set to "auto", block set to "start", and inline set to "nearest". [CSSOMVIEW]
  1549. ScrollIntoViewOptions scroll_options;
  1550. scroll_options.block = Bindings::ScrollLogicalPosition::Start;
  1551. scroll_options.inline_ = Bindings::ScrollLogicalPosition::Nearest;
  1552. (void)target->scroll_into_view(scroll_options);
  1553. // 7. Run the focusing steps for target, with the Document's viewport as the fallback target.
  1554. // FIXME: Pass the Document's viewport somehow.
  1555. HTML::run_focusing_steps(target);
  1556. // FIXME: 8. Move the sequential focus navigation starting point to target.
  1557. }
  1558. }
  1559. // https://drafts.csswg.org/cssom-view-1/#scroll-to-the-beginning-of-the-document
  1560. void Document::scroll_to_the_beginning_of_the_document()
  1561. {
  1562. // FIXME: Actually implement this algorithm
  1563. if (auto navigable = this->navigable())
  1564. navigable->perform_scroll_of_viewport({ 0, 0 });
  1565. }
  1566. StringView Document::ready_state() const
  1567. {
  1568. switch (m_readiness) {
  1569. case HTML::DocumentReadyState::Loading:
  1570. return "loading"sv;
  1571. case HTML::DocumentReadyState::Interactive:
  1572. return "interactive"sv;
  1573. case HTML::DocumentReadyState::Complete:
  1574. return "complete"sv;
  1575. }
  1576. VERIFY_NOT_REACHED();
  1577. }
  1578. // https://html.spec.whatwg.org/multipage/dom.html#update-the-current-document-readiness
  1579. void Document::update_readiness(HTML::DocumentReadyState readiness_value)
  1580. {
  1581. // 1. If document's current document readiness equals readinessValue, then return.
  1582. if (m_readiness == readiness_value)
  1583. return;
  1584. // 2. Set document's current document readiness to readinessValue.
  1585. m_readiness = readiness_value;
  1586. // 3. If document is associated with an HTML parser, then:
  1587. if (m_parser) {
  1588. // 1. Let now be the current high resolution time given document's relevant global object.
  1589. auto now = HighResolutionTime::unsafe_shared_current_time();
  1590. // 2. If readinessValue is "complete", and document's load timing info's DOM complete time is 0,
  1591. // then set document's load timing info's DOM complete time to now.
  1592. if (readiness_value == HTML::DocumentReadyState::Complete && m_load_timing_info.dom_complete_time == 0) {
  1593. m_load_timing_info.dom_complete_time = now;
  1594. }
  1595. // 3. Otherwise, if readinessValue is "interactive", and document's load timing info's DOM interactive time is 0,
  1596. // then set document's load timing info's DOM interactive time to now.
  1597. else if (readiness_value == HTML::DocumentReadyState::Interactive && m_load_timing_info.dom_interactive_time == 0) {
  1598. m_load_timing_info.dom_interactive_time = now;
  1599. }
  1600. }
  1601. // 4. Fire an event named readystatechange at document.
  1602. dispatch_event(Event::create(realm(), HTML::EventNames::readystatechange));
  1603. if (readiness_value == HTML::DocumentReadyState::Complete) {
  1604. if (navigable() && navigable()->is_traversable()) {
  1605. HTML::HTMLLinkElement::load_fallback_favicon_if_needed(*this).release_value_but_fixme_should_propagate_errors();
  1606. navigable()->traversable_navigable()->page().client().page_did_finish_loading(url());
  1607. } else {
  1608. m_needs_to_call_page_did_load = true;
  1609. }
  1610. }
  1611. }
  1612. Page& Document::page()
  1613. {
  1614. return m_page;
  1615. }
  1616. Page const& Document::page() const
  1617. {
  1618. return m_page;
  1619. }
  1620. EventTarget* Document::get_parent(Event const& event)
  1621. {
  1622. if (event.type() == HTML::EventNames::load)
  1623. return nullptr;
  1624. return m_window;
  1625. }
  1626. // https://html.spec.whatwg.org/#completely-loaded
  1627. bool Document::is_completely_loaded() const
  1628. {
  1629. return m_completely_loaded_time.has_value();
  1630. }
  1631. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#completely-finish-loading
  1632. void Document::completely_finish_loading()
  1633. {
  1634. if (!navigable())
  1635. return;
  1636. ScopeGuard notify_observers = [this] {
  1637. auto observers_to_notify = m_document_observers.values();
  1638. for (auto& document_observer : observers_to_notify) {
  1639. if (document_observer->document_completely_loaded())
  1640. document_observer->document_completely_loaded()->function()();
  1641. }
  1642. };
  1643. // 1. Assert: document's browsing context is non-null.
  1644. VERIFY(browsing_context());
  1645. // 2. Set document's completely loaded time to the current time.
  1646. m_completely_loaded_time = AK::UnixDateTime::now();
  1647. // NOTE: See the end of shared_declarative_refresh_steps.
  1648. if (m_active_refresh_timer)
  1649. m_active_refresh_timer->start();
  1650. // 3. Let container be document's browsing context's container.
  1651. if (!navigable()->container())
  1652. return;
  1653. auto container = JS::make_handle(navigable()->container());
  1654. // 4. 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.
  1655. if (container && is<HTML::HTMLIFrameElement>(*container)) {
  1656. container->queue_an_element_task(HTML::Task::Source::DOMManipulation, [container] {
  1657. run_iframe_load_event_steps(static_cast<HTML::HTMLIFrameElement&>(*container));
  1658. });
  1659. }
  1660. // 5. 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.
  1661. else if (container) {
  1662. container->queue_an_element_task(HTML::Task::Source::DOMManipulation, [container] {
  1663. container->dispatch_event(DOM::Event::create(container->realm(), HTML::EventNames::load));
  1664. });
  1665. }
  1666. }
  1667. String Document::cookie(Cookie::Source source)
  1668. {
  1669. return page().client().page_did_request_cookie(m_url, source);
  1670. }
  1671. void Document::set_cookie(StringView cookie_string, Cookie::Source source)
  1672. {
  1673. auto cookie = Cookie::parse_cookie(cookie_string);
  1674. if (!cookie.has_value())
  1675. return;
  1676. page().client().page_did_set_cookie(m_url, cookie.value(), source);
  1677. }
  1678. String Document::dump_dom_tree_as_json() const
  1679. {
  1680. StringBuilder builder;
  1681. auto json = MUST(JsonObjectSerializer<>::try_create(builder));
  1682. serialize_tree_as_json(json);
  1683. MUST(json.finish());
  1684. return MUST(builder.to_string());
  1685. }
  1686. // https://html.spec.whatwg.org/multipage/semantics.html#has-a-style-sheet-that-is-blocking-scripts
  1687. bool Document::has_a_style_sheet_that_is_blocking_scripts() const
  1688. {
  1689. // FIXME: 1. If document's script-blocking style sheet set is not empty, then return true.
  1690. if (m_script_blocking_style_sheet_counter > 0)
  1691. return true;
  1692. // 2. If document's node navigable is null, then return false.
  1693. if (!navigable())
  1694. return false;
  1695. // 3. Let containerDocument be document's node navigable's container document.
  1696. auto container_document = navigable()->container_document();
  1697. // FIXME: 4. If containerDocument is non-null and containerDocument's script-blocking style sheet set is not empty, then return true.
  1698. if (container_document && container_document->m_script_blocking_style_sheet_counter > 0)
  1699. return true;
  1700. // 5. Return false
  1701. return false;
  1702. }
  1703. String Document::referrer() const
  1704. {
  1705. return m_referrer;
  1706. }
  1707. void Document::set_referrer(String referrer)
  1708. {
  1709. m_referrer = move(referrer);
  1710. }
  1711. // https://html.spec.whatwg.org/multipage/document-sequences.html#fully-active
  1712. bool Document::is_fully_active() const
  1713. {
  1714. // A Document d is said to be fully active when d is the active document of a navigable navigable, and either
  1715. // navigable is a top-level traversable or navigable's container document is fully active.
  1716. auto navigable = this->navigable();
  1717. if (!navigable)
  1718. return false;
  1719. auto traversable = navigable->traversable_navigable();
  1720. if (navigable == traversable && traversable->is_top_level_traversable())
  1721. return true;
  1722. auto container_document = navigable->container_document();
  1723. if (container_document && container_document->is_fully_active())
  1724. return true;
  1725. return false;
  1726. }
  1727. bool Document::is_active() const
  1728. {
  1729. return navigable() && navigable()->active_document() == this;
  1730. }
  1731. // https://html.spec.whatwg.org/multipage/history.html#dom-document-location
  1732. JS::GCPtr<HTML::Location> Document::location()
  1733. {
  1734. // The Document object's location attribute's getter must return this Document object's relevant global object's Location object,
  1735. // if this Document object is fully active, and null otherwise.
  1736. if (!is_fully_active())
  1737. return nullptr;
  1738. return window().location();
  1739. }
  1740. // https://html.spec.whatwg.org/multipage/interaction.html#dom-document-hidden
  1741. bool Document::hidden() const
  1742. {
  1743. return m_visibility_state == HTML::VisibilityState::Hidden;
  1744. }
  1745. // https://html.spec.whatwg.org/multipage/interaction.html#dom-document-visibilitystate
  1746. StringView Document::visibility_state() const
  1747. {
  1748. switch (m_visibility_state) {
  1749. case HTML::VisibilityState::Hidden:
  1750. return "hidden"sv;
  1751. case HTML::VisibilityState::Visible:
  1752. return "visible"sv;
  1753. }
  1754. VERIFY_NOT_REACHED();
  1755. }
  1756. void Document::set_visibility_state(Badge<HTML::BrowsingContext>, HTML::VisibilityState visibility_state)
  1757. {
  1758. m_visibility_state = visibility_state;
  1759. }
  1760. // https://html.spec.whatwg.org/multipage/interaction.html#update-the-visibility-state
  1761. void Document::update_the_visibility_state(HTML::VisibilityState visibility_state)
  1762. {
  1763. // 1. If document's visibility state equals visibilityState, then return.
  1764. if (m_visibility_state == visibility_state)
  1765. return;
  1766. // 2. Set document's visibility state to visibilityState.
  1767. m_visibility_state = visibility_state;
  1768. // FIXME: 3. Run any page visibility change steps which may be defined in other specifications, with visibility state and document.
  1769. // 4. Fire an event named visibilitychange at document, with its bubbles attribute initialized to true.
  1770. auto event = DOM::Event::create(realm(), HTML::EventNames::visibilitychange);
  1771. event->set_bubbles(true);
  1772. dispatch_event(event);
  1773. }
  1774. // https://drafts.csswg.org/cssom-view/#run-the-resize-steps
  1775. void Document::run_the_resize_steps()
  1776. {
  1777. // 1. If doc’s viewport has had its width or height changed
  1778. // (e.g. as a result of the user resizing the browser window, or changing the page zoom scale factor,
  1779. // or an iframe element’s dimensions are changed) since the last time these steps were run,
  1780. // fire an event named resize at the Window object associated with doc.
  1781. auto viewport_size = viewport_rect().size().to_type<int>();
  1782. if (m_last_viewport_size == viewport_size)
  1783. return;
  1784. m_last_viewport_size = viewport_size;
  1785. window().dispatch_event(DOM::Event::create(realm(), UIEvents::EventNames::resize));
  1786. schedule_layout_update();
  1787. }
  1788. // https://w3c.github.io/csswg-drafts/cssom-view-1/#document-run-the-scroll-steps
  1789. void Document::run_the_scroll_steps()
  1790. {
  1791. // 1. For each item target in doc’s pending scroll event targets, in the order they were added to the list, run these substeps:
  1792. for (auto& target : m_pending_scroll_event_targets) {
  1793. // 1. If target is a Document, fire an event named scroll that bubbles at target and fire an event named scroll at the VisualViewport that is associated with target.
  1794. if (is<Document>(*target)) {
  1795. auto event = DOM::Event::create(realm(), HTML::EventNames::scroll);
  1796. event->set_bubbles(true);
  1797. target->dispatch_event(event);
  1798. // FIXME: Fire at the associated VisualViewport
  1799. }
  1800. // 2. Otherwise, fire an event named scroll at target.
  1801. else {
  1802. auto event = DOM::Event::create(realm(), HTML::EventNames::scroll);
  1803. target->dispatch_event(event);
  1804. }
  1805. }
  1806. // 2. Empty doc’s pending scroll event targets.
  1807. m_pending_scroll_event_targets.clear();
  1808. }
  1809. void Document::add_media_query_list(JS::NonnullGCPtr<CSS::MediaQueryList> media_query_list)
  1810. {
  1811. m_media_query_lists.append(*media_query_list);
  1812. }
  1813. // https://drafts.csswg.org/cssom-view/#evaluate-media-queries-and-report-changes
  1814. void Document::evaluate_media_queries_and_report_changes()
  1815. {
  1816. // NOTE: Not in the spec, but we take this opportunity to prune null WeakPtrs.
  1817. m_media_query_lists.remove_all_matching([](auto& it) {
  1818. return it.is_null();
  1819. });
  1820. // 1. For each MediaQueryList object target that has doc as its document,
  1821. // in the order they were created, oldest first, run these substeps:
  1822. for (auto& media_query_list_ptr : m_media_query_lists) {
  1823. // 1. If target’s matches state has changed since the last time these steps
  1824. // were run, fire an event at target using the MediaQueryListEvent constructor,
  1825. // with its type attribute initialized to change, its isTrusted attribute
  1826. // initialized to true, its media attribute initialized to target’s media,
  1827. // and its matches attribute initialized to target’s matches state.
  1828. if (media_query_list_ptr.is_null())
  1829. continue;
  1830. JS::GCPtr<CSS::MediaQueryList> media_query_list = media_query_list_ptr.ptr();
  1831. bool did_match = media_query_list->matches();
  1832. bool now_matches = media_query_list->evaluate();
  1833. if (did_match != now_matches) {
  1834. CSS::MediaQueryListEventInit init;
  1835. init.media = media_query_list->media();
  1836. init.matches = now_matches;
  1837. auto event = CSS::MediaQueryListEvent::create(realm(), HTML::EventNames::change, init);
  1838. event->set_is_trusted(true);
  1839. media_query_list->dispatch_event(*event);
  1840. }
  1841. }
  1842. // Also not in the spec, but this is as good a place as any to evaluate @media rules!
  1843. evaluate_media_rules();
  1844. }
  1845. void Document::evaluate_media_rules()
  1846. {
  1847. bool any_media_queries_changed_match_state = false;
  1848. for (auto& style_sheet : style_sheets().sheets()) {
  1849. if (style_sheet->evaluate_media_queries(window()))
  1850. any_media_queries_changed_match_state = true;
  1851. }
  1852. if (any_media_queries_changed_match_state) {
  1853. style_computer().invalidate_rule_cache();
  1854. invalidate_style();
  1855. }
  1856. }
  1857. DOMImplementation* Document::implementation()
  1858. {
  1859. if (!m_implementation)
  1860. m_implementation = DOMImplementation::create(*this);
  1861. return m_implementation;
  1862. }
  1863. bool Document::has_focus() const
  1864. {
  1865. // FIXME: Return whether we actually have focus.
  1866. return true;
  1867. }
  1868. void Document::set_parser(Badge<HTML::HTMLParser>, HTML::HTMLParser& parser)
  1869. {
  1870. m_parser = parser;
  1871. }
  1872. void Document::detach_parser(Badge<HTML::HTMLParser>)
  1873. {
  1874. m_parser = nullptr;
  1875. }
  1876. // https://www.w3.org/TR/xml/#NT-NameStartChar
  1877. static bool is_valid_name_start_character(u32 code_point)
  1878. {
  1879. return code_point == ':'
  1880. || (code_point >= 'A' && code_point <= 'Z')
  1881. || code_point == '_'
  1882. || (code_point >= 'a' && code_point <= 'z')
  1883. || (code_point >= 0xc0 && code_point <= 0xd6)
  1884. || (code_point >= 0xd8 && code_point <= 0xf6)
  1885. || (code_point >= 0xf8 && code_point <= 0x2ff)
  1886. || (code_point >= 0x370 && code_point <= 0x37d)
  1887. || (code_point >= 0x37f && code_point <= 0x1fff)
  1888. || (code_point >= 0x200c && code_point <= 0x200d)
  1889. || (code_point >= 0x2070 && code_point <= 0x218f)
  1890. || (code_point >= 0x2c00 && code_point <= 0x2fef)
  1891. || (code_point >= 0x3001 && code_point <= 0xD7ff)
  1892. || (code_point >= 0xf900 && code_point <= 0xfdcf)
  1893. || (code_point >= 0xfdf0 && code_point <= 0xfffd)
  1894. || (code_point >= 0x10000 && code_point <= 0xeffff);
  1895. }
  1896. // https://www.w3.org/TR/xml/#NT-NameChar
  1897. static inline bool is_valid_name_character(u32 code_point)
  1898. {
  1899. return is_valid_name_start_character(code_point)
  1900. || code_point == '-'
  1901. || code_point == '.'
  1902. || (code_point >= '0' && code_point <= '9')
  1903. || code_point == 0xb7
  1904. || (code_point >= 0x300 && code_point <= 0x36f)
  1905. || (code_point >= 0x203f && code_point <= 0x2040);
  1906. }
  1907. bool Document::is_valid_name(String const& name)
  1908. {
  1909. auto code_points = Utf8View { name };
  1910. auto it = code_points.begin();
  1911. if (code_points.is_empty())
  1912. return false;
  1913. if (!is_valid_name_start_character(*it))
  1914. return false;
  1915. ++it;
  1916. for (; it != code_points.end(); ++it) {
  1917. if (!is_valid_name_character(*it))
  1918. return false;
  1919. }
  1920. return true;
  1921. }
  1922. // https://dom.spec.whatwg.org/#validate
  1923. WebIDL::ExceptionOr<Document::PrefixAndTagName> Document::validate_qualified_name(JS::Realm& realm, FlyString const& qualified_name)
  1924. {
  1925. if (qualified_name.is_empty())
  1926. return WebIDL::InvalidCharacterError::create(realm, "Empty string is not a valid qualified name."_fly_string);
  1927. auto utf8view = qualified_name.code_points();
  1928. Optional<size_t> colon_offset;
  1929. bool at_start_of_name = true;
  1930. for (auto it = utf8view.begin(); it != utf8view.end(); ++it) {
  1931. auto code_point = *it;
  1932. if (code_point == ':') {
  1933. if (colon_offset.has_value())
  1934. return WebIDL::InvalidCharacterError::create(realm, "More than one colon (:) in qualified name."_fly_string);
  1935. colon_offset = utf8view.byte_offset_of(it);
  1936. at_start_of_name = true;
  1937. continue;
  1938. }
  1939. if (at_start_of_name) {
  1940. if (!is_valid_name_start_character(code_point))
  1941. return WebIDL::InvalidCharacterError::create(realm, "Invalid start of qualified name."_fly_string);
  1942. at_start_of_name = false;
  1943. continue;
  1944. }
  1945. if (!is_valid_name_character(code_point))
  1946. return WebIDL::InvalidCharacterError::create(realm, "Invalid character in qualified name."_fly_string);
  1947. }
  1948. if (!colon_offset.has_value())
  1949. return Document::PrefixAndTagName {
  1950. .prefix = {},
  1951. .tag_name = qualified_name,
  1952. };
  1953. if (*colon_offset == 0)
  1954. return WebIDL::InvalidCharacterError::create(realm, "Qualified name can't start with colon (:)."_fly_string);
  1955. if (*colon_offset >= (qualified_name.bytes_as_string_view().length() - 1))
  1956. return WebIDL::InvalidCharacterError::create(realm, "Qualified name can't end with colon (:)."_fly_string);
  1957. return Document::PrefixAndTagName {
  1958. .prefix = MUST(FlyString::from_utf8(qualified_name.bytes_as_string_view().substring_view(0, *colon_offset))),
  1959. .tag_name = MUST(FlyString::from_utf8(qualified_name.bytes_as_string_view().substring_view(*colon_offset + 1))),
  1960. };
  1961. }
  1962. // https://dom.spec.whatwg.org/#dom-document-createnodeiterator
  1963. JS::NonnullGCPtr<NodeIterator> Document::create_node_iterator(Node& root, unsigned what_to_show, JS::GCPtr<NodeFilter> filter)
  1964. {
  1965. return NodeIterator::create(root, what_to_show, filter).release_value_but_fixme_should_propagate_errors();
  1966. }
  1967. // https://dom.spec.whatwg.org/#dom-document-createtreewalker
  1968. JS::NonnullGCPtr<TreeWalker> Document::create_tree_walker(Node& root, unsigned what_to_show, JS::GCPtr<NodeFilter> filter)
  1969. {
  1970. return TreeWalker::create(root, what_to_show, filter);
  1971. }
  1972. void Document::register_node_iterator(Badge<NodeIterator>, NodeIterator& node_iterator)
  1973. {
  1974. auto result = m_node_iterators.set(&node_iterator);
  1975. VERIFY(result == AK::HashSetResult::InsertedNewEntry);
  1976. }
  1977. void Document::unregister_node_iterator(Badge<NodeIterator>, NodeIterator& node_iterator)
  1978. {
  1979. bool was_removed = m_node_iterators.remove(&node_iterator);
  1980. VERIFY(was_removed);
  1981. }
  1982. void Document::register_document_observer(Badge<DocumentObserver>, DocumentObserver& document_observer)
  1983. {
  1984. auto result = m_document_observers.set(document_observer);
  1985. VERIFY(result == AK::HashSetResult::InsertedNewEntry);
  1986. }
  1987. void Document::unregister_document_observer(Badge<DocumentObserver>, DocumentObserver& document_observer)
  1988. {
  1989. bool was_removed = m_document_observers.remove(document_observer);
  1990. VERIFY(was_removed);
  1991. }
  1992. void Document::increment_number_of_things_delaying_the_load_event(Badge<DocumentLoadEventDelayer>)
  1993. {
  1994. ++m_number_of_things_delaying_the_load_event;
  1995. page().client().page_did_update_resource_count(m_number_of_things_delaying_the_load_event);
  1996. }
  1997. void Document::decrement_number_of_things_delaying_the_load_event(Badge<DocumentLoadEventDelayer>)
  1998. {
  1999. VERIFY(m_number_of_things_delaying_the_load_event);
  2000. --m_number_of_things_delaying_the_load_event;
  2001. page().client().page_did_update_resource_count(m_number_of_things_delaying_the_load_event);
  2002. }
  2003. bool Document::anything_is_delaying_the_load_event() const
  2004. {
  2005. if (m_number_of_things_delaying_the_load_event > 0)
  2006. return true;
  2007. for (auto& navigable : descendant_navigables()) {
  2008. if (navigable->container()->currently_delays_the_load_event())
  2009. return true;
  2010. }
  2011. // FIXME: Track down anything else that is supposed to delay the load event.
  2012. return false;
  2013. }
  2014. void Document::invalidate_stacking_context_tree()
  2015. {
  2016. if (auto* paintable_box = this->paintable_box())
  2017. paintable_box->invalidate_stacking_context();
  2018. }
  2019. void Document::check_favicon_after_loading_link_resource()
  2020. {
  2021. // https://html.spec.whatwg.org/multipage/links.html#rel-icon
  2022. // NOTE: firefox also load favicons outside the head tag, which is against spec (see table 4.6.7)
  2023. auto* head_element = head();
  2024. if (!head_element)
  2025. return;
  2026. auto favicon_link_elements = HTMLCollection::create(*head_element, HTMLCollection::Scope::Descendants, [](Element const& element) {
  2027. if (!is<HTML::HTMLLinkElement>(element))
  2028. return false;
  2029. return static_cast<HTML::HTMLLinkElement const&>(element).has_loaded_icon();
  2030. });
  2031. if (favicon_link_elements->length() == 0) {
  2032. dbgln_if(SPAM_DEBUG, "No favicon found to be used");
  2033. return;
  2034. }
  2035. // 4.6.7.8 Link type "icon"
  2036. //
  2037. // If there are multiple equally appropriate icons, user agents must use the last one declared
  2038. // in tree order at the time that the user agent collected the list of icons.
  2039. //
  2040. // If multiple icons are provided, the user agent must select the most appropriate icon
  2041. // according to the type, media, and sizes attributes.
  2042. //
  2043. // FIXME: There is no selective behavior yet for favicons.
  2044. for (auto i = favicon_link_elements->length(); i-- > 0;) {
  2045. auto favicon_element = favicon_link_elements->item(i);
  2046. if (favicon_element == m_active_element.ptr())
  2047. return;
  2048. // If the user agent tries to use an icon but that icon is determined, upon closer examination,
  2049. // to in fact be inappropriate (...), then the user agent must try the next-most-appropriate icon
  2050. // as determined by the attributes.
  2051. if (static_cast<HTML::HTMLLinkElement*>(favicon_element)->load_favicon_and_use_if_window_is_active()) {
  2052. m_active_favicon = favicon_element;
  2053. return;
  2054. }
  2055. }
  2056. dbgln_if(SPAM_DEBUG, "No favicon found to be used");
  2057. }
  2058. void Document::set_window(HTML::Window& window)
  2059. {
  2060. m_window = &window;
  2061. }
  2062. // https://html.spec.whatwg.org/multipage/custom-elements.html#look-up-a-custom-element-definition
  2063. JS::GCPtr<HTML::CustomElementDefinition> Document::lookup_custom_element_definition(Optional<FlyString> const& namespace_, FlyString const& local_name, Optional<String> const& is) const
  2064. {
  2065. // 1. If namespace is not the HTML namespace, return null.
  2066. if (namespace_ != Namespace::HTML)
  2067. return nullptr;
  2068. // 2. If document's browsing context is null, return null.
  2069. if (!browsing_context())
  2070. return nullptr;
  2071. // 3. Let registry be document's relevant global object's CustomElementRegistry object.
  2072. auto registry = verify_cast<HTML::Window>(relevant_global_object(*this)).custom_elements();
  2073. // 4. If there is custom element definition in registry with name and local name both equal to localName, return that custom element definition.
  2074. auto converted_local_name = local_name;
  2075. auto maybe_definition = registry->get_definition_with_name_and_local_name(converted_local_name.to_string(), converted_local_name.to_string());
  2076. if (maybe_definition)
  2077. return maybe_definition;
  2078. // 5. If there is a custom element definition in registry with name equal to is and local name equal to localName, return that custom element definition.
  2079. // 6. Return null.
  2080. // NOTE: If `is` has no value, it can never match as custom element definitions always have a name and localName (i.e. not stored as Optional<String>)
  2081. if (!is.has_value())
  2082. return nullptr;
  2083. return registry->get_definition_with_name_and_local_name(is.value(), converted_local_name.to_string());
  2084. }
  2085. CSS::StyleSheetList& Document::style_sheets()
  2086. {
  2087. if (!m_style_sheets)
  2088. m_style_sheets = CSS::StyleSheetList::create(*this);
  2089. return *m_style_sheets;
  2090. }
  2091. CSS::StyleSheetList const& Document::style_sheets() const
  2092. {
  2093. return const_cast<Document*>(this)->style_sheets();
  2094. }
  2095. JS::NonnullGCPtr<HTML::History> Document::history()
  2096. {
  2097. if (!m_history)
  2098. m_history = HTML::History::create(realm(), *this);
  2099. return *m_history;
  2100. }
  2101. JS::NonnullGCPtr<HTML::History> Document::history() const
  2102. {
  2103. return const_cast<Document*>(this)->history();
  2104. }
  2105. // https://html.spec.whatwg.org/multipage/origin.html#dom-document-domain
  2106. String Document::domain() const
  2107. {
  2108. // 1. Let effectiveDomain be this's origin's effective domain.
  2109. auto effective_domain = origin().effective_domain();
  2110. // 2. If effectiveDomain is null, then return the empty string.
  2111. if (!effective_domain.has_value())
  2112. return String {};
  2113. // 3. Return effectiveDomain, serialized.
  2114. return MUST(URLParser::serialize_host(effective_domain.release_value()));
  2115. }
  2116. void Document::set_domain(String const& domain)
  2117. {
  2118. dbgln("(STUBBED) Document::set_domain(domain='{}')", domain);
  2119. }
  2120. void Document::set_navigation_id(Optional<String> navigation_id)
  2121. {
  2122. m_navigation_id = move(navigation_id);
  2123. }
  2124. Optional<String> Document::navigation_id() const
  2125. {
  2126. return m_navigation_id;
  2127. }
  2128. HTML::SandboxingFlagSet Document::active_sandboxing_flag_set() const
  2129. {
  2130. return m_active_sandboxing_flag_set;
  2131. }
  2132. HTML::PolicyContainer Document::policy_container() const
  2133. {
  2134. return m_policy_container;
  2135. }
  2136. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#snapshotting-source-snapshot-params
  2137. HTML::SourceSnapshotParams Document::snapshot_source_snapshot_params() const
  2138. {
  2139. // To snapshot source snapshot params given a Document sourceDocument, return a new source snapshot params with
  2140. // has transient activation
  2141. // true if sourceDocument's relevant global object has transient activation; otherwise false
  2142. // sandboxing flags
  2143. // sourceDocument's active sandboxing flag set
  2144. // allows downloading
  2145. // false if sourceDocument's active sandboxing flag set has the sandboxed downloads browsing context flag set; otherwise true
  2146. // fetch client
  2147. // sourceDocument's relevant settings object
  2148. // source policy container
  2149. // sourceDocument's policy container
  2150. return HTML::SourceSnapshotParams {
  2151. .has_transient_activation = verify_cast<HTML::Window>(HTML::relevant_global_object(*this)).has_transient_activation(),
  2152. .sandboxing_flags = m_active_sandboxing_flag_set,
  2153. .allows_downloading = !has_flag(m_active_sandboxing_flag_set, HTML::SandboxingFlagSet::SandboxedDownloads),
  2154. .fetch_client = relevant_settings_object(),
  2155. .source_policy_container = m_policy_container
  2156. };
  2157. }
  2158. // https://html.spec.whatwg.org/multipage/document-sequences.html#descendant-navigables
  2159. Vector<JS::Handle<HTML::Navigable>> Document::descendant_navigables()
  2160. {
  2161. // 1. Let navigables be new list.
  2162. Vector<JS::Handle<HTML::Navigable>> navigables;
  2163. // 2. Let navigableContainers be a list of all shadow-including descendants of document that are navigable containers, in shadow-including tree order.
  2164. // 3. For each navigableContainer of navigableContainers:
  2165. for_each_shadow_including_descendant([&](DOM::Node& node) {
  2166. if (is<HTML::NavigableContainer>(node)) {
  2167. auto& navigable_container = static_cast<HTML::NavigableContainer&>(node);
  2168. // 1. If navigableContainer's content navigable is null, then continue.
  2169. if (!navigable_container.content_navigable())
  2170. return IterationDecision::Continue;
  2171. // 2. Extend navigables with navigableContainer's content navigable's active document's inclusive descendant navigables.
  2172. navigables.extend(navigable_container.content_navigable()->active_document()->inclusive_descendant_navigables());
  2173. }
  2174. return IterationDecision::Continue;
  2175. });
  2176. // 4. Return navigables.
  2177. return navigables;
  2178. }
  2179. Vector<JS::Handle<HTML::Navigable>> const Document::descendant_navigables() const
  2180. {
  2181. return const_cast<Document&>(*this).descendant_navigables();
  2182. }
  2183. // https://html.spec.whatwg.org/multipage/document-sequences.html#inclusive-descendant-navigables
  2184. Vector<JS::Handle<HTML::Navigable>> Document::inclusive_descendant_navigables()
  2185. {
  2186. // 1. Let navigables be « document's node navigable ».
  2187. Vector<JS::Handle<HTML::Navigable>> navigables;
  2188. navigables.append(*navigable());
  2189. // 2. Extend navigables with document's descendant navigables.
  2190. navigables.extend(descendant_navigables());
  2191. // 3. Return navigables.
  2192. return navigables;
  2193. }
  2194. // https://html.spec.whatwg.org/multipage/document-sequences.html#ancestor-navigables
  2195. Vector<JS::Handle<HTML::Navigable>> Document::ancestor_navigables()
  2196. {
  2197. // 1. Let navigable be document's node navigable's parent.
  2198. VERIFY(navigable());
  2199. auto navigable = this->navigable()->parent();
  2200. // 2. Let ancestors be an empty list.
  2201. Vector<JS::Handle<HTML::Navigable>> ancestors;
  2202. // 3. While navigable is not null:
  2203. while (navigable) {
  2204. // 1. Prepend navigable to ancestors.
  2205. ancestors.prepend(*navigable);
  2206. // 2. Set navigable to navigable's parent.
  2207. navigable = navigable->parent();
  2208. }
  2209. // 4. Return ancestors.
  2210. return ancestors;
  2211. }
  2212. // https://html.spec.whatwg.org/multipage/document-sequences.html#inclusive-ancestor-navigables
  2213. Vector<JS::Handle<HTML::Navigable>> Document::inclusive_ancestor_navigables()
  2214. {
  2215. // 1. Let navigables be document's ancestor navigables.
  2216. auto navigables = ancestor_navigables();
  2217. // 2. Append document's node navigable to navigables.
  2218. navigables.append(*navigable());
  2219. // 3. Return navigables.
  2220. return navigables;
  2221. }
  2222. // https://html.spec.whatwg.org/multipage/document-sequences.html#document-tree-child-navigables
  2223. Vector<JS::Handle<HTML::Navigable>> Document::document_tree_child_navigables()
  2224. {
  2225. // 1. If document's node navigable is null, then return the empty list.
  2226. if (!navigable())
  2227. return {};
  2228. // 2. Let navigables be new list.
  2229. Vector<JS::Handle<HTML::Navigable>> navigables;
  2230. // 3. Let navigableContainers be a list of all descendants of document that are navigable containers, in tree order.
  2231. // 4. For each navigableContainer of navigableContainers:
  2232. for_each_in_subtree_of_type<HTML::NavigableContainer>([&](HTML::NavigableContainer& navigable_container) {
  2233. // 1. If navigableContainer's content navigable is null, then continue.
  2234. if (!navigable_container.content_navigable())
  2235. return IterationDecision::Continue;
  2236. // 2. Append navigableContainer's content navigable to navigables.
  2237. navigables.append(*navigable_container.content_navigable());
  2238. return IterationDecision::Continue;
  2239. });
  2240. // 5. Return navigables.
  2241. return navigables;
  2242. }
  2243. // https://html.spec.whatwg.org/multipage/document-lifecycle.html#unloading-document-cleanup-steps
  2244. void Document::run_unloading_cleanup_steps()
  2245. {
  2246. // 1. Let window be document's relevant global object.
  2247. auto* window = dynamic_cast<HTML::WindowOrWorkerGlobalScopeMixin*>(&HTML::relevant_global_object(*this));
  2248. VERIFY(window);
  2249. // FIXME: 2. For each WebSocket object webSocket whose relevant global object is window, make disappear webSocket.
  2250. // If this affected any WebSocket objects, then set document's salvageable state to false.
  2251. // FIXME: 3. For each WebTransport object transport whose relevant global object is window, run the context cleanup steps given transport.
  2252. // 4. If document's salvageable state is false, then:
  2253. if (m_salvageable) {
  2254. // FIXME: 1. For each EventSource object eventSource whose relevant global object is equal to window, forcibly close eventSource.
  2255. // 2. Clear window's map of active timers.
  2256. window->clear_map_of_active_timers();
  2257. }
  2258. }
  2259. // https://html.spec.whatwg.org/multipage/document-lifecycle.html#destroy-a-document
  2260. void Document::destroy()
  2261. {
  2262. page().client().page_did_destroy_document(*this);
  2263. // NOTE: Abort needs to happen before destory. There is currently bug in the spec: https://github.com/whatwg/html/issues/9148
  2264. // 4. Abort document.
  2265. abort();
  2266. // 2. Set document's salvageable state to false.
  2267. m_salvageable = false;
  2268. // 3. Run any unloading document cleanup steps for document that are defined by this specification and other applicable specifications.
  2269. run_unloading_cleanup_steps();
  2270. // 5. Remove any tasks whose document is document from any task queue (without running those tasks).
  2271. HTML::main_thread_event_loop().task_queue().remove_tasks_matching([this](auto& task) {
  2272. return task.document() == this;
  2273. });
  2274. // 6. Set document's browsing context to null.
  2275. m_browsing_context = nullptr;
  2276. // When a frame element stops being an active frame element, the user agent must destroy a child navigable given the element.
  2277. // A frame element is said to be an active frame element when it is in a document tree and its node document's browsing context is non-null.
  2278. for (auto& navigable_container : HTML::NavigableContainer::all_instances()) {
  2279. if (&navigable_container->document() == this) {
  2280. navigable_container->destroy_the_child_navigable();
  2281. }
  2282. }
  2283. // 7. Set document's node navigable's active session history entry's document state's document to null.
  2284. if (navigable()) {
  2285. navigable()->active_session_history_entry()->document_state->set_document(nullptr);
  2286. }
  2287. // FIXME: 8. Remove document from the owner set of each WorkerGlobalScope object whose set contains document.
  2288. // FIXME: 9. For each workletGlobalScope in document's worklet global scopes, terminate workletGlobalScope.
  2289. }
  2290. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#abort-a-document
  2291. void Document::abort()
  2292. {
  2293. // 1. Abort the active documents of each of document's descendant navigables.
  2294. // If this results in any of those Document objects having their salvageable state set to false,
  2295. // then set document's salvageable state to false also.
  2296. for (auto navigable : descendant_navigables()) {
  2297. if (auto document = navigable->active_document()) {
  2298. // NOTE: This is not in the spec but we need to abort ongoing navigations in all descendandt navigables.
  2299. // See https://github.com/whatwg/html/issues/9711
  2300. navigable->set_ongoing_navigation({});
  2301. document->abort();
  2302. if (!document->m_salvageable)
  2303. m_salvageable = false;
  2304. }
  2305. }
  2306. // FIXME: 2. Cancel any instances of the fetch algorithm in the context of document,
  2307. // discarding any tasks queued for them, and discarding any further data received from the network for them.
  2308. // If this resulted in any instances of the fetch algorithm being canceled
  2309. // or any queued tasks or any network data getting discarded,
  2310. // then set document's salvageable state to false.
  2311. // 3. If document's navigation id is non-null, then:
  2312. if (m_navigation_id.has_value()) {
  2313. // 1. FIXME: Invoke WebDriver BiDi navigation aborted with document's browsing context,
  2314. // and new WebDriver BiDi navigation status whose whose id is document's navigation id,
  2315. // status is "canceled", and url is document's URL.
  2316. // 2. Set document's navigation id to null.
  2317. m_navigation_id = {};
  2318. }
  2319. // 4. If document has an active parser, then:
  2320. if (auto parser = active_parser()) {
  2321. // 1. Set document's active parser was aborted to true.
  2322. m_active_parser_was_aborted = true;
  2323. // 2. Abort that parser.
  2324. parser->abort();
  2325. // 3. Set document's salvageable state to false.
  2326. m_salvageable = false;
  2327. }
  2328. }
  2329. // https://html.spec.whatwg.org/multipage/dom.html#active-parser
  2330. JS::GCPtr<HTML::HTMLParser> Document::active_parser()
  2331. {
  2332. if (!m_parser)
  2333. return nullptr;
  2334. if (m_parser->aborted() || m_parser->stopped())
  2335. return nullptr;
  2336. return m_parser;
  2337. }
  2338. void Document::set_browsing_context(HTML::BrowsingContext* browsing_context)
  2339. {
  2340. m_browsing_context = browsing_context;
  2341. }
  2342. // https://html.spec.whatwg.org/multipage/document-lifecycle.html#unload-a-document
  2343. void Document::unload(JS::GCPtr<Document>)
  2344. {
  2345. auto& vm = this->vm();
  2346. // FIXME: 1. Assert: this is running as part of a task queued on oldDocument's event loop.
  2347. // FIXME: 2. Let unloadTimingInfo be a new document unload timing info.
  2348. // FIXME: 3. If newDocument is not given, then set unloadTimingInfo to null.
  2349. // FIXME: 4. Otherwise, if newDocument's event loop is not oldDocument's event loop, then the user agent may be unloading oldDocument in parallel. In that case, the user agent should
  2350. // set unloadTimingInfo to null.
  2351. // 5. Let intendToStoreInBfcache be true if the user agent intends to keep oldDocument alive in a session history entry, such that it can later be used for history traversal.
  2352. auto intend_to_store_in_bfcache = false;
  2353. // 6. Let eventLoop be oldDocument's relevant agent's event loop.
  2354. auto& event_loop = verify_cast<Bindings::WebEngineCustomData>(*vm.custom_data()).event_loop;
  2355. // 7. Increase eventLoop's termination nesting level by 1.
  2356. event_loop.increment_termination_nesting_level();
  2357. // 8. Increase oldDocument's unload counter by 1.
  2358. m_unload_counter += 1;
  2359. // 9. If intendToKeepInBfcache is false, then set oldDocument's salvageable state to false.
  2360. if (!intend_to_store_in_bfcache) {
  2361. m_salvageable = false;
  2362. }
  2363. // 10. If oldDocument's page showing is true:
  2364. if (m_page_showing) {
  2365. // 1. Set oldDocument's page showing to false.
  2366. m_page_showing = false;
  2367. // 2. Fire a page transition event named pagehide at oldDocument's relevant global object with oldDocument's salvageable state.
  2368. verify_cast<HTML::Window>(relevant_global_object(*this)).fire_a_page_transition_event(HTML::EventNames::pagehide, m_salvageable);
  2369. // 3. Update the visibility state of oldDocument to "hidden".
  2370. update_the_visibility_state(HTML::VisibilityState::Hidden);
  2371. }
  2372. // FIXME: 11. If unloadTimingInfo is not null, then set unloadTimingInfo's unload event start time to the current high resolution time given newDocument's relevant global object, coarsened
  2373. // given oldDocument's relevant settings object's cross-origin isolated capability.
  2374. // 12. If oldDocument's salvageable state is false, then fire an event named unload at oldDocument's relevant global object, with legacy target override flag set.
  2375. if (!m_salvageable) {
  2376. // then fire an event named unload at document's relevant global object, with legacy target override flag set.
  2377. // FIXME: The legacy target override flag is currently set by a virtual override of dispatch_event()
  2378. // We should reorganize this so that the flag appears explicitly here instead.
  2379. auto event = DOM::Event::create(realm(), HTML::EventNames::unload);
  2380. global_object().dispatch_event(event);
  2381. }
  2382. // FIXME: 13. If unloadTimingInfo is not null, then set unloadTimingInfo's unload event end time to the current high resolution time given newDocument's relevant global object, coarsened
  2383. // given oldDocument's relevant settings object's cross-origin isolated capability.
  2384. // 14. Decrease eventLoop's termination nesting level by 1.
  2385. event_loop.decrement_termination_nesting_level();
  2386. // FIXME: 15. Set oldDocument's suspension time to the current high resolution time given document's relevant global object.
  2387. // FIXME: 16. Set oldDocument's suspended timer handles to the result of getting the keys for the map of active timers.
  2388. // FIXME: 17. Set oldDocument's has been scrolled by the user to false.
  2389. // FIXME: 18. Run any unloading document cleanup steps for oldDocument that are defined by this specification and other applicable specifications.
  2390. // 19. If oldDocument's salvageable state is false, then destroy oldDocument.
  2391. if (!m_salvageable) {
  2392. destroy();
  2393. }
  2394. // 20. Decrease oldDocument's unload counter by 1.
  2395. m_unload_counter -= 1;
  2396. // FIXME: 21. If newDocument is given, newDocument's was created via cross-origin redirects is false, and newDocument's origin is the same as oldDocument's origin, then set
  2397. // newDocument's previous document unload timing to unloadTimingInfo.
  2398. did_stop_being_active_document_in_navigable();
  2399. }
  2400. // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#allowed-to-use
  2401. bool Document::is_allowed_to_use_feature(PolicyControlledFeature feature) const
  2402. {
  2403. // 1. If document's browsing context is null, then return false.
  2404. if (browsing_context() == nullptr)
  2405. return false;
  2406. // 2. If document is not fully active, then return false.
  2407. if (!is_fully_active())
  2408. return false;
  2409. // 3. If the result of running is feature enabled in document for origin on feature, document, and document's origin
  2410. // is "Enabled", then return true.
  2411. // FIXME: This is ad-hoc. Implement the Permissions Policy specification.
  2412. switch (feature) {
  2413. case PolicyControlledFeature::Autoplay:
  2414. if (PermissionsPolicy::AutoplayAllowlist::the().is_allowed_for_origin(*this, origin()) == PermissionsPolicy::Decision::Enabled)
  2415. return true;
  2416. break;
  2417. }
  2418. // 4. Return false.
  2419. return false;
  2420. }
  2421. void Document::did_stop_being_active_document_in_navigable()
  2422. {
  2423. tear_down_layout_tree();
  2424. auto observers_to_notify = m_document_observers.values();
  2425. for (auto& document_observer : observers_to_notify) {
  2426. if (document_observer->document_became_inactive())
  2427. document_observer->document_became_inactive()->function()();
  2428. }
  2429. }
  2430. // https://w3c.github.io/editing/docs/execCommand/#querycommandsupported()
  2431. bool Document::query_command_supported(String const& command) const
  2432. {
  2433. dbgln("(STUBBED) Document::query_command_supported(command='{}')", command);
  2434. return false;
  2435. }
  2436. void Document::increment_throw_on_dynamic_markup_insertion_counter(Badge<HTML::HTMLParser>)
  2437. {
  2438. ++m_throw_on_dynamic_markup_insertion_counter;
  2439. }
  2440. void Document::decrement_throw_on_dynamic_markup_insertion_counter(Badge<HTML::HTMLParser>)
  2441. {
  2442. VERIFY(m_throw_on_dynamic_markup_insertion_counter);
  2443. --m_throw_on_dynamic_markup_insertion_counter;
  2444. }
  2445. // https://html.spec.whatwg.org/multipage/scripting.html#appropriate-template-contents-owner-document
  2446. JS::NonnullGCPtr<DOM::Document> Document::appropriate_template_contents_owner_document()
  2447. {
  2448. // 1. If doc is not a Document created by this algorithm, then:
  2449. if (!created_for_appropriate_template_contents()) {
  2450. // 1. If doc does not yet have an associated inert template document, then:
  2451. if (!m_associated_inert_template_document) {
  2452. // 1. Let new doc be a new Document (whose browsing context is null). This is "a Document created by this algorithm" for the purposes of the step above.
  2453. auto new_document = HTML::HTMLDocument::create(realm());
  2454. new_document->m_created_for_appropriate_template_contents = true;
  2455. // 2. If doc is an HTML document, mark new doc as an HTML document also.
  2456. if (document_type() == Type::HTML)
  2457. new_document->set_document_type(Type::HTML);
  2458. // 3. Let doc's associated inert template document be new doc.
  2459. m_associated_inert_template_document = new_document;
  2460. }
  2461. // 2. Set doc to doc's associated inert template document.
  2462. return *m_associated_inert_template_document;
  2463. }
  2464. // 2. Return doc.
  2465. return *this;
  2466. }
  2467. String Document::dump_accessibility_tree_as_json()
  2468. {
  2469. StringBuilder builder;
  2470. auto accessibility_tree = AccessibilityTreeNode::create(this, nullptr);
  2471. build_accessibility_tree(*&accessibility_tree);
  2472. auto json = MUST(JsonObjectSerializer<>::try_create(builder));
  2473. // Empty document
  2474. if (!accessibility_tree->value()) {
  2475. MUST(json.add("type"sv, "element"sv));
  2476. MUST(json.add("role"sv, "document"sv));
  2477. } else {
  2478. accessibility_tree->serialize_tree_as_json(json, *this);
  2479. }
  2480. MUST(json.finish());
  2481. return MUST(builder.to_string());
  2482. }
  2483. // https://dom.spec.whatwg.org/#dom-document-createattribute
  2484. WebIDL::ExceptionOr<JS::NonnullGCPtr<Attr>> Document::create_attribute(String const& local_name)
  2485. {
  2486. // 1. If localName does not match the Name production in XML, then throw an "InvalidCharacterError" DOMException.
  2487. if (!is_valid_name(local_name))
  2488. return WebIDL::InvalidCharacterError::create(realm(), "Invalid character in attribute name."_fly_string);
  2489. // 2. If this is an HTML document, then set localName to localName in ASCII lowercase.
  2490. // 3. Return a new attribute whose local name is localName and node document is this.
  2491. auto deprecated_local_name = local_name.to_byte_string();
  2492. return Attr::create(*this, MUST(FlyString::from_deprecated_fly_string(is_html_document() ? deprecated_local_name.to_lowercase() : deprecated_local_name)));
  2493. }
  2494. // https://dom.spec.whatwg.org/#dom-document-createattributens
  2495. WebIDL::ExceptionOr<JS::NonnullGCPtr<Attr>> Document::create_attribute_ns(Optional<FlyString> const& namespace_, String const& qualified_name)
  2496. {
  2497. // 1. Let namespace, prefix, and localName be the result of passing namespace and qualifiedName to validate and extract.
  2498. auto extracted_qualified_name = TRY(validate_and_extract(realm(), namespace_, qualified_name));
  2499. // 2. Return a new attribute whose namespace is namespace, namespace prefix is prefix, local name is localName, and node document is this.
  2500. return Attr::create(*this, extracted_qualified_name);
  2501. }
  2502. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#make-active
  2503. void Document::make_active()
  2504. {
  2505. // 1. Let window be document's relevant global object.
  2506. auto& window = verify_cast<HTML::Window>(HTML::relevant_global_object(*this));
  2507. set_window(window);
  2508. // 2. Set document's browsing context's WindowProxy's [[Window]] internal slot value to window.
  2509. m_browsing_context->window_proxy()->set_window(window);
  2510. // 3. Set document's visibility state to document's node navigable's traversable navigable's system visibility state.
  2511. if (navigable()) {
  2512. m_visibility_state = navigable()->traversable_navigable()->system_visibility_state();
  2513. }
  2514. // 4. Set window's relevant settings object's execution ready flag.
  2515. HTML::relevant_settings_object(window).execution_ready = true;
  2516. if (m_needs_to_call_page_did_load) {
  2517. navigable()->traversable_navigable()->page().client().page_did_finish_loading(url());
  2518. m_needs_to_call_page_did_load = false;
  2519. }
  2520. }
  2521. HTML::ListOfAvailableImages& Document::list_of_available_images()
  2522. {
  2523. return *m_list_of_available_images;
  2524. }
  2525. HTML::ListOfAvailableImages const& Document::list_of_available_images() const
  2526. {
  2527. return *m_list_of_available_images;
  2528. }
  2529. CSSPixelRect Document::viewport_rect() const
  2530. {
  2531. if (auto const navigable = this->navigable())
  2532. return navigable->viewport_rect();
  2533. return CSSPixelRect {};
  2534. }
  2535. JS::NonnullGCPtr<CSS::VisualViewport> Document::visual_viewport()
  2536. {
  2537. if (!m_visual_viewport)
  2538. m_visual_viewport = CSS::VisualViewport::create(*this);
  2539. return *m_visual_viewport;
  2540. }
  2541. void Document::register_viewport_client(ViewportClient& client)
  2542. {
  2543. auto result = m_viewport_clients.set(&client);
  2544. VERIFY(result == AK::HashSetResult::InsertedNewEntry);
  2545. }
  2546. void Document::unregister_viewport_client(ViewportClient& client)
  2547. {
  2548. bool was_removed = m_viewport_clients.remove(&client);
  2549. VERIFY(was_removed);
  2550. }
  2551. void Document::inform_all_viewport_clients_about_the_current_viewport_rect()
  2552. {
  2553. for (auto* client : m_viewport_clients)
  2554. client->did_set_viewport_rect(viewport_rect());
  2555. }
  2556. void Document::register_intersection_observer(Badge<IntersectionObserver::IntersectionObserver>, IntersectionObserver::IntersectionObserver& observer)
  2557. {
  2558. auto result = m_intersection_observers.set(observer);
  2559. VERIFY(result == AK::HashSetResult::InsertedNewEntry);
  2560. }
  2561. void Document::unregister_intersection_observer(Badge<IntersectionObserver::IntersectionObserver>, IntersectionObserver::IntersectionObserver& observer)
  2562. {
  2563. bool was_removed = m_intersection_observers.remove(observer);
  2564. VERIFY(was_removed);
  2565. }
  2566. // https://www.w3.org/TR/intersection-observer/#queue-an-intersection-observer-task
  2567. void Document::queue_intersection_observer_task()
  2568. {
  2569. // 1. If document’s IntersectionObserverTaskQueued flag is set to true, return.
  2570. if (m_intersection_observer_task_queued)
  2571. return;
  2572. // 2. Set document’s IntersectionObserverTaskQueued flag to true.
  2573. m_intersection_observer_task_queued = true;
  2574. // 3. Queue a task on the IntersectionObserver task source associated with the document's event loop to notify intersection observers.
  2575. HTML::queue_global_task(HTML::Task::Source::IntersectionObserver, window(), [this]() {
  2576. auto& realm = this->realm();
  2577. // https://www.w3.org/TR/intersection-observer/#notify-intersection-observers
  2578. // 1. Set document’s IntersectionObserverTaskQueued flag to false.
  2579. m_intersection_observer_task_queued = false;
  2580. // 2. Let notify list be a list of all IntersectionObservers whose root is in the DOM tree of document.
  2581. Vector<JS::Handle<IntersectionObserver::IntersectionObserver>> notify_list;
  2582. notify_list.try_ensure_capacity(m_intersection_observers.size()).release_value_but_fixme_should_propagate_errors();
  2583. for (auto& observer : m_intersection_observers) {
  2584. notify_list.append(JS::make_handle(observer));
  2585. }
  2586. // 3. For each IntersectionObserver object observer in notify list, run these steps:
  2587. for (auto& observer : notify_list) {
  2588. // 2. Let queue be a copy of observer’s internal [[QueuedEntries]] slot.
  2589. // 3. Clear observer’s internal [[QueuedEntries]] slot.
  2590. auto queue = observer->take_records();
  2591. // 1. If observer’s internal [[QueuedEntries]] slot is empty, continue.
  2592. if (queue.is_empty())
  2593. continue;
  2594. auto wrapped_queue = MUST(JS::Array::create(realm, 0));
  2595. for (size_t i = 0; i < queue.size(); ++i) {
  2596. auto& record = queue.at(i);
  2597. auto property_index = JS::PropertyKey { i };
  2598. MUST(wrapped_queue->create_data_property(property_index, record.ptr()));
  2599. }
  2600. // 4. Let callback be the value of observer’s internal [[callback]] slot.
  2601. auto& callback = observer->callback();
  2602. // 5. Invoke callback with queue as the first argument, observer as the second argument, and observer as the callback this value. If this throws an exception, report the exception.
  2603. auto completion = WebIDL::invoke_callback(callback, observer.ptr(), wrapped_queue, observer.ptr());
  2604. if (completion.is_abrupt())
  2605. HTML::report_exception(completion, realm);
  2606. }
  2607. });
  2608. }
  2609. // https://www.w3.org/TR/intersection-observer/#queue-an-intersectionobserverentry
  2610. void Document::queue_an_intersection_observer_entry(IntersectionObserver::IntersectionObserver& observer, HighResolutionTime::DOMHighResTimeStamp time, JS::NonnullGCPtr<Geometry::DOMRectReadOnly> root_bounds, JS::NonnullGCPtr<Geometry::DOMRectReadOnly> bounding_client_rect, JS::NonnullGCPtr<Geometry::DOMRectReadOnly> intersection_rect, bool is_intersecting, double intersection_ratio, JS::NonnullGCPtr<Element> target)
  2611. {
  2612. auto& realm = this->realm();
  2613. // 1. Construct an IntersectionObserverEntry, passing in time, rootBounds, boundingClientRect, intersectionRect, isIntersecting, and target.
  2614. auto entry = realm.heap().allocate<IntersectionObserver::IntersectionObserverEntry>(realm, realm, time, root_bounds, bounding_client_rect, intersection_rect, is_intersecting, intersection_ratio, target);
  2615. // 2. Append it to observer’s internal [[QueuedEntries]] slot.
  2616. observer.queue_entry({}, entry);
  2617. // 3. Queue an intersection observer task for document.
  2618. queue_intersection_observer_task();
  2619. }
  2620. // https://www.w3.org/TR/intersection-observer/#compute-the-intersection
  2621. static JS::NonnullGCPtr<Geometry::DOMRectReadOnly> compute_intersection(JS::NonnullGCPtr<Element> target, IntersectionObserver::IntersectionObserver const& observer)
  2622. {
  2623. // 1. Let intersectionRect be the result of getting the bounding box for target.
  2624. auto intersection_rect = target->get_bounding_client_rect();
  2625. // FIXME: 2. Let container be the containing block of target.
  2626. // FIXME: 3. While container is not root:
  2627. // FIXME: 1. If container is the document of a nested browsing context, update intersectionRect by clipping to
  2628. // the viewport of the document, and update container to be the browsing context container of container.
  2629. // FIXME: 2. Map intersectionRect to the coordinate space of container.
  2630. // FIXME: 3. If container has a content clip or a css clip-path property, update intersectionRect by applying
  2631. // container’s clip.
  2632. // FIXME: 4. If container is the root element of a browsing context, update container to be the browsing context’s
  2633. // document; otherwise, update container to be the containing block of container.
  2634. // FIXME: 4. Map intersectionRect to the coordinate space of root.
  2635. // 5. Update intersectionRect by intersecting it with the root intersection rectangle.
  2636. // FIXME: Pass in target so we can properly apply rootMargin.
  2637. auto root_intersection_rectangle = observer.root_intersection_rectangle();
  2638. CSSPixelRect intersection_rect_as_pixel_rect(intersection_rect->x(), intersection_rect->y(), intersection_rect->width(), intersection_rect->height());
  2639. intersection_rect_as_pixel_rect.intersect(root_intersection_rectangle);
  2640. intersection_rect->set_x(static_cast<double>(intersection_rect_as_pixel_rect.x()));
  2641. intersection_rect->set_y(static_cast<double>(intersection_rect_as_pixel_rect.y()));
  2642. intersection_rect->set_width(static_cast<double>(intersection_rect_as_pixel_rect.width()));
  2643. intersection_rect->set_height(static_cast<double>(intersection_rect_as_pixel_rect.height()));
  2644. // FIXME: 6. Map intersectionRect to the coordinate space of the viewport of the document containing target.
  2645. // 7. Return intersectionRect.
  2646. return intersection_rect;
  2647. }
  2648. // https://www.w3.org/TR/intersection-observer/#run-the-update-intersection-observations-steps
  2649. void Document::run_the_update_intersection_observations_steps(HighResolutionTime::DOMHighResTimeStamp time)
  2650. {
  2651. auto& realm = this->realm();
  2652. // 1. Let observer list be a list of all IntersectionObservers whose root is in the DOM tree of document.
  2653. // For the top-level browsing context, this includes implicit root observers.
  2654. // 2. For each observer in observer list:
  2655. for (auto& observer : m_intersection_observers) {
  2656. // 1. Let rootBounds be observer’s root intersection rectangle.
  2657. auto root_bounds = observer->root_intersection_rectangle();
  2658. // 2. For each target in observer’s internal [[ObservationTargets]] slot, processed in the same order that
  2659. // observe() was called on each target:
  2660. for (auto& target : observer->observation_targets()) {
  2661. // 1. Let:
  2662. // thresholdIndex be 0.
  2663. size_t threshold_index = 0;
  2664. // isIntersecting be false.
  2665. bool is_intersecting = false;
  2666. // targetRect be a DOMRectReadOnly with x, y, width, and height set to 0.
  2667. auto target_rect = Geometry::DOMRectReadOnly::construct_impl(realm, 0, 0, 0, 0).release_value_but_fixme_should_propagate_errors();
  2668. // intersectionRect be a DOMRectReadOnly with x, y, width, and height set to 0.
  2669. auto intersection_rect = Geometry::DOMRectReadOnly::construct_impl(realm, 0, 0, 0, 0).release_value_but_fixme_should_propagate_errors();
  2670. // SPEC ISSUE: It doesn't pass in intersection ratio to "queue an IntersectionObserverEntry" despite needing it.
  2671. // This is default 0, as isIntersecting is default false, see step 9.
  2672. double intersection_ratio = 0.0;
  2673. // 2. If the intersection root is not the implicit root, and target is not in the same document as the intersection root, skip to step 11.
  2674. // 3. If the intersection root is an Element, and target is not a descendant of the intersection root in the containing block chain, skip to step 11.
  2675. // FIXME: Actually use the containing block chain.
  2676. auto intersection_root = observer->intersection_root();
  2677. auto intersection_root_document = intersection_root.visit([](auto& node) -> JS::NonnullGCPtr<Document> {
  2678. return node->document();
  2679. });
  2680. if (!(observer->root().has<Empty>() && &target->document() == intersection_root_document.ptr())
  2681. || !(intersection_root.has<JS::Handle<DOM::Element>>() && !target->is_descendant_of(*intersection_root.get<JS::Handle<DOM::Element>>()))) {
  2682. // 4. Set targetRect to the DOMRectReadOnly obtained by getting the bounding box for target.
  2683. target_rect = target->get_bounding_client_rect();
  2684. // 5. Let intersectionRect be the result of running the compute the intersection algorithm on target and
  2685. // observer’s intersection root.
  2686. intersection_rect = compute_intersection(target, observer);
  2687. // 6. Let targetArea be targetRect’s area.
  2688. auto target_area = target_rect->width() * target_rect->height();
  2689. // 7. Let intersectionArea be intersectionRect’s area.
  2690. auto intersection_area = intersection_rect->width() * intersection_rect->height();
  2691. // 8. Let isIntersecting be true if targetRect and rootBounds intersect or are edge-adjacent, even if the
  2692. // intersection has zero area (because rootBounds or targetRect have zero area).
  2693. CSSPixelRect target_rect_as_pixel_rect(target_rect->x(), target_rect->y(), target_rect->width(), target_rect->height());
  2694. is_intersecting = target_rect_as_pixel_rect.intersects(root_bounds);
  2695. // 9. If targetArea is non-zero, let intersectionRatio be intersectionArea divided by targetArea.
  2696. // Otherwise, let intersectionRatio be 1 if isIntersecting is true, or 0 if isIntersecting is false.
  2697. if (target_area != 0.0)
  2698. intersection_ratio = intersection_area / target_area;
  2699. else
  2700. intersection_ratio = is_intersecting ? 1.0 : 0.0;
  2701. // 10. Set thresholdIndex to the index of the first entry in observer.thresholds whose value is greater
  2702. // than intersectionRatio, or the length of observer.thresholds if intersectionRatio is greater than
  2703. // or equal to the last entry in observer.thresholds.
  2704. threshold_index = observer->thresholds().find_first_index_if([&intersection_ratio](double threshold_value) {
  2705. return threshold_value > intersection_ratio;
  2706. })
  2707. .value_or(observer->thresholds().size());
  2708. }
  2709. // 11. Let intersectionObserverRegistration be the IntersectionObserverRegistration record in target’s
  2710. // internal [[RegisteredIntersectionObservers]] slot whose observer property is equal to observer.
  2711. auto& intersection_observer_registration = target->get_intersection_observer_registration({}, observer);
  2712. // 12. Let previousThresholdIndex be the intersectionObserverRegistration’s previousThresholdIndex property.
  2713. auto previous_threshold_index = intersection_observer_registration.previous_threshold_index;
  2714. // 13. Let previousIsIntersecting be the intersectionObserverRegistration’s previousIsIntersecting property.
  2715. auto previous_is_intersecting = intersection_observer_registration.previous_is_intersecting;
  2716. // 14. If thresholdIndex does not equal previousThresholdIndex or if isIntersecting does not equal
  2717. // previousIsIntersecting, queue an IntersectionObserverEntry, passing in observer, time,
  2718. // rootBounds, targetRect, intersectionRect, isIntersecting, and target.
  2719. if (threshold_index != previous_threshold_index || is_intersecting != previous_is_intersecting) {
  2720. auto root_bounds_as_dom_rect = Geometry::DOMRectReadOnly::construct_impl(realm, static_cast<double>(root_bounds.x()), static_cast<double>(root_bounds.y()), static_cast<double>(root_bounds.width()), static_cast<double>(root_bounds.height())).release_value_but_fixme_should_propagate_errors();
  2721. // SPEC ISSUE: It doesn't pass in intersectionRatio, but it's required.
  2722. queue_an_intersection_observer_entry(observer, time, root_bounds_as_dom_rect, target_rect, intersection_rect, is_intersecting, intersection_ratio, target);
  2723. }
  2724. // 15. Assign thresholdIndex to intersectionObserverRegistration’s previousThresholdIndex property.
  2725. intersection_observer_registration.previous_threshold_index = threshold_index;
  2726. // 16. Assign isIntersecting to intersectionObserverRegistration’s previousIsIntersecting property.
  2727. intersection_observer_registration.previous_is_intersecting = is_intersecting;
  2728. }
  2729. }
  2730. }
  2731. // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#start-intersection-observing-a-lazy-loading-element
  2732. void Document::start_intersection_observing_a_lazy_loading_element(Element& element)
  2733. {
  2734. VERIFY(element.is_lazy_loading());
  2735. auto& realm = this->realm();
  2736. // 1. Let doc be element's node document.
  2737. VERIFY(&element.document() == this);
  2738. // 2. If doc's lazy load intersection observer is null, set it to a new IntersectionObserver instance, initialized as follows:
  2739. if (!m_lazy_load_intersection_observer) {
  2740. // - The callback is these steps, with arguments entries and observer:
  2741. auto callback = JS::NativeFunction::create(realm, "", [this](JS::VM& vm) -> JS::ThrowCompletionOr<JS::Value> {
  2742. // For each entry in entries using a method of iteration which does not trigger developer-modifiable array accessors or iteration hooks:
  2743. auto& entries = verify_cast<JS::Array>(vm.argument(0).as_object());
  2744. auto entries_length = MUST(MUST(entries.get(vm.names.length)).to_length(vm));
  2745. for (size_t i = 0; i < entries_length; ++i) {
  2746. auto property_key = JS::PropertyKey { i };
  2747. auto& entry = verify_cast<IntersectionObserver::IntersectionObserverEntry>(entries.get_without_side_effects(property_key).as_object());
  2748. // 1. Let resumptionSteps be null.
  2749. JS::GCPtr<JS::HeapFunction<void()>> resumption_steps;
  2750. // 2. If entry.isIntersecting is true, then set resumptionSteps to entry.target's lazy load resumption steps.
  2751. if (entry.is_intersecting()) {
  2752. // 5. Set entry.target's lazy load resumption steps to null.
  2753. VERIFY(entry.target()->is_lazy_loading());
  2754. resumption_steps = entry.target()->take_lazy_load_resumption_steps({});
  2755. }
  2756. // 3. If resumptionSteps is null, then return.
  2757. if (!resumption_steps) {
  2758. // NOTE: This is wrong in the spec, since we want to keep processing
  2759. // entries even if one of them doesn't have resumption steps.
  2760. // FIXME: Spec bug: https://github.com/whatwg/html/issues/10019
  2761. continue;
  2762. }
  2763. // 4. Stop intersection-observing a lazy loading element for entry.target.
  2764. // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#stop-intersection-observing-a-lazy-loading-element
  2765. // 1. Let doc be element's node document.
  2766. // NOTE: It's `this`.
  2767. // 2. Assert: doc's lazy load intersection observer is not null.
  2768. VERIFY(m_lazy_load_intersection_observer);
  2769. // 3. Call doc's lazy load intersection observer unobserve method with element as the argument.
  2770. m_lazy_load_intersection_observer->unobserve(entry.target());
  2771. // 6. Invoke resumptionSteps.
  2772. resumption_steps->function()();
  2773. }
  2774. return JS::js_undefined();
  2775. });
  2776. // FIXME: The options is an IntersectionObserverInit dictionary with the following dictionary members: «[ "rootMargin" → lazy load root margin ]»
  2777. // Spec Note: This allows for fetching the image during scrolling, when it does not yet — but is about to — intersect the viewport.
  2778. auto options = IntersectionObserver::IntersectionObserverInit {};
  2779. auto wrapped_callback = realm.heap().allocate_without_realm<WebIDL::CallbackType>(callback, Bindings::host_defined_environment_settings_object(realm));
  2780. m_lazy_load_intersection_observer = IntersectionObserver::IntersectionObserver::construct_impl(realm, wrapped_callback, options).release_value_but_fixme_should_propagate_errors();
  2781. }
  2782. // 3. Call doc's lazy load intersection observer's observe method with element as the argument.
  2783. VERIFY(m_lazy_load_intersection_observer);
  2784. m_lazy_load_intersection_observer->observe(element);
  2785. }
  2786. // https://html.spec.whatwg.org/multipage/semantics.html#shared-declarative-refresh-steps
  2787. void Document::shared_declarative_refresh_steps(StringView input, JS::GCPtr<HTML::HTMLMetaElement const> meta_element)
  2788. {
  2789. // 1. If document's will declaratively refresh is true, then return.
  2790. if (m_will_declaratively_refresh)
  2791. return;
  2792. // 2. Let position point at the first code point of input.
  2793. GenericLexer lexer(input);
  2794. // 3. Skip ASCII whitespace within input given position.
  2795. lexer.ignore_while(Infra::is_ascii_whitespace);
  2796. // 4. Let time be 0.
  2797. u32 time = 0;
  2798. // 5. Collect a sequence of code points that are ASCII digits from input given position, and let the result be timeString.
  2799. auto time_string = lexer.consume_while(is_ascii_digit);
  2800. // 6. If timeString is the empty string, then:
  2801. if (time_string.is_empty()) {
  2802. // 1. If the code point in input pointed to by position is not U+002E (.), then return.
  2803. if (lexer.peek() != '.')
  2804. return;
  2805. }
  2806. // 7. Otherwise, set time to the result of parsing timeString using the rules for parsing non-negative integers.
  2807. auto maybe_time = Web::HTML::parse_non_negative_integer(time_string);
  2808. // FIXME: Since we only collected ASCII digits, this can only fail because of overflow. What do we do when that happens? For now, default to 0.
  2809. if (maybe_time.has_value() && maybe_time.value() < NumericLimits<int>::max() && !Checked<int>::multiplication_would_overflow(static_cast<int>(maybe_time.value()), 1000)) {
  2810. time = maybe_time.value();
  2811. }
  2812. // 8. Collect a sequence of code points that are ASCII digits and U+002E FULL STOP characters (.) from input given
  2813. // position. Ignore any collected characters.
  2814. lexer.ignore_while([](auto c) {
  2815. return is_ascii_digit(c) || c == '.';
  2816. });
  2817. // 9. Let urlRecord be document's URL.
  2818. auto url_record = url();
  2819. // 10. If position is not past the end of input, then:
  2820. if (!lexer.is_eof()) {
  2821. // 1. If the code point in input pointed to by position is not U+003B (;), U+002C (,), or ASCII whitespace, then return.
  2822. if (lexer.peek() != ';' && lexer.peek() != ',' && !Infra::is_ascii_whitespace(lexer.peek()))
  2823. return;
  2824. // 2. Skip ASCII whitespace within input given position.
  2825. lexer.ignore_while(Infra::is_ascii_whitespace);
  2826. // 3. If the code point in input pointed to by position is U+003B (;) or U+002C (,), then advance position to the next code point.
  2827. if (lexer.peek() == ';' || lexer.peek() == ',')
  2828. lexer.ignore(1);
  2829. // 4. Skip ASCII whitespace within input given position.
  2830. lexer.ignore_while(Infra::is_ascii_whitespace);
  2831. }
  2832. // 11. If position is not past the end of input, then:
  2833. if (!lexer.is_eof()) {
  2834. // 1. Let urlString be the substring of input from the code point at position to the end of the string.
  2835. auto url_string = lexer.remaining();
  2836. // 2. If the code point in input pointed to by position is U+0055 (U) or U+0075 (u), then advance position to the next code point. Otherwise, jump to the step labeled skip quotes.
  2837. if (lexer.peek() == 'U' || lexer.peek() == 'u')
  2838. lexer.ignore(1);
  2839. else
  2840. goto skip_quotes;
  2841. // 3. If the code point in input pointed to by position is U+0052 (R) or U+0072 (r), then advance position to the next code point. Otherwise, jump to the step labeled parse.
  2842. if (lexer.peek() == 'R' || lexer.peek() == 'r')
  2843. lexer.ignore(1);
  2844. else
  2845. goto parse;
  2846. // 4. If the code point in input pointed to by position is U+004C (L) or U+006C (l), then advance position to the next code point. Otherwise, jump to the step labeled parse.
  2847. if (lexer.peek() == 'L' || lexer.peek() == 'l')
  2848. lexer.ignore(1);
  2849. else
  2850. goto parse;
  2851. // 5. Skip ASCII whitespace within input given position.
  2852. lexer.ignore_while(Infra::is_ascii_whitespace);
  2853. // 6. If the code point in input pointed to by position is U+003D (=), then advance position to the next code point. Otherwise, jump to the step labeled parse.
  2854. if (lexer.peek() == '=')
  2855. lexer.ignore(1);
  2856. else
  2857. goto parse;
  2858. // 7. Skip ASCII whitespace within input given position.
  2859. lexer.ignore_while(Infra::is_ascii_whitespace);
  2860. skip_quotes : {
  2861. // 8. Skip quotes: If the code point in input pointed to by position is U+0027 (') or U+0022 ("), then let
  2862. // quote be that code point, and advance position to the next code point. Otherwise, let quote be the empty
  2863. // string.
  2864. Optional<char> quote;
  2865. if (lexer.peek() == '\'' || lexer.peek() == '"')
  2866. quote = lexer.consume();
  2867. // 9. Set urlString to the substring of input from the code point at position to the end of the string.
  2868. // 10. If quote is not the empty string, and there is a code point in urlString equal to quote, then truncate
  2869. // urlString at that code point, so that it and all subsequent code points are removed.
  2870. url_string = lexer.consume_while([&quote](auto c) {
  2871. return !quote.has_value() || c != quote.value();
  2872. });
  2873. }
  2874. parse:
  2875. // 11. Parse: Parse urlString relative to document. If that fails, return. Otherwise, set urlRecord to the
  2876. // resulting URL record.
  2877. auto maybe_url_record = parse_url(url_string);
  2878. if (!maybe_url_record.is_valid())
  2879. return;
  2880. url_record = maybe_url_record;
  2881. }
  2882. // 12. Set document's will declaratively refresh to true.
  2883. m_will_declaratively_refresh = true;
  2884. // 13. Perform one or more of the following steps:
  2885. // - After the refresh has come due (as defined below), if the user has not canceled the redirect and, if meta is
  2886. // given, document's active sandboxing flag set does not have the sandboxed automatic features browsing context
  2887. // flag set, then navigate document's node navigable to urlRecord using document, with historyHandling set to
  2888. // "replace".
  2889. m_active_refresh_timer = Core::Timer::create_single_shot(time * 1000, [this, has_meta_element = !!meta_element, url_record = move(url_record)]() {
  2890. if (has_meta_element && has_flag(active_sandboxing_flag_set(), HTML::SandboxingFlagSet::SandboxedAutomaticFeatures))
  2891. return;
  2892. VERIFY(navigable());
  2893. MUST(navigable()->navigate({ .url = url_record, .source_document = *this }));
  2894. }).release_value_but_fixme_should_propagate_errors();
  2895. // For the purposes of the previous paragraph, a refresh is said to have come due as soon as the later of the
  2896. // following two conditions occurs:
  2897. // - At least time seconds have elapsed since document's completely loaded time, adjusted to take into
  2898. // account user or user agent preferences.
  2899. // m_active_refresh_timer is started in completely_finished_loading after setting the completely loaded time.
  2900. // - If meta is given, at least time seconds have elapsed since meta was inserted into the document document,
  2901. // adjusted to take into account user or user agent preferences.
  2902. // NOTE: This is only done if completely loaded time has a value because shared_declarative_refresh_steps is called
  2903. // by HTMLMetaElement::inserted and if the document hasn't finished loading when the meta element was inserted,
  2904. // then the document completely finishing loading will _always_ come after inserting the meta element.
  2905. if (meta_element && m_completely_loaded_time.has_value()) {
  2906. m_active_refresh_timer->start();
  2907. }
  2908. }
  2909. Painting::ViewportPaintable const* Document::paintable() const
  2910. {
  2911. return static_cast<Painting::ViewportPaintable const*>(Node::paintable());
  2912. }
  2913. Painting::ViewportPaintable* Document::paintable()
  2914. {
  2915. return static_cast<Painting::ViewportPaintable*>(Node::paintable());
  2916. }
  2917. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#restore-the-history-object-state
  2918. void Document::restore_the_history_object_state(JS::NonnullGCPtr<HTML::SessionHistoryEntry> entry)
  2919. {
  2920. // 1. Let targetRealm be document's relevant realm.
  2921. auto& target_realm = HTML::relevant_realm(*this);
  2922. // 2. Let state be StructuredDeserialize(entry's classic history API state, targetRealm). If this throws an exception, catch it and let state be null.
  2923. // 3. Set document's history object's state to state.
  2924. auto state_or_error = HTML::structured_deserialize(target_realm.vm(), entry->classic_history_api_state, target_realm, {});
  2925. if (state_or_error.is_error())
  2926. m_history->set_state(JS::js_null());
  2927. else
  2928. m_history->set_state(state_or_error.release_value());
  2929. }
  2930. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#update-document-for-history-step-application
  2931. void Document::update_for_history_step_application(JS::NonnullGCPtr<HTML::SessionHistoryEntry> entry, bool do_not_reactivate, size_t script_history_length, size_t script_history_index, Optional<Vector<JS::NonnullGCPtr<HTML::SessionHistoryEntry>>> entries_for_navigation_api, bool update_navigation_api)
  2932. {
  2933. // 1. Let documentIsNew be true if document's latest entry is null; otherwise false.
  2934. auto document_is_new = !m_latest_entry;
  2935. // 2. Let documentsEntryChanged be true if document's latest entry is not entry; otherwise false.
  2936. auto documents_entry_changed = m_latest_entry != entry;
  2937. // 3. Set document's history object's index to scriptHistoryIndex.
  2938. history()->m_index = script_history_index;
  2939. // 4. Set document's history object's length to scriptHistoryLength.
  2940. history()->m_length = script_history_length;
  2941. // 5. If documentsEntryChanged is true, then:
  2942. // NOTE: documentsEntryChanged can be false for one of two reasons: either we are restoring from bfcache,
  2943. // or we are asynchronously finishing up a synchronous navigation which already synchronously set document's latest entry.
  2944. // The doNotReactivate argument distinguishes between these two cases.
  2945. if (documents_entry_changed) {
  2946. // 1. Let oldURL be document's latest entry's URL.
  2947. auto old_url = m_latest_entry ? m_latest_entry->url : AK::URL {};
  2948. // 2. Set document's latest entry to entry.
  2949. m_latest_entry = entry;
  2950. // 3. Restore the history object state given document and entry.
  2951. restore_the_history_object_state(entry);
  2952. // 4. Let navigation be history's relevant global object's navigation API.
  2953. auto navigation = verify_cast<HTML::Window>(HTML::relevant_global_object(*this)).navigation();
  2954. // 5. If documentIsNew is false, then:
  2955. if (!document_is_new) {
  2956. // AD HOC: Skip this in situations the spec steps don't account for
  2957. if (update_navigation_api) {
  2958. // 1. Update the navigation API entries for a same-document navigation given navigation, entry, and "traverse".
  2959. navigation->update_the_navigation_api_entries_for_a_same_document_navigation(entry, Bindings::NavigationType::Traverse);
  2960. }
  2961. // FIXME: 2. Fire an event named popstate at document's relevant global object, using PopStateEvent,
  2962. // with the state attribute initialized to document's history object's state and hasUAVisualTransition initialized to true
  2963. // if a visual transition, to display a cached rendered state of the latest entry, was done by the user agent.
  2964. // FIXME: 3. Restore persisted state given entry.
  2965. // FIXME: 4. If oldURL's fragment is not equal to entry's URL's fragment, then queue a global task on the DOM manipulation task source
  2966. // given document's relevant global object to fire an event named hashchange at document's relevant global object,
  2967. // using HashChangeEvent, with the oldURL attribute initialized to the serialization of oldURL and the newURL attribute
  2968. // initialized to the serialization of entry's URL.
  2969. }
  2970. // 6. Otherwise:
  2971. else {
  2972. // 1. Assert: entriesForNavigationAPI is given.
  2973. VERIFY(entries_for_navigation_api.has_value());
  2974. // FIXME: 2. Restore persisted state given entry.
  2975. // 3. Initialize the navigation API entries for a new document given navigation, entriesForNavigationAPI, and entry.
  2976. navigation->initialize_the_navigation_api_entries_for_a_new_document(*entries_for_navigation_api, entry);
  2977. }
  2978. }
  2979. // 6. If documentIsNew is true, then:
  2980. if (document_is_new) {
  2981. // FIXME: 1. Try to scroll to the fragment for document.
  2982. // FIXME: According to the spec we should only scroll here if document has no parser or parsing has stopped.
  2983. // It should be ok to remove this after we implement navigation events and scrolling will happen in
  2984. // "process scroll behavior".
  2985. scroll_to_the_fragment();
  2986. // 2. At this point scripts may run for the newly-created document document.
  2987. m_ready_to_run_scripts = true;
  2988. }
  2989. // 7. Otherwise, if documentsEntryChanged is false and doNotReactivate is false, then:
  2990. // NOTE: This is for bfcache restoration
  2991. if (!documents_entry_changed && !do_not_reactivate) {
  2992. // FIXME: 1. Assert: entriesForNavigationAPI is given.
  2993. // FIXME: 2. Reactivate document given entry and entriesForNavigationAPI.
  2994. }
  2995. }
  2996. HashMap<AK::URL, JS::GCPtr<HTML::SharedImageRequest>>& Document::shared_image_requests()
  2997. {
  2998. return m_shared_image_requests;
  2999. }
  3000. // https://www.w3.org/TR/web-animations-1/#dom-document-timeline
  3001. JS::NonnullGCPtr<Animations::DocumentTimeline> Document::timeline()
  3002. {
  3003. // The DocumentTimeline object representing the default document timeline.
  3004. if (!m_default_timeline)
  3005. m_default_timeline = Animations::DocumentTimeline::create(realm(), *this, static_cast<double>(MonotonicTime::now().milliseconds()));
  3006. return *m_default_timeline;
  3007. }
  3008. void Document::associate_with_timeline(JS::NonnullGCPtr<Animations::AnimationTimeline> timeline)
  3009. {
  3010. m_associated_animation_timelines.set(timeline);
  3011. }
  3012. void Document::disassociate_with_timeline(JS::NonnullGCPtr<Animations::AnimationTimeline> timeline)
  3013. {
  3014. m_associated_animation_timelines.remove(timeline);
  3015. }
  3016. void Document::append_pending_animation_event(Web::DOM::Document::PendingAnimationEvent const& event)
  3017. {
  3018. m_pending_animation_event_queue.append(event);
  3019. }
  3020. void Document::element_id_changed(Badge<DOM::Element>)
  3021. {
  3022. for (auto* form_associated_element : m_form_associated_elements_with_form_attribute)
  3023. form_associated_element->element_id_changed({});
  3024. }
  3025. void Document::element_with_id_was_added_or_removed(Badge<DOM::Element>)
  3026. {
  3027. for (auto* form_associated_element : m_form_associated_elements_with_form_attribute)
  3028. form_associated_element->element_with_id_was_added_or_removed({});
  3029. }
  3030. void Document::add_form_associated_element_with_form_attribute(HTML::FormAssociatedElement& form_associated_element)
  3031. {
  3032. m_form_associated_elements_with_form_attribute.append(&form_associated_element);
  3033. }
  3034. void Document::remove_form_associated_element_with_form_attribute(HTML::FormAssociatedElement& form_associated_element)
  3035. {
  3036. m_form_associated_elements_with_form_attribute.remove_all_matching([&](auto* element) {
  3037. return element == &form_associated_element;
  3038. });
  3039. }
  3040. void Document::set_design_mode_enabled_state(bool design_mode_enabled)
  3041. {
  3042. m_design_mode_enabled = design_mode_enabled;
  3043. set_editable(design_mode_enabled);
  3044. }
  3045. // https://html.spec.whatwg.org/multipage/interaction.html#making-entire-documents-editable:-the-designmode-idl-attribute
  3046. String Document::design_mode() const
  3047. {
  3048. // The designMode getter steps are to return "on" if this's design mode enabled is true; otherwise "off".
  3049. return design_mode_enabled_state() ? "on"_string : "off"_string;
  3050. }
  3051. WebIDL::ExceptionOr<void> Document::set_design_mode(String const& design_mode)
  3052. {
  3053. // 1. Let value be the given value, converted to ASCII lowercase.
  3054. auto value = MUST(design_mode.to_lowercase());
  3055. // 2. If value is "on" and this's design mode enabled is false, then:
  3056. if (value == "on"sv && !m_design_mode_enabled) {
  3057. // 1. Set this's design mode enabled to true.
  3058. set_design_mode_enabled_state(true);
  3059. // 2. Reset this's active range's start and end boundary points to be at the start of this.
  3060. if (auto selection = get_selection(); selection) {
  3061. if (auto active_range = selection->range(); active_range) {
  3062. TRY(active_range->set_start(*this, 0));
  3063. TRY(active_range->set_end(*this, 0));
  3064. update_layout();
  3065. }
  3066. }
  3067. // 3. Run the focusing steps for this's document element, if non-null.
  3068. if (auto* document_element = this->document_element(); document_element)
  3069. HTML::run_focusing_steps(document_element);
  3070. }
  3071. // 3. If value is "off", then set this's design mode enabled to false.
  3072. else if (value == "off"sv) {
  3073. set_design_mode_enabled_state(false);
  3074. }
  3075. return {};
  3076. }
  3077. // https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint
  3078. Element const* Document::element_from_point(double x, double y)
  3079. {
  3080. // 1. If either argument is negative, x is greater than the viewport width excluding the size of a rendered scroll
  3081. // bar (if any), or y is greater than the viewport height excluding the size of a rendered scroll bar (if any), or
  3082. // there is no viewport associated with the document, return null and terminate these steps.
  3083. auto viewport_rect = this->viewport_rect();
  3084. CSSPixelPoint position { x, y };
  3085. // FIXME: This should account for the size of the scroll bar.
  3086. if (x < 0 || y < 0 || position.x() > viewport_rect.width() || position.y() > viewport_rect.height())
  3087. return nullptr;
  3088. // Ensure the layout tree exists prior to hit testing.
  3089. update_layout();
  3090. // 2. If there is a box in the viewport that would be a target for hit testing at coordinates x,y, when applying the transforms
  3091. // that apply to the descendants of the viewport, return the associated element and terminate these steps.
  3092. Optional<Painting::HitTestResult> hit_test_result;
  3093. if (auto const* paintable_box = this->paintable_box(); paintable_box) {
  3094. (void)paintable_box->hit_test(position, Painting::HitTestType::Exact, [&](Painting::HitTestResult result) {
  3095. auto* dom_node = result.dom_node();
  3096. if (dom_node && dom_node->is_element()) {
  3097. hit_test_result = result;
  3098. return Painting::TraversalDecision::Break;
  3099. }
  3100. return Painting::TraversalDecision::Continue;
  3101. });
  3102. }
  3103. if (hit_test_result.has_value())
  3104. return static_cast<Element*>(hit_test_result->dom_node());
  3105. // 3. If the document has a root element, return the root element and terminate these steps.
  3106. if (auto const* document_root_element = first_child_of_type<Element>(); document_root_element)
  3107. return document_root_element;
  3108. // 4. Return null.
  3109. return nullptr;
  3110. }
  3111. // https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint
  3112. Vector<JS::NonnullGCPtr<Element>> Document::elements_from_point(double x, double y)
  3113. {
  3114. // 1. Let sequence be a new empty sequence.
  3115. Vector<JS::NonnullGCPtr<Element>> sequence;
  3116. // 2. If either argument is negative, x is greater than the viewport width excluding the size of a rendered scroll bar (if any),
  3117. // or y is greater than the viewport height excluding the size of a rendered scroll bar (if any),
  3118. // or there is no viewport associated with the document, return sequence and terminate these steps.
  3119. auto viewport_rect = this->viewport_rect();
  3120. CSSPixelPoint position { x, y };
  3121. // FIXME: This should account for the size of the scroll bar.
  3122. if (x < 0 || y < 0 || position.x() > viewport_rect.width() || position.y() > viewport_rect.height())
  3123. return sequence;
  3124. // Ensure the layout tree exists prior to hit testing.
  3125. update_layout();
  3126. // 3. For each box in the viewport, in paint order, starting with the topmost box, that would be a target for
  3127. // hit testing at coordinates x,y even if nothing would be overlapping it, when applying the transforms that
  3128. // apply to the descendants of the viewport, append the associated element to sequence.
  3129. if (auto const* paintable_box = this->paintable_box(); paintable_box) {
  3130. (void)paintable_box->hit_test(position, Painting::HitTestType::Exact, [&](Painting::HitTestResult result) {
  3131. auto* dom_node = result.dom_node();
  3132. if (dom_node && dom_node->is_element())
  3133. sequence.append(*static_cast<Element*>(dom_node));
  3134. return Painting::TraversalDecision::Continue;
  3135. });
  3136. }
  3137. // 4. If the document has a root element, and the last item in sequence is not the root element,
  3138. // append the root element to sequence.
  3139. if (auto* root_element = document_element(); root_element && (sequence.is_empty() || (sequence.last() != root_element)))
  3140. sequence.append(*root_element);
  3141. // 5. Return sequence.
  3142. return sequence;
  3143. }
  3144. }