Document.cpp 222 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261
  1. /*
  2. * Copyright (c) 2018-2024, 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. * Copyright (c) 2024, Matthew Olsson <mattco@serenityos.org>
  7. *
  8. * SPDX-License-Identifier: BSD-2-Clause
  9. */
  10. #include <AK/CharacterTypes.h>
  11. #include <AK/Debug.h>
  12. #include <AK/GenericLexer.h>
  13. #include <AK/InsertionSort.h>
  14. #include <AK/StringBuilder.h>
  15. #include <AK/Utf8View.h>
  16. #include <LibCore/Timer.h>
  17. #include <LibJS/Runtime/Array.h>
  18. #include <LibJS/Runtime/FunctionObject.h>
  19. #include <LibJS/Runtime/NativeFunction.h>
  20. #include <LibWeb/Animations/Animation.h>
  21. #include <LibWeb/Animations/AnimationPlaybackEvent.h>
  22. #include <LibWeb/Animations/AnimationTimeline.h>
  23. #include <LibWeb/Animations/DocumentTimeline.h>
  24. #include <LibWeb/Bindings/DocumentPrototype.h>
  25. #include <LibWeb/Bindings/MainThreadVM.h>
  26. #include <LibWeb/CSS/AnimationEvent.h>
  27. #include <LibWeb/CSS/CSSAnimation.h>
  28. #include <LibWeb/CSS/FontFaceSet.h>
  29. #include <LibWeb/CSS/MediaQueryList.h>
  30. #include <LibWeb/CSS/MediaQueryListEvent.h>
  31. #include <LibWeb/CSS/StyleComputer.h>
  32. #include <LibWeb/CSS/SystemColor.h>
  33. #include <LibWeb/CSS/VisualViewport.h>
  34. #include <LibWeb/Cookie/ParsedCookie.h>
  35. #include <LibWeb/DOM/AdoptedStyleSheets.h>
  36. #include <LibWeb/DOM/Attr.h>
  37. #include <LibWeb/DOM/CDATASection.h>
  38. #include <LibWeb/DOM/Comment.h>
  39. #include <LibWeb/DOM/CustomEvent.h>
  40. #include <LibWeb/DOM/DOMImplementation.h>
  41. #include <LibWeb/DOM/Document.h>
  42. #include <LibWeb/DOM/DocumentFragment.h>
  43. #include <LibWeb/DOM/DocumentObserver.h>
  44. #include <LibWeb/DOM/DocumentType.h>
  45. #include <LibWeb/DOM/Element.h>
  46. #include <LibWeb/DOM/ElementFactory.h>
  47. #include <LibWeb/DOM/Event.h>
  48. #include <LibWeb/DOM/HTMLCollection.h>
  49. #include <LibWeb/DOM/NodeIterator.h>
  50. #include <LibWeb/DOM/ProcessingInstruction.h>
  51. #include <LibWeb/DOM/Range.h>
  52. #include <LibWeb/DOM/ShadowRoot.h>
  53. #include <LibWeb/DOM/Text.h>
  54. #include <LibWeb/DOM/TreeWalker.h>
  55. #include <LibWeb/DOMURL/DOMURL.h>
  56. #include <LibWeb/Dump.h>
  57. #include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h>
  58. #include <LibWeb/FileAPI/BlobURLStore.h>
  59. #include <LibWeb/HTML/AttributeNames.h>
  60. #include <LibWeb/HTML/BrowsingContext.h>
  61. #include <LibWeb/HTML/CustomElements/CustomElementDefinition.h>
  62. #include <LibWeb/HTML/CustomElements/CustomElementReactionNames.h>
  63. #include <LibWeb/HTML/CustomElements/CustomElementRegistry.h>
  64. #include <LibWeb/HTML/DocumentState.h>
  65. #include <LibWeb/HTML/EventLoop/EventLoop.h>
  66. #include <LibWeb/HTML/EventNames.h>
  67. #include <LibWeb/HTML/Focus.h>
  68. #include <LibWeb/HTML/HTMLAllCollection.h>
  69. #include <LibWeb/HTML/HTMLAnchorElement.h>
  70. #include <LibWeb/HTML/HTMLAreaElement.h>
  71. #include <LibWeb/HTML/HTMLBaseElement.h>
  72. #include <LibWeb/HTML/HTMLBodyElement.h>
  73. #include <LibWeb/HTML/HTMLDocument.h>
  74. #include <LibWeb/HTML/HTMLEmbedElement.h>
  75. #include <LibWeb/HTML/HTMLFormElement.h>
  76. #include <LibWeb/HTML/HTMLFrameSetElement.h>
  77. #include <LibWeb/HTML/HTMLHeadElement.h>
  78. #include <LibWeb/HTML/HTMLHtmlElement.h>
  79. #include <LibWeb/HTML/HTMLIFrameElement.h>
  80. #include <LibWeb/HTML/HTMLImageElement.h>
  81. #include <LibWeb/HTML/HTMLLinkElement.h>
  82. #include <LibWeb/HTML/HTMLObjectElement.h>
  83. #include <LibWeb/HTML/HTMLScriptElement.h>
  84. #include <LibWeb/HTML/HTMLTitleElement.h>
  85. #include <LibWeb/HTML/HashChangeEvent.h>
  86. #include <LibWeb/HTML/ListOfAvailableImages.h>
  87. #include <LibWeb/HTML/Location.h>
  88. #include <LibWeb/HTML/MessageEvent.h>
  89. #include <LibWeb/HTML/Navigable.h>
  90. #include <LibWeb/HTML/Navigation.h>
  91. #include <LibWeb/HTML/NavigationParams.h>
  92. #include <LibWeb/HTML/Numbers.h>
  93. #include <LibWeb/HTML/Origin.h>
  94. #include <LibWeb/HTML/Parser/HTMLParser.h>
  95. #include <LibWeb/HTML/PopStateEvent.h>
  96. #include <LibWeb/HTML/Scripting/ClassicScript.h>
  97. #include <LibWeb/HTML/Scripting/ExceptionReporter.h>
  98. #include <LibWeb/HTML/Scripting/WindowEnvironmentSettingsObject.h>
  99. #include <LibWeb/HTML/TraversableNavigable.h>
  100. #include <LibWeb/HTML/Window.h>
  101. #include <LibWeb/HTML/WindowProxy.h>
  102. #include <LibWeb/HighResolutionTime/Performance.h>
  103. #include <LibWeb/HighResolutionTime/TimeOrigin.h>
  104. #include <LibWeb/Infra/CharacterTypes.h>
  105. #include <LibWeb/Infra/Strings.h>
  106. #include <LibWeb/IntersectionObserver/IntersectionObserver.h>
  107. #include <LibWeb/Layout/BlockFormattingContext.h>
  108. #include <LibWeb/Layout/TreeBuilder.h>
  109. #include <LibWeb/Layout/Viewport.h>
  110. #include <LibWeb/Namespace.h>
  111. #include <LibWeb/Page/Page.h>
  112. #include <LibWeb/Painting/ViewportPaintable.h>
  113. #include <LibWeb/PermissionsPolicy/AutoplayAllowlist.h>
  114. #include <LibWeb/ResizeObserver/ResizeObserver.h>
  115. #include <LibWeb/ResizeObserver/ResizeObserverEntry.h>
  116. #include <LibWeb/SVG/SVGDecodedImageData.h>
  117. #include <LibWeb/SVG/SVGElement.h>
  118. #include <LibWeb/SVG/SVGTitleElement.h>
  119. #include <LibWeb/SVG/TagNames.h>
  120. #include <LibWeb/Selection/Selection.h>
  121. #include <LibWeb/UIEvents/EventNames.h>
  122. #include <LibWeb/UIEvents/FocusEvent.h>
  123. #include <LibWeb/UIEvents/KeyboardEvent.h>
  124. #include <LibWeb/UIEvents/MouseEvent.h>
  125. #include <LibWeb/WebIDL/AbstractOperations.h>
  126. #include <LibWeb/WebIDL/DOMException.h>
  127. #include <LibWeb/WebIDL/ExceptionOr.h>
  128. namespace Web::DOM {
  129. JS_DEFINE_ALLOCATOR(Document);
  130. // https://html.spec.whatwg.org/multipage/origin.html#obtain-browsing-context-navigation
  131. static JS::NonnullGCPtr<HTML::BrowsingContext> obtain_a_browsing_context_to_use_for_a_navigation_response(
  132. HTML::BrowsingContext& browsing_context,
  133. HTML::SandboxingFlagSet sandbox_flags,
  134. HTML::CrossOriginOpenerPolicy navigation_coop,
  135. HTML::CrossOriginOpenerPolicyEnforcementResult coop_enforcement_result)
  136. {
  137. // 1. If browsingContext is not a top-level browsing context, return browsingContext.
  138. if (!browsing_context.is_top_level())
  139. return browsing_context;
  140. // 2. If coopEnforcementResult's needs a browsing context group switch is false, then:
  141. if (!coop_enforcement_result.needs_a_browsing_context_group_switch) {
  142. // 1. If coopEnforcementResult's would need a browsing context group switch due to report-only is true,
  143. if (coop_enforcement_result.would_need_a_browsing_context_group_switch_due_to_report_only) {
  144. // FIXME: set browsing context's virtual browsing context group ID to a new unique identifier.
  145. }
  146. // 2. Return browsingContext.
  147. return browsing_context;
  148. }
  149. // 3. Let newBrowsingContext be the first return value of creating a new top-level browsing context and document
  150. 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;
  151. // FIXME: 4. If navigationCOOP's value is "same-origin-plurs-COEP", then set newBrowsingContext's group's
  152. // cross-origin isolation mode to either "logical" or "concrete". The choice of which is implementation-defined.
  153. // 5. If sandboxFlags is not empty, then:
  154. if (!is_empty(sandbox_flags)) {
  155. // 1. Assert navigationCOOP's value is "unsafe-none".
  156. VERIFY(navigation_coop.value == HTML::CrossOriginOpenerPolicyValue::UnsafeNone);
  157. // 2. Assert: newBrowsingContext's popup sandboxing flag set is empty.
  158. // 3. Set newBrowsingContext's popup sandboxing flag set to a clone of sandboxFlags.
  159. }
  160. // 6. Return newBrowsingContext.
  161. return new_browsing_context;
  162. }
  163. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#initialise-the-document-object
  164. WebIDL::ExceptionOr<JS::NonnullGCPtr<Document>> Document::create_and_initialize(Type type, String content_type, HTML::NavigationParams const& navigation_params)
  165. {
  166. // 1. Let browsingContext be navigationParams's navigable's active browsing context.
  167. auto browsing_context = navigation_params.navigable->active_browsing_context();
  168. // 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,
  169. // navigationParams's cross-origin opener policy, and navigationParams's COOP enforcement result.
  170. browsing_context = obtain_a_browsing_context_to_use_for_a_navigation_response(
  171. *browsing_context,
  172. navigation_params.final_sandboxing_flag_set,
  173. navigation_params.cross_origin_opener_policy,
  174. navigation_params.coop_enforcement_result);
  175. // FIXME: 3. Let permissionsPolicy be the result of creating a permissions policy from a response
  176. // given browsingContext, navigationParams's origin, and navigationParams's response.
  177. // 4. Let creationURL be navigationParams's response's URL.
  178. auto creation_url = navigation_params.response->url();
  179. // 5. If navigationParams's request is non-null, then set creationURL to navigationParams's request's current URL.
  180. if (navigation_params.request) {
  181. creation_url = navigation_params.request->current_url();
  182. }
  183. // 6. Let window be null.
  184. JS::GCPtr<HTML::Window> window;
  185. // 7. If browsingContext's active document's is initial about:blank is true,
  186. // and browsingContext's active document's origin is same origin-domain with navigationParams's origin,
  187. // then set window to browsingContext's active window.
  188. // FIXME: still_on_its_initial_about_blank_document() is not in the spec anymore.
  189. // However, replacing this with the spec-mandated is_initial_about_blank() results in the browsing context
  190. // holding an incorrect active document for the replace from initial about:blank to the real document.
  191. // See #22293 for more details.
  192. if (false
  193. && (browsing_context->active_document() && browsing_context->active_document()->origin().is_same_origin(navigation_params.origin))) {
  194. window = browsing_context->active_window();
  195. }
  196. // 8. Otherwise:
  197. else {
  198. // FIXME: 1. Let oacHeader be the result of getting a structured field value given `Origin-Agent-Cluster` and "item" from response's header list.
  199. // FIXME: 2. Let requestsOAC be true if oacHeader is not null and oacHeader[0] is the boolean true; otherwise false.
  200. [[maybe_unused]] auto requests_oac = false;
  201. // FIXME: 3. If navigationParams's reserved environment is a non-secure context, then set requestsOAC to false.
  202. // FIXME: 4. Let agent be the result of obtaining a similar-origin window agent given navigationParams's origin, browsingContext's group, and requestsOAC.
  203. // 5. Let realm execution context be the result of creating a new JavaScript realm given agent and the following customizations:
  204. auto realm_execution_context = Bindings::create_a_new_javascript_realm(
  205. Bindings::main_thread_vm(),
  206. [&](JS::Realm& realm) -> JS::Object* {
  207. // - For the global object, create a new Window object.
  208. window = HTML::Window::create(realm);
  209. return window;
  210. },
  211. [&](JS::Realm&) -> JS::Object* {
  212. // - For the global this binding, use browsingContext's WindowProxy object.
  213. return browsing_context->window_proxy();
  214. });
  215. // 6. Set window to the global object of realmExecutionContext's Realm component.
  216. window = verify_cast<HTML::Window>(realm_execution_context->realm->global_object());
  217. // 7. Let topLevelCreationURL be creationURL.
  218. auto top_level_creation_url = creation_url;
  219. // 8. Let topLevelOrigin be navigationParams's origin.
  220. auto top_level_origin = navigation_params.origin;
  221. // 9. If navigable's container is not null, then:
  222. if (navigation_params.navigable->container()) {
  223. // 1. Let parentEnvironment be navigable's container's relevant settings object.
  224. auto& parent_environment = HTML::relevant_settings_object(*navigation_params.navigable->container());
  225. // 2. Set topLevelCreationURL to parentEnvironment's top-level creation URL.
  226. top_level_creation_url = parent_environment.top_level_creation_url;
  227. // 3. Set topLevelOrigin to parentEnvironment's top-level origin.
  228. top_level_origin = parent_environment.top_level_origin;
  229. }
  230. // 9. Set up a window environment settings object with creationURL, realm execution context,
  231. // navigationParams's reserved environment, topLevelCreationURL, and topLevelOrigin.
  232. // FIXME: Why do we assume `creation_url` is non-empty here? Is this a spec bug?
  233. // FIXME: Why do we assume `top_level_creation_url` is non-empty here? Is this a spec bug?
  234. HTML::WindowEnvironmentSettingsObject::setup(
  235. browsing_context->page(),
  236. creation_url.value(),
  237. move(realm_execution_context),
  238. navigation_params.reserved_environment,
  239. top_level_creation_url.value(),
  240. top_level_origin);
  241. }
  242. // 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.
  243. // 10. Let document be a new Document, with
  244. // type: type
  245. // content type: contentType
  246. // origin: navigationParams's origin
  247. // browsing context: browsingContext
  248. // policy container: navigationParams's policy container
  249. // FIXME: permissions policy: permissionsPolicy
  250. // active sandboxing flag set: navigationParams's final sandboxing flag set
  251. // FIXME: cross-origin opener policy: navigationParams's cross-origin opener policy
  252. // FIXME: load timing info: loadTimingInfo
  253. // FIXME: was created via cross-origin redirects: navigationParams's response's has cross-origin redirects
  254. // during-loading navigation ID for WebDriver BiDi: navigationParams's id
  255. // URL: creationURL
  256. // current document readiness: "loading"
  257. // about base URL: navigationParams's about base URL
  258. // allow declarative shadow roots: true
  259. auto document = HTML::HTMLDocument::create(window->realm());
  260. document->m_type = type;
  261. document->m_content_type = move(content_type);
  262. document->set_origin(navigation_params.origin);
  263. document->set_browsing_context(browsing_context);
  264. document->m_policy_container = navigation_params.policy_container;
  265. document->m_active_sandboxing_flag_set = navigation_params.final_sandboxing_flag_set;
  266. document->m_navigation_id = navigation_params.id;
  267. document->set_url(*creation_url);
  268. document->m_readiness = HTML::DocumentReadyState::Loading;
  269. document->m_about_base_url = navigation_params.about_base_url;
  270. document->set_allow_declarative_shadow_roots(true);
  271. document->m_window = window;
  272. // NOTE: Non-standard: Pull out the Last-Modified header for use in the lastModified property.
  273. if (auto maybe_last_modified = navigation_params.response->header_list()->get("Last-Modified"sv.bytes()); maybe_last_modified.has_value())
  274. document->m_last_modified = Core::DateTime::parse("%a, %d %b %Y %H:%M:%S %Z"sv, maybe_last_modified.value());
  275. // 11. Set window's associated Document to document.
  276. window->set_associated_document(*document);
  277. // FIXME: 12. Run CSP initialization for a Document given document.
  278. // 13. If navigationParams's request is non-null, then:
  279. if (navigation_params.request) {
  280. // 1. Set document's referrer to the empty string.
  281. document->m_referrer = String {};
  282. // 2. Let referrer be navigationParams's request's referrer.
  283. auto const& referrer = navigation_params.request->referrer();
  284. // 3. If referrer is a URL record, then set document's referrer to the serialization of referrer.
  285. if (referrer.has<URL::URL>()) {
  286. document->m_referrer = MUST(String::from_byte_string(referrer.get<URL::URL>().serialize()));
  287. }
  288. }
  289. // FIXME: 14: If navigationParams's fetch controller is not null, then:
  290. // FIXME: 15. Create the navigation timing entry for document, with navigationParams's response's timing info, redirectCount, navigationParams's navigation timing type, and
  291. // navigationParams's response's service worker timing info.
  292. // FIXME: 16. If navigationParams's response has a `Refresh` header, then:
  293. // FIXME: 17. If navigationParams's commit early hints is not null, then call navigationParams's commit early hints with document.
  294. // FIXME: 18. Process link headers given document, navigationParams's response, and "pre-media".
  295. // 19. Return document.
  296. return document;
  297. }
  298. WebIDL::ExceptionOr<JS::NonnullGCPtr<Document>> Document::construct_impl(JS::Realm& realm)
  299. {
  300. return Document::create(realm);
  301. }
  302. JS::NonnullGCPtr<Document> Document::create(JS::Realm& realm, URL::URL const& url)
  303. {
  304. return realm.heap().allocate<Document>(realm, realm, url);
  305. }
  306. Document::Document(JS::Realm& realm, const URL::URL& url)
  307. : ParentNode(realm, *this, NodeType::DOCUMENT_NODE)
  308. , m_page(Bindings::host_defined_page(realm))
  309. , m_style_computer(make<CSS::StyleComputer>(*this))
  310. , m_url(url)
  311. {
  312. m_legacy_platform_object_flags = PlatformObject::LegacyPlatformObjectFlags {
  313. .supports_named_properties = true,
  314. .has_legacy_override_built_ins_interface_extended_attribute = true,
  315. };
  316. HTML::main_thread_event_loop().register_document({}, *this);
  317. }
  318. Document::~Document()
  319. {
  320. HTML::main_thread_event_loop().unregister_document({}, *this);
  321. }
  322. void Document::initialize(JS::Realm& realm)
  323. {
  324. Base::initialize(realm);
  325. WEB_SET_PROTOTYPE_FOR_INTERFACE(Document);
  326. m_selection = heap().allocate<Selection::Selection>(realm, realm, *this);
  327. m_list_of_available_images = heap().allocate<HTML::ListOfAvailableImages>(realm);
  328. page().client().page_did_create_new_document(*this);
  329. }
  330. // https://html.spec.whatwg.org/multipage/document-lifecycle.html#populate-with-html/head/body
  331. WebIDL::ExceptionOr<void> Document::populate_with_html_head_and_body()
  332. {
  333. // 1. Let html be the result of creating an element given document, html, and the HTML namespace.
  334. auto html = TRY(DOM::create_element(*this, HTML::TagNames::html, Namespace::HTML));
  335. // 2. Let head be the result of creating an element given document, head, and the HTML namespace.
  336. auto head = TRY(DOM::create_element(*this, HTML::TagNames::head, Namespace::HTML));
  337. // 3. Let body be the result of creating an element given document, body, and the HTML namespace.
  338. auto body = TRY(DOM::create_element(*this, HTML::TagNames::body, Namespace::HTML));
  339. // 4. Append html to document.
  340. TRY(append_child(html));
  341. // 5. Append head to html.
  342. TRY(html->append_child(head));
  343. // 6. Append body to html.
  344. TRY(html->append_child(body));
  345. return {};
  346. }
  347. void Document::finalize()
  348. {
  349. Base::finalize();
  350. page().client().page_did_destroy_document(*this);
  351. }
  352. void Document::visit_edges(Cell::Visitor& visitor)
  353. {
  354. Base::visit_edges(visitor);
  355. visitor.visit(m_page);
  356. visitor.visit(m_window);
  357. visitor.visit(m_layout_root);
  358. visitor.visit(m_style_sheets);
  359. visitor.visit(m_hovered_node);
  360. visitor.visit(m_inspected_node);
  361. visitor.visit(m_active_favicon);
  362. visitor.visit(m_focused_element);
  363. visitor.visit(m_active_element);
  364. visitor.visit(m_target_element);
  365. visitor.visit(m_implementation);
  366. visitor.visit(m_current_script);
  367. visitor.visit(m_associated_inert_template_document);
  368. visitor.visit(m_appropriate_template_contents_owner_document);
  369. visitor.visit(m_pending_parsing_blocking_script);
  370. visitor.visit(m_history);
  371. visitor.visit(m_browsing_context);
  372. visitor.visit(m_applets);
  373. visitor.visit(m_anchors);
  374. visitor.visit(m_images);
  375. visitor.visit(m_embeds);
  376. visitor.visit(m_links);
  377. visitor.visit(m_forms);
  378. visitor.visit(m_scripts);
  379. visitor.visit(m_all);
  380. visitor.visit(m_fonts);
  381. visitor.visit(m_selection);
  382. visitor.visit(m_first_base_element_with_href_in_tree_order);
  383. visitor.visit(m_parser);
  384. visitor.visit(m_lazy_load_intersection_observer);
  385. visitor.visit(m_visual_viewport);
  386. visitor.visit(m_latest_entry);
  387. visitor.visit(m_default_timeline);
  388. visitor.visit(m_scripts_to_execute_when_parsing_has_finished);
  389. visitor.visit(m_scripts_to_execute_in_order_as_soon_as_possible);
  390. visitor.visit(m_scripts_to_execute_as_soon_as_possible);
  391. visitor.visit(m_node_iterators);
  392. visitor.visit(m_document_observers);
  393. visitor.visit(m_pending_scroll_event_targets);
  394. visitor.visit(m_pending_scrollend_event_targets);
  395. visitor.visit(m_intersection_observers);
  396. visitor.visit(m_resize_observers);
  397. visitor.visit(m_shared_image_requests);
  398. visitor.visit(m_associated_animation_timelines);
  399. visitor.visit(m_list_of_available_images);
  400. for (auto* form_associated_element : m_form_associated_elements_with_form_attribute)
  401. visitor.visit(form_associated_element->form_associated_element_to_html_element());
  402. visitor.visit(m_potentially_named_elements);
  403. for (auto& event : m_pending_animation_event_queue) {
  404. visitor.visit(event.event);
  405. visitor.visit(event.target);
  406. }
  407. visitor.visit(m_adopted_style_sheets);
  408. visitor.visit(m_shadow_roots);
  409. visitor.visit(m_top_layer_elements);
  410. visitor.visit(m_top_layer_pending_removals);
  411. }
  412. // https://w3c.github.io/selection-api/#dom-document-getselection
  413. JS::GCPtr<Selection::Selection> Document::get_selection() const
  414. {
  415. // The method must return the selection associated with this if this has an associated browsing context,
  416. // and it must return null otherwise.
  417. if (!browsing_context())
  418. return {};
  419. return m_selection;
  420. }
  421. // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-document-write
  422. WebIDL::ExceptionOr<void> Document::write(Vector<String> const& strings)
  423. {
  424. StringBuilder builder;
  425. builder.join(""sv, strings);
  426. return run_the_document_write_steps(builder.string_view());
  427. }
  428. // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-document-writeln
  429. WebIDL::ExceptionOr<void> Document::writeln(Vector<String> const& strings)
  430. {
  431. StringBuilder builder;
  432. builder.join(""sv, strings);
  433. builder.append("\n"sv);
  434. return run_the_document_write_steps(builder.string_view());
  435. }
  436. // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-write-steps
  437. WebIDL::ExceptionOr<void> Document::run_the_document_write_steps(StringView input)
  438. {
  439. // 1. If document is an XML document, then throw an "InvalidStateError" DOMException.
  440. if (m_type == Type::XML)
  441. return WebIDL::InvalidStateError::create(realm(), "write() called on XML document."_fly_string);
  442. // 2. If document's throw-on-dynamic-markup-insertion counter is greater than 0, then throw an "InvalidStateError" DOMException.
  443. if (m_throw_on_dynamic_markup_insertion_counter > 0)
  444. return WebIDL::InvalidStateError::create(realm(), "throw-on-dynamic-markup-insertion-counter greater than zero."_fly_string);
  445. // 3. If document's active parser was aborted is true, then return.
  446. if (m_active_parser_was_aborted)
  447. return {};
  448. // 4. If the insertion point is undefined, then:
  449. if (!(m_parser && m_parser->tokenizer().is_insertion_point_defined())) {
  450. // 1. If document's unload counter is greater than 0 or document's ignore-destructive-writes counter is greater than 0, then return.
  451. if (m_unload_counter > 0 || m_ignore_destructive_writes_counter > 0)
  452. return {};
  453. // 2. Run the document open steps with document.
  454. TRY(open());
  455. }
  456. // 5. Insert input into the input stream just before the insertion point.
  457. m_parser->tokenizer().insert_input_at_insertion_point(input);
  458. // 6. If there is no pending parsing-blocking script, have the HTML parser process input, one code point at a time,
  459. // processing resulting tokens as they are emitted, and stopping when the tokenizer reaches the insertion point
  460. // or when the processing of the tokenizer is aborted by the tree construction stage (this can happen if a script
  461. // end tag token is emitted by the tokenizer).
  462. if (!pending_parsing_blocking_script())
  463. m_parser->run(HTML::HTMLTokenizer::StopAtInsertionPoint::Yes);
  464. return {};
  465. }
  466. // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-document-open
  467. WebIDL::ExceptionOr<Document*> Document::open(Optional<String> const&, Optional<String> const&)
  468. {
  469. // If document belongs to a child navigable, we need to make sure its initial navigation is done,
  470. // because subsequent steps will modify "initial about:blank" to false, which would cause
  471. // initial navigation to fail in case it was "about:blank".
  472. if (auto navigable = this->navigable(); navigable && navigable->container() && !navigable->container()->content_navigable_initialized()) {
  473. HTML::main_thread_event_loop().spin_processing_tasks_with_source_until(HTML::Task::Source::NavigationAndTraversal, [navigable_container = navigable->container()] {
  474. return navigable_container->content_navigable_initialized();
  475. });
  476. }
  477. // 1. If document is an XML document, then throw an "InvalidStateError" DOMException exception.
  478. if (m_type == Type::XML)
  479. return WebIDL::InvalidStateError::create(realm(), "open() called on XML document."_fly_string);
  480. // 2. If document's throw-on-dynamic-markup-insertion counter is greater than 0, then throw an "InvalidStateError" DOMException.
  481. if (m_throw_on_dynamic_markup_insertion_counter > 0)
  482. return WebIDL::InvalidStateError::create(realm(), "throw-on-dynamic-markup-insertion-counter greater than zero."_fly_string);
  483. // FIXME: 3. Let entryDocument be the entry global object's associated Document.
  484. auto& entry_document = *this;
  485. // 4. If document's origin is not same origin to entryDocument's origin, then throw a "SecurityError" DOMException.
  486. if (origin() != entry_document.origin())
  487. return WebIDL::SecurityError::create(realm(), "Document.origin() not the same as entryDocument's."_fly_string);
  488. // 5. If document has an active parser whose script nesting level is greater than 0, then return document.
  489. if (m_parser && m_parser->script_nesting_level() > 0)
  490. return this;
  491. // 6. Similarly, if document's unload counter is greater than 0, then return document.
  492. if (m_unload_counter > 0)
  493. return this;
  494. // 7. If document's active parser was aborted is true, then return document.
  495. if (m_active_parser_was_aborted)
  496. return this;
  497. // 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.
  498. // FIXME: 9. For each shadow-including inclusive descendant node of document, erase all event listeners and handlers given node.
  499. // 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.
  500. // 11. Replace all with null within document, without firing any mutation events.
  501. replace_all(nullptr);
  502. // 12. If document is fully active, then:
  503. if (is_fully_active()) {
  504. // 1. Let newURL be a copy of entryDocument's URL.
  505. auto new_url = entry_document.url();
  506. // 2. If entryDocument is not document, then set newURL's fragment to null.
  507. if (&entry_document != this)
  508. new_url.set_fragment({});
  509. // FIXME: 3. Run the URL and history update steps with document and newURL.
  510. }
  511. // 13. Set document's is initial about:blank to false.
  512. set_is_initial_about_blank(false);
  513. // FIXME: 14. If document's iframe load in progress flag is set, then set document's mute iframe load flag.
  514. // 15. Set document to no-quirks mode.
  515. set_quirks_mode(QuirksMode::No);
  516. // 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.
  517. m_parser = HTML::HTMLParser::create_for_scripting(*this);
  518. // 17. Set the insertion point to point at just before the end of the input stream (which at this point will be empty).
  519. m_parser->tokenizer().update_insertion_point();
  520. // 18. Update the current document readiness of document to "loading".
  521. update_readiness(HTML::DocumentReadyState::Loading);
  522. // 19. Return document.
  523. return this;
  524. }
  525. // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-document-open-window
  526. WebIDL::ExceptionOr<JS::GCPtr<HTML::WindowProxy>> Document::open(StringView url, StringView name, StringView features)
  527. {
  528. // 1. If this is not fully active, then throw an "InvalidAccessError" DOMException exception.
  529. if (!is_fully_active())
  530. return WebIDL::InvalidAccessError::create(realm(), "Cannot perform open on a document that isn't fully active."_fly_string);
  531. // 2. Return the result of running the window open steps with url, name, and features.
  532. return window()->open_impl(url, name, features);
  533. }
  534. // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#closing-the-input-stream
  535. WebIDL::ExceptionOr<void> Document::close()
  536. {
  537. // 1. If document is an XML document, then throw an "InvalidStateError" DOMException exception.
  538. if (m_type == Type::XML)
  539. return WebIDL::InvalidStateError::create(realm(), "close() called on XML document."_fly_string);
  540. // 2. If document's throw-on-dynamic-markup-insertion counter is greater than 0, then throw an "InvalidStateError" DOMException.
  541. if (m_throw_on_dynamic_markup_insertion_counter > 0)
  542. return WebIDL::InvalidStateError::create(realm(), "throw-on-dynamic-markup-insertion-counter greater than zero."_fly_string);
  543. // 3. If there is no script-created parser associated with the document, then return.
  544. if (!m_parser)
  545. return {};
  546. // FIXME: 4. Insert an explicit "EOF" character at the end of the parser's input stream.
  547. m_parser->tokenizer().insert_eof();
  548. // 5. If there is a pending parsing-blocking script, then return.
  549. if (pending_parsing_blocking_script())
  550. return {};
  551. // 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.
  552. m_parser->run();
  553. return {};
  554. }
  555. HTML::Origin Document::origin() const
  556. {
  557. return m_origin;
  558. }
  559. void Document::set_origin(HTML::Origin const& origin)
  560. {
  561. m_origin = origin;
  562. }
  563. void Document::schedule_style_update()
  564. {
  565. if (!browsing_context())
  566. return;
  567. // NOTE: Update of the style is a step in HTML event loop processing.
  568. HTML::main_thread_event_loop().schedule();
  569. }
  570. void Document::schedule_layout_update()
  571. {
  572. if (!browsing_context())
  573. return;
  574. // NOTE: Update of the layout is a step in HTML event loop processing.
  575. HTML::main_thread_event_loop().schedule();
  576. }
  577. bool Document::is_child_allowed(Node const& node) const
  578. {
  579. switch (node.type()) {
  580. case NodeType::DOCUMENT_NODE:
  581. case NodeType::TEXT_NODE:
  582. return false;
  583. case NodeType::COMMENT_NODE:
  584. return true;
  585. case NodeType::DOCUMENT_TYPE_NODE:
  586. return !first_child_of_type<DocumentType>();
  587. case NodeType::ELEMENT_NODE:
  588. return !first_child_of_type<Element>();
  589. default:
  590. return false;
  591. }
  592. }
  593. Element* Document::document_element()
  594. {
  595. return first_child_of_type<Element>();
  596. }
  597. Element const* Document::document_element() const
  598. {
  599. return first_child_of_type<Element>();
  600. }
  601. // https://html.spec.whatwg.org/multipage/dom.html#the-html-element-2
  602. HTML::HTMLHtmlElement* Document::html_element()
  603. {
  604. // The html element of a document is its document element, if it's an html element, and null otherwise.
  605. auto* html = document_element();
  606. if (is<HTML::HTMLHtmlElement>(html))
  607. return verify_cast<HTML::HTMLHtmlElement>(html);
  608. return nullptr;
  609. }
  610. // https://html.spec.whatwg.org/multipage/dom.html#the-head-element-2
  611. HTML::HTMLHeadElement* Document::head()
  612. {
  613. // The head element of a document is the first head element that is a child of the html element, if there is one,
  614. // or null otherwise.
  615. auto* html = html_element();
  616. if (!html)
  617. return nullptr;
  618. return html->first_child_of_type<HTML::HTMLHeadElement>();
  619. }
  620. // https://html.spec.whatwg.org/multipage/dom.html#the-title-element-2
  621. JS::GCPtr<HTML::HTMLTitleElement> Document::title_element()
  622. {
  623. // The title element of a document is the first title element in the document (in tree order), if there is one, or
  624. // null otherwise.
  625. JS::GCPtr<HTML::HTMLTitleElement> title_element = nullptr;
  626. for_each_in_subtree_of_type<HTML::HTMLTitleElement>([&](auto& title_element_in_tree) {
  627. title_element = title_element_in_tree;
  628. return TraversalDecision::Break;
  629. });
  630. return title_element;
  631. }
  632. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-dir
  633. StringView Document::dir() const
  634. {
  635. // The dir IDL attribute on Document objects must reflect the dir content attribute of the html
  636. // element, if any, limited to only known values. If there is no such element, then the
  637. // attribute must return the empty string and do nothing on setting.
  638. if (auto html = html_element())
  639. return html->dir();
  640. return ""sv;
  641. }
  642. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-dir
  643. void Document::set_dir(String const& dir)
  644. {
  645. // The dir IDL attribute on Document objects must reflect the dir content attribute of the html
  646. // element, if any, limited to only known values. If there is no such element, then the
  647. // attribute must return the empty string and do nothing on setting.
  648. if (auto html = html_element())
  649. html->set_dir(dir);
  650. }
  651. HTML::HTMLElement* Document::body()
  652. {
  653. auto* html = html_element();
  654. if (!html)
  655. return nullptr;
  656. auto* first_body = html->first_child_of_type<HTML::HTMLBodyElement>();
  657. if (first_body)
  658. return first_body;
  659. auto* first_frameset = html->first_child_of_type<HTML::HTMLFrameSetElement>();
  660. if (first_frameset)
  661. return first_frameset;
  662. return nullptr;
  663. }
  664. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-body
  665. WebIDL::ExceptionOr<void> Document::set_body(HTML::HTMLElement* new_body)
  666. {
  667. if (!is<HTML::HTMLBodyElement>(new_body) && !is<HTML::HTMLFrameSetElement>(new_body))
  668. return WebIDL::HierarchyRequestError::create(realm(), "Invalid document body element, must be 'body' or 'frameset'"_fly_string);
  669. auto* existing_body = body();
  670. if (existing_body) {
  671. (void)TRY(existing_body->parent()->replace_child(*new_body, *existing_body));
  672. return {};
  673. }
  674. auto* document_element = this->document_element();
  675. if (!document_element)
  676. return WebIDL::HierarchyRequestError::create(realm(), "Missing document element"_fly_string);
  677. (void)TRY(document_element->append_child(*new_body));
  678. return {};
  679. }
  680. // https://html.spec.whatwg.org/multipage/dom.html#document.title
  681. String Document::title() const
  682. {
  683. String value;
  684. // 1. If the document element is an SVG svg element, then let value be the child text content of the first SVG title
  685. // element that is a child of the document element.
  686. if (auto const* document_element = this->document_element(); is<SVG::SVGElement>(document_element)) {
  687. if (auto const* title_element = document_element->first_child_of_type<SVG::SVGTitleElement>())
  688. value = title_element->child_text_content();
  689. }
  690. // 2. Otherwise, let value be the child text content of the title element, or the empty string if the title element
  691. // is null.
  692. else if (auto title_element = this->title_element()) {
  693. value = title_element->text_content().value_or(String {});
  694. }
  695. // 3. Strip and collapse ASCII whitespace in value.
  696. auto title = Infra::strip_and_collapse_whitespace(value).release_value_but_fixme_should_propagate_errors();
  697. // 4. Return value.
  698. return title;
  699. }
  700. // https://html.spec.whatwg.org/multipage/dom.html#document.title
  701. WebIDL::ExceptionOr<void> Document::set_title(String const& title)
  702. {
  703. auto* document_element = this->document_element();
  704. // -> If the document element is an SVG svg element
  705. if (is<SVG::SVGElement>(document_element)) {
  706. JS::GCPtr<Element> element;
  707. // 1. If there is an SVG title element that is a child of the document element, let element be the first such
  708. // element.
  709. if (auto* title_element = document_element->first_child_of_type<SVG::SVGTitleElement>()) {
  710. element = title_element;
  711. }
  712. // 2. Otherwise:
  713. else {
  714. // 1. Let element be the result of creating an element given the document element's node document, title,
  715. // and the SVG namespace.
  716. element = TRY(DOM::create_element(*this, HTML::TagNames::title, Namespace::SVG));
  717. // 2. Insert element as the first child of the document element.
  718. document_element->insert_before(*element, nullptr);
  719. }
  720. // 3. String replace all with the given value within element.
  721. element->string_replace_all(title);
  722. }
  723. // -> If the document element is in the HTML namespace
  724. else if (document_element && document_element->namespace_uri() == Namespace::HTML) {
  725. auto title_element = this->title_element();
  726. auto* head_element = this->head();
  727. // 1. If the title element is null and the head element is null, then return.
  728. if (title_element == nullptr && head_element == nullptr)
  729. return {};
  730. JS::GCPtr<Element> element;
  731. // 2. If the title element is non-null, let element be the title element.
  732. if (title_element) {
  733. element = title_element;
  734. }
  735. // 3. Otherwise:
  736. else {
  737. // 1. Let element be the result of creating an element given the document element's node document, title,
  738. // and the HTML namespace.
  739. element = TRY(DOM::create_element(*this, HTML::TagNames::title, Namespace::HTML));
  740. // 2. Append element to the head element.
  741. TRY(head_element->append_child(*element));
  742. }
  743. // 4. String replace all with the given value within element.
  744. element->string_replace_all(title);
  745. }
  746. // -> Otherwise
  747. else {
  748. // Do nothing.
  749. return {};
  750. }
  751. if (browsing_context() == &page().top_level_browsing_context())
  752. page().client().page_did_change_title(title.to_byte_string());
  753. return {};
  754. }
  755. void Document::tear_down_layout_tree()
  756. {
  757. m_layout_root = nullptr;
  758. m_paintable = nullptr;
  759. }
  760. Color Document::background_color() const
  761. {
  762. // CSS2 says we should use the HTML element's background color unless it's transparent...
  763. if (auto* html_element = this->html_element(); html_element && html_element->layout_node()) {
  764. auto color = html_element->layout_node()->computed_values().background_color();
  765. if (color.alpha())
  766. return color;
  767. }
  768. // ...in which case we use the BODY element's background color.
  769. if (auto* body_element = body(); body_element && body_element->layout_node()) {
  770. auto color = body_element->layout_node()->computed_values().background_color();
  771. return color;
  772. }
  773. // By default, the document is transparent.
  774. // The outermost canvas is colored by the PageHost.
  775. return Color::Transparent;
  776. }
  777. Vector<CSS::BackgroundLayerData> const* Document::background_layers() const
  778. {
  779. auto* body_element = body();
  780. if (!body_element)
  781. return {};
  782. auto body_layout_node = body_element->layout_node();
  783. if (!body_layout_node)
  784. return {};
  785. return &body_layout_node->background_layers();
  786. }
  787. void Document::update_base_element(Badge<HTML::HTMLBaseElement>)
  788. {
  789. JS::GCPtr<HTML::HTMLBaseElement const> base_element;
  790. for_each_in_subtree_of_type<HTML::HTMLBaseElement>([&base_element](HTML::HTMLBaseElement const& base_element_in_tree) {
  791. if (base_element_in_tree.has_attribute(HTML::AttributeNames::href)) {
  792. base_element = &base_element_in_tree;
  793. return TraversalDecision::Break;
  794. }
  795. return TraversalDecision::Continue;
  796. });
  797. m_first_base_element_with_href_in_tree_order = base_element;
  798. }
  799. JS::GCPtr<HTML::HTMLBaseElement const> Document::first_base_element_with_href_in_tree_order() const
  800. {
  801. return m_first_base_element_with_href_in_tree_order;
  802. }
  803. // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fallback-base-url
  804. URL::URL Document::fallback_base_url() const
  805. {
  806. // 1. If document is an iframe srcdoc document, then:
  807. if (HTML::url_matches_about_srcdoc(m_url)) {
  808. // 1. Assert: document's about base URL is non-null.
  809. VERIFY(m_about_base_url.has_value());
  810. // 2. Return document's about base URL.
  811. return m_about_base_url.value();
  812. }
  813. // 2. If document's URL matches about:blank and document's about base URL is non-null, then return document's about base URL.
  814. if (HTML::url_matches_about_blank(m_url) && m_about_base_url.has_value())
  815. return m_about_base_url.value();
  816. // 3. Return document's URL.
  817. return m_url;
  818. }
  819. // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#document-base-url
  820. URL::URL Document::base_url() const
  821. {
  822. // 1. If there is no base element that has an href attribute in the Document, then return the Document's fallback base URL.
  823. auto base_element = first_base_element_with_href_in_tree_order();
  824. if (!base_element)
  825. return fallback_base_url();
  826. // 2. Otherwise, return the frozen base URL of the first base element in the Document that has an href attribute, in tree order.
  827. return base_element->frozen_base_url();
  828. }
  829. // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#parse-a-url
  830. URL::URL Document::parse_url(StringView url) const
  831. {
  832. // 1. Let baseURL be environment's base URL, if environment is a Document object; otherwise environment's API base URL.
  833. auto base_url = this->base_url();
  834. // 2. Return the result of applying the URL parser to url, with baseURL.
  835. return DOMURL::parse(url, base_url);
  836. }
  837. void Document::set_needs_layout()
  838. {
  839. if (m_needs_layout)
  840. return;
  841. m_needs_layout = true;
  842. schedule_layout_update();
  843. }
  844. void Document::invalidate_layout()
  845. {
  846. tear_down_layout_tree();
  847. schedule_layout_update();
  848. }
  849. static void propagate_scrollbar_width_to_viewport(Element& root_element, Layout::Viewport& viewport)
  850. {
  851. // https://drafts.csswg.org/css-scrollbars/#scrollbar-width
  852. // UAs must apply the scrollbar-color value set on the root element to the viewport.
  853. auto& viewport_computed_values = viewport.mutable_computed_values();
  854. auto& root_element_computed_values = root_element.layout_node()->computed_values();
  855. viewport_computed_values.set_scrollbar_width(root_element_computed_values.scrollbar_width());
  856. }
  857. static void propagate_overflow_to_viewport(Element& root_element, Layout::Viewport& viewport)
  858. {
  859. // https://drafts.csswg.org/css-overflow-3/#overflow-propagation
  860. // UAs must apply the overflow-* values set on the root element to the viewport
  861. // when the root element’s display value is not none.
  862. auto overflow_origin_node = root_element.layout_node();
  863. auto& viewport_computed_values = viewport.mutable_computed_values();
  864. // However, when the root element is an [HTML] html element (including XML syntax for HTML)
  865. // whose overflow value is visible (in both axes), and that element has as a child
  866. // a body element whose display value is also not none,
  867. // user agents must instead apply the overflow-* values of the first such child element to the viewport.
  868. if (root_element.is_html_html_element()) {
  869. auto root_element_layout_node = root_element.layout_node();
  870. auto& root_element_computed_values = root_element_layout_node->mutable_computed_values();
  871. if (root_element_computed_values.overflow_x() == CSS::Overflow::Visible && root_element_computed_values.overflow_y() == CSS::Overflow::Visible) {
  872. auto* body_element = root_element.first_child_of_type<HTML::HTMLBodyElement>();
  873. if (body_element && body_element->layout_node())
  874. overflow_origin_node = body_element->layout_node();
  875. }
  876. }
  877. // NOTE: This is where we assign the chosen overflow values to the viewport.
  878. auto& overflow_origin_computed_values = overflow_origin_node->mutable_computed_values();
  879. viewport_computed_values.set_overflow_x(overflow_origin_computed_values.overflow_x());
  880. viewport_computed_values.set_overflow_y(overflow_origin_computed_values.overflow_y());
  881. // The element from which the value is propagated must then have a used overflow value of visible.
  882. overflow_origin_computed_values.set_overflow_x(CSS::Overflow::Visible);
  883. overflow_origin_computed_values.set_overflow_y(CSS::Overflow::Visible);
  884. }
  885. void Document::update_layout()
  886. {
  887. if (!is_active())
  888. return;
  889. // NOTE: If our parent document needs a relayout, we must do that *first*.
  890. // This is necessary as the parent layout may cause our viewport to change.
  891. auto navigable = this->navigable();
  892. if (navigable && navigable->container())
  893. navigable->container()->document().update_layout();
  894. update_style();
  895. if (!m_needs_layout && m_layout_root)
  896. return;
  897. // NOTE: If this is a document hosting <template> contents, layout is unnecessary.
  898. if (m_created_for_appropriate_template_contents)
  899. return;
  900. if (!navigable)
  901. return;
  902. auto* document_element = this->document_element();
  903. auto viewport_rect = this->viewport_rect();
  904. if (!m_layout_root) {
  905. Layout::TreeBuilder tree_builder;
  906. m_layout_root = verify_cast<Layout::Viewport>(*tree_builder.build(*this));
  907. if (document_element && document_element->layout_node()) {
  908. propagate_overflow_to_viewport(*document_element, *m_layout_root);
  909. propagate_scrollbar_width_to_viewport(*document_element, *m_layout_root);
  910. }
  911. }
  912. Layout::LayoutState layout_state;
  913. {
  914. Layout::BlockFormattingContext root_formatting_context(layout_state, *m_layout_root, nullptr);
  915. auto& viewport = static_cast<Layout::Viewport&>(*m_layout_root);
  916. auto& viewport_state = layout_state.get_mutable(viewport);
  917. viewport_state.set_content_width(viewport_rect.width());
  918. viewport_state.set_content_height(viewport_rect.height());
  919. if (document_element && document_element->layout_node()) {
  920. auto& icb_state = layout_state.get_mutable(verify_cast<Layout::NodeWithStyleAndBoxModelMetrics>(*document_element->layout_node()));
  921. icb_state.set_content_width(viewport_rect.width());
  922. }
  923. root_formatting_context.run(
  924. *m_layout_root,
  925. Layout::LayoutMode::Normal,
  926. Layout::AvailableSpace(
  927. Layout::AvailableSize::make_definite(viewport_rect.width()),
  928. Layout::AvailableSize::make_definite(viewport_rect.height())));
  929. }
  930. layout_state.commit(*m_layout_root);
  931. // Broadcast the current viewport rect to any new paintables, so they know whether they're visible or not.
  932. inform_all_viewport_clients_about_the_current_viewport_rect();
  933. navigable->set_needs_display();
  934. set_needs_to_resolve_paint_only_properties();
  935. if (navigable->is_traversable()) {
  936. // NOTE: The assignment of scroll frames only needs to occur for traversables because they take care of all
  937. // nested navigable documents.
  938. paintable()->assign_scroll_frames();
  939. paintable()->assign_clip_frames();
  940. page().client().page_did_layout();
  941. }
  942. paintable()->recompute_selection_states();
  943. m_needs_layout = false;
  944. // Scrolling by zero offset will clamp scroll offset back to valid range if it was out of bounds
  945. // after the viewport size change.
  946. if (auto window = this->window())
  947. window->scroll_by(0, 0);
  948. }
  949. [[nodiscard]] static CSS::RequiredInvalidationAfterStyleChange update_style_recursively(Node& node, CSS::StyleComputer& style_computer)
  950. {
  951. bool const needs_full_style_update = node.document().needs_full_style_update();
  952. CSS::RequiredInvalidationAfterStyleChange invalidation;
  953. if (node.is_element())
  954. style_computer.push_ancestor(static_cast<Element const&>(node));
  955. // NOTE: If the current node has `display:none`, we can disregard all invalidation
  956. // caused by its children, as they will not be rendered anyway.
  957. // We will still recompute style for the children, though.
  958. bool is_display_none = false;
  959. if (is<Element>(node)) {
  960. invalidation |= static_cast<Element&>(node).recompute_style();
  961. is_display_none = static_cast<Element&>(node).computed_css_values()->display().is_none();
  962. }
  963. node.set_needs_style_update(false);
  964. if (needs_full_style_update || node.child_needs_style_update()) {
  965. if (node.is_element()) {
  966. if (auto shadow_root = static_cast<DOM::Element&>(node).shadow_root()) {
  967. if (needs_full_style_update || shadow_root->needs_style_update() || shadow_root->child_needs_style_update()) {
  968. auto subtree_invalidation = update_style_recursively(*shadow_root, style_computer);
  969. if (!is_display_none)
  970. invalidation |= subtree_invalidation;
  971. }
  972. }
  973. }
  974. node.for_each_child([&](auto& child) {
  975. if (needs_full_style_update || child.needs_style_update() || child.child_needs_style_update()) {
  976. auto subtree_invalidation = update_style_recursively(child, style_computer);
  977. if (!is_display_none)
  978. invalidation |= subtree_invalidation;
  979. }
  980. return IterationDecision::Continue;
  981. });
  982. }
  983. node.set_child_needs_style_update(false);
  984. if (node.is_element())
  985. style_computer.pop_ancestor(static_cast<Element const&>(node));
  986. return invalidation;
  987. }
  988. void Document::update_style()
  989. {
  990. if (!browsing_context())
  991. return;
  992. update_animated_style_if_needed();
  993. // Associated with each top-level browsing context is a current transition generation that is incremented on each
  994. // style change event. [CSS-Transitions-2]
  995. m_transition_generation++;
  996. if (!needs_full_style_update() && !needs_style_update() && !child_needs_style_update())
  997. return;
  998. // NOTE: If this is a document hosting <template> contents, style update is unnecessary.
  999. if (m_created_for_appropriate_template_contents)
  1000. return;
  1001. // Fetch the viewport rect once, instead of repeatedly, during style computation.
  1002. style_computer().set_viewport_rect({}, viewport_rect());
  1003. evaluate_media_rules();
  1004. style_computer().reset_ancestor_filter();
  1005. auto invalidation = update_style_recursively(*this, style_computer());
  1006. if (invalidation.rebuild_layout_tree) {
  1007. invalidate_layout();
  1008. } else {
  1009. if (invalidation.relayout)
  1010. set_needs_layout();
  1011. if (invalidation.rebuild_stacking_context_tree)
  1012. invalidate_stacking_context_tree();
  1013. }
  1014. m_needs_full_style_update = false;
  1015. }
  1016. void Document::update_animated_style_if_needed()
  1017. {
  1018. if (!m_needs_animated_style_update)
  1019. return;
  1020. for (auto& timeline : m_associated_animation_timelines) {
  1021. for (auto& animation : timeline->associated_animations()) {
  1022. if (auto effect = animation->effect(); effect && effect->target())
  1023. effect->target()->reset_animated_css_properties();
  1024. }
  1025. for (auto& animation : timeline->associated_animations()) {
  1026. if (auto effect = animation->effect())
  1027. effect->update_style_properties();
  1028. }
  1029. }
  1030. m_needs_animated_style_update = false;
  1031. }
  1032. void Document::update_paint_and_hit_testing_properties_if_needed()
  1033. {
  1034. if (!m_needs_to_resolve_paint_only_properties)
  1035. return;
  1036. m_needs_to_resolve_paint_only_properties = false;
  1037. if (auto* paintable = this->paintable())
  1038. paintable->resolve_paint_only_properties();
  1039. }
  1040. void Document::set_normal_link_color(Color color)
  1041. {
  1042. m_normal_link_color = color;
  1043. }
  1044. void Document::set_active_link_color(Color color)
  1045. {
  1046. m_active_link_color = color;
  1047. }
  1048. void Document::set_visited_link_color(Color color)
  1049. {
  1050. m_visited_link_color = color;
  1051. }
  1052. Layout::Viewport const* Document::layout_node() const
  1053. {
  1054. return static_cast<Layout::Viewport const*>(Node::layout_node());
  1055. }
  1056. Layout::Viewport* Document::layout_node()
  1057. {
  1058. return static_cast<Layout::Viewport*>(Node::layout_node());
  1059. }
  1060. void Document::set_inspected_node(Node* node, Optional<CSS::Selector::PseudoElement::Type> pseudo_element)
  1061. {
  1062. if (m_inspected_node.ptr() == node && m_inspected_pseudo_element == pseudo_element)
  1063. return;
  1064. if (auto layout_node = inspected_layout_node(); layout_node && layout_node->paintable())
  1065. layout_node->paintable()->set_needs_display();
  1066. m_inspected_node = node;
  1067. m_inspected_pseudo_element = pseudo_element;
  1068. if (auto layout_node = inspected_layout_node(); layout_node && layout_node->paintable())
  1069. layout_node->paintable()->set_needs_display();
  1070. }
  1071. Layout::Node* Document::inspected_layout_node()
  1072. {
  1073. if (!m_inspected_node)
  1074. return nullptr;
  1075. if (!m_inspected_pseudo_element.has_value() || !m_inspected_node->is_element())
  1076. return m_inspected_node->layout_node();
  1077. auto& element = static_cast<Element&>(*m_inspected_node);
  1078. return element.get_pseudo_element_node(m_inspected_pseudo_element.value());
  1079. }
  1080. static Node* find_common_ancestor(Node* a, Node* b)
  1081. {
  1082. if (!a || !b)
  1083. return nullptr;
  1084. if (a == b)
  1085. return a;
  1086. HashTable<Node*> ancestors;
  1087. for (auto* node = a; node; node = node->parent_or_shadow_host())
  1088. ancestors.set(node);
  1089. for (auto* node = b; node; node = node->parent_or_shadow_host()) {
  1090. if (ancestors.contains(node))
  1091. return node;
  1092. }
  1093. return nullptr;
  1094. }
  1095. void Document::set_hovered_node(Node* node)
  1096. {
  1097. if (m_hovered_node.ptr() == node)
  1098. return;
  1099. JS::GCPtr<Node> old_hovered_node = move(m_hovered_node);
  1100. m_hovered_node = node;
  1101. auto* common_ancestor = find_common_ancestor(old_hovered_node, m_hovered_node);
  1102. if (common_ancestor)
  1103. common_ancestor->invalidate_style();
  1104. else
  1105. invalidate_style();
  1106. // https://w3c.github.io/uievents/#mouseout
  1107. if (old_hovered_node && old_hovered_node != m_hovered_node) {
  1108. UIEvents::MouseEventInit mouse_event_init {};
  1109. mouse_event_init.related_target = m_hovered_node;
  1110. auto event = UIEvents::MouseEvent::create(realm(), UIEvents::EventNames::mouseout, mouse_event_init);
  1111. old_hovered_node->dispatch_event(event);
  1112. }
  1113. // https://w3c.github.io/uievents/#mouseleave
  1114. if (old_hovered_node && (!m_hovered_node || !m_hovered_node->is_descendant_of(*old_hovered_node))) {
  1115. // FIXME: Check if we need to dispatch these events in a specific order.
  1116. for (auto target = old_hovered_node; target && target.ptr() != common_ancestor; target = target->parent()) {
  1117. // FIXME: Populate the event with mouse coordinates, etc.
  1118. UIEvents::MouseEventInit mouse_event_init {};
  1119. mouse_event_init.related_target = m_hovered_node;
  1120. target->dispatch_event(UIEvents::MouseEvent::create(realm(), UIEvents::EventNames::mouseleave, mouse_event_init));
  1121. }
  1122. }
  1123. // https://w3c.github.io/uievents/#mouseover
  1124. if (m_hovered_node && m_hovered_node != old_hovered_node) {
  1125. UIEvents::MouseEventInit mouse_event_init {};
  1126. mouse_event_init.related_target = old_hovered_node;
  1127. auto event = UIEvents::MouseEvent::create(realm(), UIEvents::EventNames::mouseover, mouse_event_init);
  1128. m_hovered_node->dispatch_event(event);
  1129. }
  1130. // https://w3c.github.io/uievents/#mouseenter
  1131. if (m_hovered_node && (!old_hovered_node || !m_hovered_node->is_ancestor_of(*old_hovered_node))) {
  1132. // FIXME: Check if we need to dispatch these events in a specific order.
  1133. for (auto target = m_hovered_node; target && target.ptr() != common_ancestor; target = target->parent()) {
  1134. // FIXME: Populate the event with mouse coordinates, etc.
  1135. UIEvents::MouseEventInit mouse_event_init {};
  1136. mouse_event_init.related_target = old_hovered_node;
  1137. target->dispatch_event(UIEvents::MouseEvent::create(realm(), UIEvents::EventNames::mouseenter, mouse_event_init));
  1138. }
  1139. }
  1140. }
  1141. JS::NonnullGCPtr<HTMLCollection> Document::get_elements_by_name(FlyString const& name)
  1142. {
  1143. return HTMLCollection::create(*this, HTMLCollection::Scope::Descendants, [name](Element const& element) {
  1144. return element.name() == name;
  1145. });
  1146. }
  1147. JS::NonnullGCPtr<HTMLCollection> Document::get_elements_by_class_name(StringView class_names)
  1148. {
  1149. Vector<FlyString> list_of_class_names;
  1150. for (auto& name : class_names.split_view(' ')) {
  1151. list_of_class_names.append(FlyString::from_utf8(name).release_value_but_fixme_should_propagate_errors());
  1152. }
  1153. 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) {
  1154. for (auto& name : list_of_class_names) {
  1155. if (!element.has_class(name, quirks_mode ? CaseSensitivity::CaseInsensitive : CaseSensitivity::CaseSensitive))
  1156. return false;
  1157. }
  1158. return true;
  1159. });
  1160. }
  1161. // https://html.spec.whatwg.org/multipage/obsolete.html#dom-document-applets
  1162. JS::NonnullGCPtr<HTMLCollection> Document::applets()
  1163. {
  1164. if (!m_applets)
  1165. m_applets = HTMLCollection::create(*this, HTMLCollection::Scope::Descendants, [](auto&) { return false; });
  1166. return *m_applets;
  1167. }
  1168. // https://html.spec.whatwg.org/multipage/obsolete.html#dom-document-anchors
  1169. JS::NonnullGCPtr<HTMLCollection> Document::anchors()
  1170. {
  1171. if (!m_anchors) {
  1172. m_anchors = HTMLCollection::create(*this, HTMLCollection::Scope::Descendants, [](Element const& element) {
  1173. return is<HTML::HTMLAnchorElement>(element) && element.name().has_value();
  1174. });
  1175. }
  1176. return *m_anchors;
  1177. }
  1178. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-images
  1179. JS::NonnullGCPtr<HTMLCollection> Document::images()
  1180. {
  1181. if (!m_images) {
  1182. m_images = HTMLCollection::create(*this, HTMLCollection::Scope::Descendants, [](Element const& element) {
  1183. return is<HTML::HTMLImageElement>(element);
  1184. });
  1185. }
  1186. return *m_images;
  1187. }
  1188. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-embeds
  1189. JS::NonnullGCPtr<HTMLCollection> Document::embeds()
  1190. {
  1191. if (!m_embeds) {
  1192. m_embeds = HTMLCollection::create(*this, HTMLCollection::Scope::Descendants, [](Element const& element) {
  1193. return is<HTML::HTMLEmbedElement>(element);
  1194. });
  1195. }
  1196. return *m_embeds;
  1197. }
  1198. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-plugins
  1199. JS::NonnullGCPtr<HTMLCollection> Document::plugins()
  1200. {
  1201. return embeds();
  1202. }
  1203. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-links
  1204. JS::NonnullGCPtr<HTMLCollection> Document::links()
  1205. {
  1206. if (!m_links) {
  1207. m_links = HTMLCollection::create(*this, HTMLCollection::Scope::Descendants, [](Element const& element) {
  1208. return (is<HTML::HTMLAnchorElement>(element) || is<HTML::HTMLAreaElement>(element)) && element.has_attribute(HTML::AttributeNames::href);
  1209. });
  1210. }
  1211. return *m_links;
  1212. }
  1213. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-forms
  1214. JS::NonnullGCPtr<HTMLCollection> Document::forms()
  1215. {
  1216. if (!m_forms) {
  1217. m_forms = HTMLCollection::create(*this, HTMLCollection::Scope::Descendants, [](Element const& element) {
  1218. return is<HTML::HTMLFormElement>(element);
  1219. });
  1220. }
  1221. return *m_forms;
  1222. }
  1223. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-scripts
  1224. JS::NonnullGCPtr<HTMLCollection> Document::scripts()
  1225. {
  1226. if (!m_scripts) {
  1227. m_scripts = HTMLCollection::create(*this, HTMLCollection::Scope::Descendants, [](Element const& element) {
  1228. return is<HTML::HTMLScriptElement>(element);
  1229. });
  1230. }
  1231. return *m_scripts;
  1232. }
  1233. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-all
  1234. JS::NonnullGCPtr<HTML::HTMLAllCollection> Document::all()
  1235. {
  1236. if (!m_all) {
  1237. // The all attribute must return an HTMLAllCollection rooted at the Document node, whose filter matches all elements.
  1238. m_all = HTML::HTMLAllCollection::create(*this, HTML::HTMLAllCollection::Scope::Descendants, [](Element const&) {
  1239. return true;
  1240. });
  1241. }
  1242. return *m_all;
  1243. }
  1244. // https://drafts.csswg.org/css-font-loading/#font-source
  1245. JS::NonnullGCPtr<CSS::FontFaceSet> Document::fonts()
  1246. {
  1247. if (!m_fonts)
  1248. m_fonts = CSS::FontFaceSet::create(realm());
  1249. return *m_fonts;
  1250. }
  1251. // https://html.spec.whatwg.org/multipage/obsolete.html#dom-document-clear
  1252. void Document::clear()
  1253. {
  1254. // Do nothing
  1255. }
  1256. // https://html.spec.whatwg.org/multipage/obsolete.html#dom-document-captureevents
  1257. void Document::capture_events()
  1258. {
  1259. // Do nothing
  1260. }
  1261. // https://html.spec.whatwg.org/multipage/obsolete.html#dom-document-releaseevents
  1262. void Document::release_events()
  1263. {
  1264. // Do nothing
  1265. }
  1266. Color Document::normal_link_color() const
  1267. {
  1268. if (m_normal_link_color.has_value())
  1269. return m_normal_link_color.value();
  1270. return CSS::SystemColor::link_text();
  1271. }
  1272. Color Document::active_link_color() const
  1273. {
  1274. if (m_active_link_color.has_value())
  1275. return m_active_link_color.value();
  1276. return CSS::SystemColor::active_text();
  1277. }
  1278. Color Document::visited_link_color() const
  1279. {
  1280. if (m_visited_link_color.has_value())
  1281. return m_visited_link_color.value();
  1282. return CSS::SystemColor::visited_text();
  1283. }
  1284. // https://html.spec.whatwg.org/multipage/webappapis.html#relevant-settings-object
  1285. HTML::EnvironmentSettingsObject& Document::relevant_settings_object() const
  1286. {
  1287. // Then, the relevant settings object for a platform object o is the environment settings object of the relevant Realm for o.
  1288. return Bindings::host_defined_environment_settings_object(realm());
  1289. }
  1290. // https://dom.spec.whatwg.org/#dom-document-createelement
  1291. WebIDL::ExceptionOr<JS::NonnullGCPtr<Element>> Document::create_element(String const& a_local_name, Variant<String, ElementCreationOptions> const& options)
  1292. {
  1293. auto local_name = a_local_name.to_byte_string();
  1294. // 1. If localName does not match the Name production, then throw an "InvalidCharacterError" DOMException.
  1295. if (!is_valid_name(a_local_name))
  1296. return WebIDL::InvalidCharacterError::create(realm(), "Invalid character in tag name."_fly_string);
  1297. // 2. If this is an HTML document, then set localName to localName in ASCII lowercase.
  1298. if (document_type() == Type::HTML)
  1299. local_name = local_name.to_lowercase();
  1300. // 3. Let is be null.
  1301. Optional<String> is_value;
  1302. // 4. If options is a dictionary and options["is"] exists, then set is to it.
  1303. if (options.has<ElementCreationOptions>()) {
  1304. auto const& element_creation_options = options.get<ElementCreationOptions>();
  1305. if (element_creation_options.is.has_value())
  1306. is_value = element_creation_options.is.value();
  1307. }
  1308. // 5. Let namespace be the HTML namespace, if this is an HTML document or this’s content type is "application/xhtml+xml"; otherwise null.
  1309. Optional<FlyString> namespace_;
  1310. if (document_type() == Type::HTML || content_type() == "application/xhtml+xml"sv)
  1311. namespace_ = Namespace::HTML;
  1312. // 6. Return the result of creating an element given this, localName, namespace, null, is, and with the synchronous custom elements flag set.
  1313. return TRY(DOM::create_element(*this, MUST(FlyString::from_deprecated_fly_string(local_name)), move(namespace_), {}, move(is_value), true));
  1314. }
  1315. // https://dom.spec.whatwg.org/#dom-document-createelementns
  1316. // https://dom.spec.whatwg.org/#internal-createelementns-steps
  1317. WebIDL::ExceptionOr<JS::NonnullGCPtr<Element>> Document::create_element_ns(Optional<FlyString> const& namespace_, String const& qualified_name, Variant<String, ElementCreationOptions> const& options)
  1318. {
  1319. // 1. Let namespace, prefix, and localName be the result of passing namespace and qualifiedName to validate and extract.
  1320. auto extracted_qualified_name = TRY(validate_and_extract(realm(), namespace_, qualified_name));
  1321. // 2. Let is be null.
  1322. Optional<String> is_value;
  1323. // 3. If options is a dictionary and options["is"] exists, then set is to it.
  1324. if (options.has<ElementCreationOptions>()) {
  1325. auto const& element_creation_options = options.get<ElementCreationOptions>();
  1326. if (element_creation_options.is.has_value())
  1327. is_value = element_creation_options.is.value();
  1328. }
  1329. // 4. Return the result of creating an element given document, localName, namespace, prefix, is, and with the synchronous custom elements flag set.
  1330. return TRY(DOM::create_element(*this, extracted_qualified_name.local_name(), extracted_qualified_name.namespace_(), extracted_qualified_name.prefix(), move(is_value), true));
  1331. }
  1332. JS::NonnullGCPtr<DocumentFragment> Document::create_document_fragment()
  1333. {
  1334. return heap().allocate<DocumentFragment>(realm(), *this);
  1335. }
  1336. JS::NonnullGCPtr<Text> Document::create_text_node(String const& data)
  1337. {
  1338. return heap().allocate<Text>(realm(), *this, data);
  1339. }
  1340. // https://dom.spec.whatwg.org/#dom-document-createcdatasection
  1341. WebIDL::ExceptionOr<JS::NonnullGCPtr<CDATASection>> Document::create_cdata_section(String const& data)
  1342. {
  1343. // 1. If this is an HTML document, then throw a "NotSupportedError" DOMException.
  1344. if (is_html_document())
  1345. return WebIDL::NotSupportedError::create(realm(), "This operation is not supported for HTML documents"_fly_string);
  1346. // 2. If data contains the string "]]>", then throw an "InvalidCharacterError" DOMException.
  1347. if (data.contains("]]>"sv))
  1348. return WebIDL::InvalidCharacterError::create(realm(), "String may not contain ']]>'"_fly_string);
  1349. // 3. Return a new CDATASection node with its data set to data and node document set to this.
  1350. return heap().allocate<CDATASection>(realm(), *this, data);
  1351. }
  1352. JS::NonnullGCPtr<Comment> Document::create_comment(String const& data)
  1353. {
  1354. return heap().allocate<Comment>(realm(), *this, data);
  1355. }
  1356. // https://dom.spec.whatwg.org/#dom-document-createprocessinginstruction
  1357. WebIDL::ExceptionOr<JS::NonnullGCPtr<ProcessingInstruction>> Document::create_processing_instruction(String const& target, String const& data)
  1358. {
  1359. // FIXME: 1. If target does not match the Name production, then throw an "InvalidCharacterError" DOMException.
  1360. // FIXME: 2. If data contains the string "?>", then throw an "InvalidCharacterError" DOMException.
  1361. // 3. Return a new ProcessingInstruction node, with target set to target, data set to data, and node document set to this.
  1362. return heap().allocate<ProcessingInstruction>(realm(), *this, data, target);
  1363. }
  1364. JS::NonnullGCPtr<Range> Document::create_range()
  1365. {
  1366. return Range::create(*this);
  1367. }
  1368. // https://dom.spec.whatwg.org/#dom-document-createevent
  1369. WebIDL::ExceptionOr<JS::NonnullGCPtr<Event>> Document::create_event(StringView interface)
  1370. {
  1371. auto& realm = this->realm();
  1372. // NOTE: This is named event here, since we do step 5 and 6 as soon as possible for each case.
  1373. // 1. Let constructor be null.
  1374. JS::GCPtr<Event> event;
  1375. // 2. If interface is an ASCII case-insensitive match for any of the strings in the first column in the following table,
  1376. // then set constructor to the interface in the second column on the same row as the matching string:
  1377. if (Infra::is_ascii_case_insensitive_match(interface, "beforeunloadevent"sv)) {
  1378. event = Event::create(realm, FlyString {}); // FIXME: Create BeforeUnloadEvent
  1379. } else if (Infra::is_ascii_case_insensitive_match(interface, "compositionevent"sv)) {
  1380. event = Event::create(realm, FlyString {}); // FIXME: Create CompositionEvent
  1381. } else if (Infra::is_ascii_case_insensitive_match(interface, "customevent"sv)) {
  1382. event = CustomEvent::create(realm, FlyString {});
  1383. } else if (Infra::is_ascii_case_insensitive_match(interface, "devicemotionevent"sv)) {
  1384. event = Event::create(realm, FlyString {}); // FIXME: Create DeviceMotionEvent
  1385. } else if (Infra::is_ascii_case_insensitive_match(interface, "deviceorientationevent"sv)) {
  1386. event = Event::create(realm, FlyString {}); // FIXME: Create DeviceOrientationEvent
  1387. } else if (Infra::is_ascii_case_insensitive_match(interface, "dragevent"sv)) {
  1388. event = Event::create(realm, FlyString {}); // FIXME: Create DragEvent
  1389. } else if (Infra::is_ascii_case_insensitive_match(interface, "event"sv)
  1390. || Infra::is_ascii_case_insensitive_match(interface, "events"sv)) {
  1391. event = Event::create(realm, FlyString {});
  1392. } else if (Infra::is_ascii_case_insensitive_match(interface, "focusevent"sv)) {
  1393. event = UIEvents::FocusEvent::create(realm, FlyString {});
  1394. } else if (Infra::is_ascii_case_insensitive_match(interface, "hashchangeevent"sv)) {
  1395. event = HTML::HashChangeEvent::create(realm, FlyString {}, {});
  1396. } else if (Infra::is_ascii_case_insensitive_match(interface, "htmlevents"sv)) {
  1397. event = Event::create(realm, FlyString {});
  1398. } else if (Infra::is_ascii_case_insensitive_match(interface, "keyboardevent"sv)) {
  1399. event = UIEvents::KeyboardEvent::create(realm, String {});
  1400. } else if (Infra::is_ascii_case_insensitive_match(interface, "messageevent"sv)) {
  1401. event = HTML::MessageEvent::create(realm, String {});
  1402. } else if (Infra::is_ascii_case_insensitive_match(interface, "mouseevent"sv)
  1403. || Infra::is_ascii_case_insensitive_match(interface, "mouseevents"sv)) {
  1404. event = UIEvents::MouseEvent::create(realm, FlyString {});
  1405. } else if (Infra::is_ascii_case_insensitive_match(interface, "storageevent"sv)) {
  1406. event = Event::create(realm, FlyString {}); // FIXME: Create StorageEvent
  1407. } else if (Infra::is_ascii_case_insensitive_match(interface, "svgevents"sv)) {
  1408. event = Event::create(realm, FlyString {});
  1409. } else if (Infra::is_ascii_case_insensitive_match(interface, "textevent"sv)) {
  1410. event = Event::create(realm, FlyString {}); // FIXME: Create CompositionEvent
  1411. } else if (Infra::is_ascii_case_insensitive_match(interface, "touchevent"sv)) {
  1412. event = Event::create(realm, FlyString {}); // FIXME: Create TouchEvent
  1413. } else if (Infra::is_ascii_case_insensitive_match(interface, "uievent"sv)
  1414. || Infra::is_ascii_case_insensitive_match(interface, "uievents"sv)) {
  1415. event = UIEvents::UIEvent::create(realm, FlyString {});
  1416. }
  1417. // 3. If constructor is null, then throw a "NotSupportedError" DOMException.
  1418. if (!event) {
  1419. return WebIDL::NotSupportedError::create(realm, "No constructor for interface found"_fly_string);
  1420. }
  1421. // FIXME: 4. If the interface indicated by constructor is not exposed on the relevant global object of this, then throw a "NotSupportedError" DOMException.
  1422. // NOTE: These are done in the if-chain above
  1423. // 5. Let event be the result of creating an event given constructor.
  1424. // 6. Initialize event’s type attribute to the empty string.
  1425. // 7. Initialize event’s timeStamp attribute to the result of calling current high resolution time with this’s relevant global object.
  1426. // NOTE: This is handled by each constructor.
  1427. // 8. Initialize event’s isTrusted attribute to false.
  1428. event->set_is_trusted(false);
  1429. // 9. Unset event’s initialized flag.
  1430. event->set_initialized(false);
  1431. // 10. Return event.
  1432. return JS::NonnullGCPtr(*event);
  1433. }
  1434. void Document::set_pending_parsing_blocking_script(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement* script)
  1435. {
  1436. m_pending_parsing_blocking_script = script;
  1437. }
  1438. JS::NonnullGCPtr<HTML::HTMLScriptElement> Document::take_pending_parsing_blocking_script(Badge<HTML::HTMLParser>)
  1439. {
  1440. VERIFY(m_pending_parsing_blocking_script);
  1441. auto script = m_pending_parsing_blocking_script;
  1442. m_pending_parsing_blocking_script = nullptr;
  1443. return *script;
  1444. }
  1445. void Document::add_script_to_execute_when_parsing_has_finished(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement& script)
  1446. {
  1447. m_scripts_to_execute_when_parsing_has_finished.append(script);
  1448. }
  1449. Vector<JS::Handle<HTML::HTMLScriptElement>> Document::take_scripts_to_execute_when_parsing_has_finished(Badge<HTML::HTMLParser>)
  1450. {
  1451. Vector<JS::Handle<HTML::HTMLScriptElement>> handles;
  1452. for (auto script : m_scripts_to_execute_when_parsing_has_finished)
  1453. handles.append(JS::make_handle(script));
  1454. m_scripts_to_execute_when_parsing_has_finished.clear();
  1455. return handles;
  1456. }
  1457. void Document::add_script_to_execute_as_soon_as_possible(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement& script)
  1458. {
  1459. m_scripts_to_execute_as_soon_as_possible.append(script);
  1460. }
  1461. Vector<JS::Handle<HTML::HTMLScriptElement>> Document::take_scripts_to_execute_as_soon_as_possible(Badge<HTML::HTMLParser>)
  1462. {
  1463. Vector<JS::Handle<HTML::HTMLScriptElement>> handles;
  1464. for (auto script : m_scripts_to_execute_as_soon_as_possible)
  1465. handles.append(JS::make_handle(script));
  1466. m_scripts_to_execute_as_soon_as_possible.clear();
  1467. return handles;
  1468. }
  1469. void Document::add_script_to_execute_in_order_as_soon_as_possible(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement& script)
  1470. {
  1471. m_scripts_to_execute_in_order_as_soon_as_possible.append(script);
  1472. }
  1473. Vector<JS::Handle<HTML::HTMLScriptElement>> Document::take_scripts_to_execute_in_order_as_soon_as_possible(Badge<HTML::HTMLParser>)
  1474. {
  1475. Vector<JS::Handle<HTML::HTMLScriptElement>> handles;
  1476. for (auto script : m_scripts_to_execute_in_order_as_soon_as_possible)
  1477. handles.append(JS::make_handle(script));
  1478. m_scripts_to_execute_in_order_as_soon_as_possible.clear();
  1479. return handles;
  1480. }
  1481. // https://dom.spec.whatwg.org/#dom-document-importnode
  1482. WebIDL::ExceptionOr<JS::NonnullGCPtr<Node>> Document::import_node(JS::NonnullGCPtr<Node> node, bool deep)
  1483. {
  1484. // 1. If node is a document or shadow root, then throw a "NotSupportedError" DOMException.
  1485. if (is<Document>(*node) || is<ShadowRoot>(*node))
  1486. return WebIDL::NotSupportedError::create(realm(), "Cannot import a document or shadow root."_fly_string);
  1487. // 2. Return a clone of node, with this and the clone children flag set if deep is true.
  1488. return node->clone_node(this, deep);
  1489. }
  1490. // https://dom.spec.whatwg.org/#concept-node-adopt
  1491. void Document::adopt_node(Node& node)
  1492. {
  1493. // 1. Let oldDocument be node’s node document.
  1494. auto& old_document = node.document();
  1495. // 2. If node’s parent is non-null, then remove node.
  1496. if (node.parent())
  1497. node.remove();
  1498. // 3. If document is not oldDocument, then:
  1499. if (&old_document != this) {
  1500. // 1. For each inclusiveDescendant in node’s shadow-including inclusive descendants:
  1501. node.for_each_shadow_including_inclusive_descendant([&](DOM::Node& inclusive_descendant) {
  1502. // 1. Set inclusiveDescendant’s node document to document.
  1503. inclusive_descendant.set_document({}, *this);
  1504. // FIXME: 2. If inclusiveDescendant is an element, then set the node document of each attribute in inclusiveDescendant’s
  1505. // attribute list to document.
  1506. return TraversalDecision::Continue;
  1507. });
  1508. // 2. For each inclusiveDescendant in node’s shadow-including inclusive descendants that is custom,
  1509. // enqueue a custom element callback reaction with inclusiveDescendant, callback name "adoptedCallback",
  1510. // and an argument list containing oldDocument and document.
  1511. node.for_each_shadow_including_inclusive_descendant([&](DOM::Node& inclusive_descendant) {
  1512. if (!is<DOM::Element>(inclusive_descendant))
  1513. return TraversalDecision::Continue;
  1514. auto& element = static_cast<DOM::Element&>(inclusive_descendant);
  1515. if (element.is_custom()) {
  1516. auto& vm = this->vm();
  1517. JS::MarkedVector<JS::Value> arguments { vm.heap() };
  1518. arguments.append(&old_document);
  1519. arguments.append(this);
  1520. element.enqueue_a_custom_element_callback_reaction(HTML::CustomElementReactionNames::adoptedCallback, move(arguments));
  1521. }
  1522. return TraversalDecision::Continue;
  1523. });
  1524. // 3. For each inclusiveDescendant in node’s shadow-including inclusive descendants, in shadow-including tree order,
  1525. // run the adopting steps with inclusiveDescendant and oldDocument.
  1526. node.for_each_shadow_including_inclusive_descendant([&](auto& inclusive_descendant) {
  1527. inclusive_descendant.adopted_from(old_document);
  1528. return TraversalDecision::Continue;
  1529. });
  1530. // Transfer NodeIterators rooted at `node` from old_document to this document.
  1531. Vector<NodeIterator&> node_iterators_to_transfer;
  1532. for (auto node_iterator : old_document.m_node_iterators) {
  1533. if (node_iterator->root().ptr() == &node)
  1534. node_iterators_to_transfer.append(*node_iterator);
  1535. }
  1536. for (auto& node_iterator : node_iterators_to_transfer) {
  1537. old_document.m_node_iterators.remove(&node_iterator);
  1538. m_node_iterators.set(&node_iterator);
  1539. }
  1540. }
  1541. }
  1542. // https://dom.spec.whatwg.org/#dom-document-adoptnode
  1543. WebIDL::ExceptionOr<JS::NonnullGCPtr<Node>> Document::adopt_node_binding(JS::NonnullGCPtr<Node> node)
  1544. {
  1545. if (is<Document>(*node))
  1546. return WebIDL::NotSupportedError::create(realm(), "Cannot adopt a document into a document"_fly_string);
  1547. if (is<ShadowRoot>(*node))
  1548. return WebIDL::HierarchyRequestError::create(realm(), "Cannot adopt a shadow root into a document"_fly_string);
  1549. if (is<DocumentFragment>(*node) && verify_cast<DocumentFragment>(*node).host())
  1550. return node;
  1551. adopt_node(*node);
  1552. return node;
  1553. }
  1554. DocumentType const* Document::doctype() const
  1555. {
  1556. return first_child_of_type<DocumentType>();
  1557. }
  1558. String const& Document::compat_mode() const
  1559. {
  1560. static String const back_compat = "BackCompat"_string;
  1561. static String const css1_compat = "CSS1Compat"_string;
  1562. if (m_quirks_mode == QuirksMode::Yes)
  1563. return back_compat;
  1564. return css1_compat;
  1565. }
  1566. bool Document::is_editable() const
  1567. {
  1568. return m_editable;
  1569. }
  1570. void Document::set_focused_element(Element* element)
  1571. {
  1572. if (m_focused_element.ptr() == element)
  1573. return;
  1574. if (m_focused_element) {
  1575. m_focused_element->did_lose_focus();
  1576. m_focused_element->set_needs_style_update(true);
  1577. }
  1578. m_focused_element = element;
  1579. if (m_focused_element) {
  1580. m_focused_element->did_receive_focus();
  1581. m_focused_element->set_needs_style_update(true);
  1582. }
  1583. if (paintable())
  1584. paintable()->set_needs_display();
  1585. // Scroll the viewport if necessary to make the newly focused element visible.
  1586. if (m_focused_element) {
  1587. m_focused_element->queue_an_element_task(HTML::Task::Source::UserInteraction, [&]() {
  1588. ScrollIntoViewOptions scroll_options;
  1589. scroll_options.block = Bindings::ScrollLogicalPosition::Nearest;
  1590. scroll_options.inline_ = Bindings::ScrollLogicalPosition::Nearest;
  1591. (void)m_focused_element->scroll_into_view(scroll_options);
  1592. });
  1593. }
  1594. }
  1595. void Document::set_active_element(Element* element)
  1596. {
  1597. if (m_active_element.ptr() == element)
  1598. return;
  1599. m_active_element = element;
  1600. if (paintable())
  1601. paintable()->set_needs_display();
  1602. }
  1603. void Document::set_target_element(Element* element)
  1604. {
  1605. if (m_target_element.ptr() == element)
  1606. return;
  1607. if (m_target_element)
  1608. m_target_element->set_needs_style_update(true);
  1609. m_target_element = element;
  1610. if (m_target_element)
  1611. m_target_element->set_needs_style_update(true);
  1612. if (paintable())
  1613. paintable()->set_needs_display();
  1614. }
  1615. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#the-indicated-part-of-the-document
  1616. Document::IndicatedPart Document::determine_the_indicated_part() const
  1617. {
  1618. // For an HTML document document, the following processing model must be followed to determine its indicated part:
  1619. // 1. Let fragment be document's URL's fragment.
  1620. auto fragment = url().fragment();
  1621. // 2. If fragment is the empty string, then return the special value top of the document.
  1622. if (!fragment.has_value() || fragment->is_empty())
  1623. return Document::TopOfTheDocument {};
  1624. // 3. Let potentialIndicatedElement be the result of finding a potential indicated element given document and fragment.
  1625. auto* potential_indicated_element = find_a_potential_indicated_element(*fragment);
  1626. // 4. If potentialIndicatedElement is not null, then return potentialIndicatedElement.
  1627. if (potential_indicated_element)
  1628. return potential_indicated_element;
  1629. // 5. Let fragmentBytes be the result of percent-decoding fragment.
  1630. // 6. Let decodedFragment be the result of running UTF-8 decode without BOM on fragmentBytes.
  1631. auto decoded_fragment = URL::percent_decode(*fragment);
  1632. // 7. Set potentialIndicatedElement to the result of finding a potential indicated element given document and decodedFragment.
  1633. potential_indicated_element = find_a_potential_indicated_element(MUST(FlyString::from_deprecated_fly_string(decoded_fragment)));
  1634. // 8. If potentialIndicatedElement is not null, then return potentialIndicatedElement.
  1635. if (potential_indicated_element)
  1636. return potential_indicated_element;
  1637. // 9. If decodedFragment is an ASCII case-insensitive match for the string top, then return the top of the document.
  1638. if (Infra::is_ascii_case_insensitive_match(decoded_fragment, "top"sv))
  1639. return Document::TopOfTheDocument {};
  1640. // 10. Return null.
  1641. return nullptr;
  1642. }
  1643. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#find-a-potential-indicated-element
  1644. Element* Document::find_a_potential_indicated_element(FlyString const& fragment) const
  1645. {
  1646. // To find a potential indicated element given a Document document and a string fragment, run these steps:
  1647. // 1. If there is an element in the document tree whose root is document and that has an ID equal to
  1648. // fragment, then return the first such element in tree order.
  1649. if (auto element = get_element_by_id(fragment))
  1650. return const_cast<Element*>(element.ptr());
  1651. // 2. If there is an a element in the document tree whose root is document that has a name attribute
  1652. // whose value is equal to fragment, then return the first such element in tree order.
  1653. Element* element_with_name = nullptr;
  1654. root().for_each_in_subtree_of_type<Element>([&](Element const& element) {
  1655. if (element.name() == fragment) {
  1656. element_with_name = const_cast<Element*>(&element);
  1657. return TraversalDecision::Break;
  1658. }
  1659. return TraversalDecision::Continue;
  1660. });
  1661. if (element_with_name)
  1662. return element_with_name;
  1663. // 3. Return null.
  1664. return nullptr;
  1665. }
  1666. // https://www.w3.org/TR/css-animations-2/#event-dispatch
  1667. void Document::dispatch_events_for_animation_if_necessary(JS::NonnullGCPtr<Animations::Animation> animation)
  1668. {
  1669. // Each time a new animation frame is established and the animation does not have a pending play task or pending
  1670. // pause task, the events to dispatch are determined by comparing the animation’s phase before and after
  1671. // establishing the new animation frame as follows:
  1672. auto effect = animation->effect();
  1673. if (!effect || !effect->is_keyframe_effect() || !animation->is_css_animation() || animation->pending())
  1674. return;
  1675. auto& css_animation = verify_cast<CSS::CSSAnimation>(*animation);
  1676. JS::GCPtr<Element> target = effect->target();
  1677. if (!target)
  1678. return;
  1679. if (target->paintable())
  1680. target->paintable()->set_needs_display();
  1681. auto previous_phase = effect->previous_phase();
  1682. auto current_phase = effect->phase();
  1683. auto current_iteration = effect->current_iteration().value_or(0.0);
  1684. if (previous_phase != current_phase) {
  1685. auto owning_element = css_animation.owning_element();
  1686. auto dispatch_event = [&](FlyString const& name, double elapsed_time) {
  1687. append_pending_animation_event({
  1688. .event = CSS::AnimationEvent::create(
  1689. owning_element->realm(),
  1690. name,
  1691. {
  1692. { .bubbles = true },
  1693. css_animation.id(),
  1694. elapsed_time,
  1695. }),
  1696. .animation = css_animation,
  1697. .target = *target,
  1698. .scheduled_event_time = HighResolutionTime::unsafe_shared_current_time(),
  1699. });
  1700. };
  1701. // For calculating the elapsedTime of each event, the following definitions are used:
  1702. // - interval start = max(min(-start delay, active duration), 0)
  1703. auto interval_start = max(min(-effect->start_delay(), effect->active_duration()), 0.0);
  1704. // - interval end = max(min(associated effect end - start delay, active duration), 0)
  1705. auto interval_end = max(min(effect->end_time() - effect->start_delay(), effect->active_duration()), 0.0);
  1706. switch (previous_phase) {
  1707. case Animations::AnimationEffect::Phase::Before:
  1708. [[fallthrough]];
  1709. case Animations::AnimationEffect::Phase::Idle:
  1710. if (current_phase == Animations::AnimationEffect::Phase::Active) {
  1711. dispatch_event(HTML::EventNames::animationstart, interval_start);
  1712. } else if (current_phase == Animations::AnimationEffect::Phase::After) {
  1713. dispatch_event(HTML::EventNames::animationstart, interval_start);
  1714. dispatch_event(HTML::EventNames::animationend, interval_end);
  1715. }
  1716. break;
  1717. case Animations::AnimationEffect::Phase::Active:
  1718. if (current_phase == Animations::AnimationEffect::Phase::Before) {
  1719. dispatch_event(HTML::EventNames::animationend, interval_start);
  1720. } else if (current_phase == Animations::AnimationEffect::Phase::Active) {
  1721. auto previous_current_iteration = effect->previous_current_iteration();
  1722. if (previous_current_iteration != current_iteration) {
  1723. // The elapsed time for an animationiteration event is defined as follows:
  1724. // 1. Let previous current iteration be the current iteration from the previous animation frame.
  1725. // 2. If previous current iteration is greater than current iteration, let iteration boundary be current iteration + 1,
  1726. // otherwise let it be current iteration.
  1727. auto iteration_boundary = previous_current_iteration > current_iteration ? current_iteration + 1 : current_iteration;
  1728. // 3. The elapsed time is the result of evaluating (iteration boundary - iteration start) × iteration duration).
  1729. auto iteration_duration_variant = effect->iteration_duration();
  1730. auto iteration_duration = iteration_duration_variant.has<String>() ? 0.0 : iteration_duration_variant.get<double>();
  1731. auto elapsed_time = (iteration_boundary - effect->iteration_start()) * iteration_duration;
  1732. dispatch_event(HTML::EventNames::animationiteration, elapsed_time);
  1733. }
  1734. } else if (current_phase == Animations::AnimationEffect::Phase::After) {
  1735. dispatch_event(HTML::EventNames::animationend, interval_end);
  1736. }
  1737. break;
  1738. case Animations::AnimationEffect::Phase::After:
  1739. if (current_phase == Animations::AnimationEffect::Phase::Active) {
  1740. dispatch_event(HTML::EventNames::animationstart, interval_end);
  1741. } else if (current_phase == Animations::AnimationEffect::Phase::Before) {
  1742. dispatch_event(HTML::EventNames::animationstart, interval_end);
  1743. dispatch_event(HTML::EventNames::animationend, interval_start);
  1744. }
  1745. break;
  1746. }
  1747. if (current_phase == Animations::AnimationEffect::Phase::Idle && previous_phase != Animations::AnimationEffect::Phase::Idle && previous_phase != Animations::AnimationEffect::Phase::After) {
  1748. // FIXME: Calculate a non-zero time when the animation is cancelled by means other than calling cancel()
  1749. auto cancel_time = animation->release_saved_cancel_time().value_or(0.0);
  1750. dispatch_event(HTML::EventNames::animationcancel, cancel_time);
  1751. }
  1752. }
  1753. effect->set_previous_phase(current_phase);
  1754. effect->set_previous_current_iteration(current_iteration);
  1755. }
  1756. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#scroll-to-the-fragment-identifier
  1757. void Document::scroll_to_the_fragment()
  1758. {
  1759. // To scroll to the fragment given a Document document:
  1760. // 1. If document's indicated part is null, then set document's target element to null.
  1761. auto indicated_part = determine_the_indicated_part();
  1762. if (indicated_part.has<Element*>() && indicated_part.get<Element*>() == nullptr) {
  1763. set_target_element(nullptr);
  1764. }
  1765. // 2. Otherwise, if document's indicated part is top of the document, then:
  1766. else if (indicated_part.has<TopOfTheDocument>()) {
  1767. // 1. Set document's target element to null.
  1768. set_target_element(nullptr);
  1769. // 2. Scroll to the beginning of the document for document. [CSSOMVIEW]
  1770. scroll_to_the_beginning_of_the_document();
  1771. // 3. Return.
  1772. return;
  1773. }
  1774. // 3. Otherwise:
  1775. else {
  1776. // 1. Assert: document's indicated part is an element.
  1777. VERIFY(indicated_part.has<Element*>());
  1778. // 2. Let target be document's indicated part.
  1779. auto target = indicated_part.get<Element*>();
  1780. // 3. Set document's target element to target.
  1781. set_target_element(target);
  1782. // FIXME: 4. Run the ancestor details revealing algorithm on target.
  1783. // FIXME: 5. Run the ancestor hidden-until-found revealing algorithm on target.
  1784. // 6. Scroll target into view, with behavior set to "auto", block set to "start", and inline set to "nearest". [CSSOMVIEW]
  1785. ScrollIntoViewOptions scroll_options;
  1786. scroll_options.block = Bindings::ScrollLogicalPosition::Start;
  1787. scroll_options.inline_ = Bindings::ScrollLogicalPosition::Nearest;
  1788. (void)target->scroll_into_view(scroll_options);
  1789. // 7. Run the focusing steps for target, with the Document's viewport as the fallback target.
  1790. // FIXME: Pass the Document's viewport somehow.
  1791. HTML::run_focusing_steps(target);
  1792. // FIXME: 8. Move the sequential focus navigation starting point to target.
  1793. }
  1794. }
  1795. // https://drafts.csswg.org/cssom-view-1/#scroll-to-the-beginning-of-the-document
  1796. void Document::scroll_to_the_beginning_of_the_document()
  1797. {
  1798. // FIXME: Actually implement this algorithm
  1799. if (auto navigable = this->navigable())
  1800. navigable->perform_scroll_of_viewport({ 0, 0 });
  1801. }
  1802. StringView Document::ready_state() const
  1803. {
  1804. switch (m_readiness) {
  1805. case HTML::DocumentReadyState::Loading:
  1806. return "loading"sv;
  1807. case HTML::DocumentReadyState::Interactive:
  1808. return "interactive"sv;
  1809. case HTML::DocumentReadyState::Complete:
  1810. return "complete"sv;
  1811. }
  1812. VERIFY_NOT_REACHED();
  1813. }
  1814. // https://html.spec.whatwg.org/multipage/dom.html#update-the-current-document-readiness
  1815. void Document::update_readiness(HTML::DocumentReadyState readiness_value)
  1816. {
  1817. // 1. If document's current document readiness equals readinessValue, then return.
  1818. if (m_readiness == readiness_value)
  1819. return;
  1820. // 2. Set document's current document readiness to readinessValue.
  1821. m_readiness = readiness_value;
  1822. // 3. If document is associated with an HTML parser, then:
  1823. if (m_parser) {
  1824. // 1. Let now be the current high resolution time given document's relevant global object.
  1825. auto now = HighResolutionTime::current_high_resolution_time(relevant_global_object(*this));
  1826. // 2. If readinessValue is "complete", and document's load timing info's DOM complete time is 0,
  1827. // then set document's load timing info's DOM complete time to now.
  1828. if (readiness_value == HTML::DocumentReadyState::Complete && m_load_timing_info.dom_complete_time == 0) {
  1829. m_load_timing_info.dom_complete_time = now;
  1830. }
  1831. // 3. Otherwise, if readinessValue is "interactive", and document's load timing info's DOM interactive time is 0,
  1832. // then set document's load timing info's DOM interactive time to now.
  1833. else if (readiness_value == HTML::DocumentReadyState::Interactive && m_load_timing_info.dom_interactive_time == 0) {
  1834. m_load_timing_info.dom_interactive_time = now;
  1835. }
  1836. }
  1837. // 4. Fire an event named readystatechange at document.
  1838. dispatch_event(Event::create(realm(), HTML::EventNames::readystatechange));
  1839. if (readiness_value == HTML::DocumentReadyState::Complete) {
  1840. auto navigable = this->navigable();
  1841. if (navigable && navigable->is_traversable()) {
  1842. if (!is_decoded_svg()) {
  1843. HTML::HTMLLinkElement::load_fallback_favicon_if_needed(*this).release_value_but_fixme_should_propagate_errors();
  1844. }
  1845. navigable->traversable_navigable()->page().client().page_did_finish_loading(url());
  1846. } else {
  1847. m_needs_to_call_page_did_load = true;
  1848. }
  1849. }
  1850. }
  1851. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-lastmodified
  1852. String Document::last_modified() const
  1853. {
  1854. // The lastModified attribute, on getting, must return the date and time of the Document's source file's
  1855. // last modification, in the user's local time zone, in the following format:
  1856. // 1. The month component of the date.
  1857. // 2. A U+002F SOLIDUS character (/).
  1858. // 3. The day component of the date.
  1859. // 4. A U+002F SOLIDUS character (/).
  1860. // 5. The year component of the date.
  1861. // 6. A U+0020 SPACE character.
  1862. // 7. The hours component of the time.
  1863. // 8. A U+003A COLON character (:).
  1864. // 9. The minutes component of the time.
  1865. // 10. A U+003A COLON character (:).
  1866. // 11. The seconds component of the time.
  1867. // The Document's source file's last modification date and time must be derived from relevant features
  1868. // of the networking protocols used, e.g. from the value of the HTTP `Last-Modified` header of the document,
  1869. // or from metadata in the file system for local files. If the last modification date and time are not known,
  1870. // the attribute must return the current date and time in the above format.
  1871. constexpr auto format_string = "%m/%d/%Y %H:%M:%S"sv;
  1872. if (m_last_modified.has_value())
  1873. return MUST(m_last_modified.value().to_string(format_string));
  1874. return MUST(Core::DateTime::now().to_string(format_string));
  1875. }
  1876. Page& Document::page()
  1877. {
  1878. return m_page;
  1879. }
  1880. Page const& Document::page() const
  1881. {
  1882. return m_page;
  1883. }
  1884. EventTarget* Document::get_parent(Event const& event)
  1885. {
  1886. if (event.type() == HTML::EventNames::load)
  1887. return nullptr;
  1888. return m_window;
  1889. }
  1890. // https://html.spec.whatwg.org/#completely-loaded
  1891. bool Document::is_completely_loaded() const
  1892. {
  1893. return m_completely_loaded_time.has_value();
  1894. }
  1895. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#completely-finish-loading
  1896. void Document::completely_finish_loading()
  1897. {
  1898. if (!navigable())
  1899. return;
  1900. ScopeGuard notify_observers = [this] {
  1901. auto observers_to_notify = m_document_observers.values();
  1902. for (auto& document_observer : observers_to_notify) {
  1903. if (document_observer->document_completely_loaded())
  1904. document_observer->document_completely_loaded()->function()();
  1905. }
  1906. };
  1907. // 1. Assert: document's browsing context is non-null.
  1908. VERIFY(browsing_context());
  1909. // 2. Set document's completely loaded time to the current time.
  1910. m_completely_loaded_time = AK::UnixDateTime::now();
  1911. // NOTE: See the end of shared_declarative_refresh_steps.
  1912. if (m_active_refresh_timer)
  1913. m_active_refresh_timer->start();
  1914. // 3. Let container be document's browsing context's container.
  1915. if (!navigable()->container())
  1916. return;
  1917. auto container = JS::make_handle(navigable()->container());
  1918. // 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.
  1919. if (container && is<HTML::HTMLIFrameElement>(*container)) {
  1920. container->queue_an_element_task(HTML::Task::Source::DOMManipulation, [container] {
  1921. run_iframe_load_event_steps(static_cast<HTML::HTMLIFrameElement&>(*container));
  1922. });
  1923. }
  1924. // 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.
  1925. else if (container) {
  1926. container->queue_an_element_task(HTML::Task::Source::DOMManipulation, [container] {
  1927. container->dispatch_event(DOM::Event::create(container->realm(), HTML::EventNames::load));
  1928. });
  1929. }
  1930. }
  1931. String Document::cookie(Cookie::Source source)
  1932. {
  1933. return page().client().page_did_request_cookie(m_url, source);
  1934. }
  1935. void Document::set_cookie(StringView cookie_string, Cookie::Source source)
  1936. {
  1937. auto cookie = Cookie::parse_cookie(cookie_string);
  1938. if (!cookie.has_value())
  1939. return;
  1940. page().client().page_did_set_cookie(m_url, cookie.value(), source);
  1941. }
  1942. String Document::fg_color() const
  1943. {
  1944. if (auto* body_element = body(); body_element && !is<HTML::HTMLFrameSetElement>(*body_element))
  1945. return body_element->get_attribute_value(HTML::AttributeNames::text);
  1946. return ""_string;
  1947. }
  1948. void Document::set_fg_color(String const& value)
  1949. {
  1950. if (auto* body_element = body(); body_element && !is<HTML::HTMLFrameSetElement>(*body_element))
  1951. MUST(body_element->set_attribute(HTML::AttributeNames::text, value));
  1952. }
  1953. String Document::link_color() const
  1954. {
  1955. if (auto* body_element = body(); body_element && !is<HTML::HTMLFrameSetElement>(*body_element))
  1956. return body_element->get_attribute_value(HTML::AttributeNames::link);
  1957. return ""_string;
  1958. }
  1959. void Document::set_link_color(String const& value)
  1960. {
  1961. if (auto* body_element = body(); body_element && !is<HTML::HTMLFrameSetElement>(*body_element))
  1962. MUST(body_element->set_attribute(HTML::AttributeNames::link, value));
  1963. }
  1964. String Document::vlink_color() const
  1965. {
  1966. if (auto* body_element = body(); body_element && !is<HTML::HTMLFrameSetElement>(*body_element))
  1967. return body_element->get_attribute_value(HTML::AttributeNames::vlink);
  1968. return ""_string;
  1969. }
  1970. void Document::set_vlink_color(String const& value)
  1971. {
  1972. if (auto* body_element = body(); body_element && !is<HTML::HTMLFrameSetElement>(*body_element))
  1973. MUST(body_element->set_attribute(HTML::AttributeNames::vlink, value));
  1974. }
  1975. String Document::alink_color() const
  1976. {
  1977. if (auto* body_element = body(); body_element && !is<HTML::HTMLFrameSetElement>(*body_element))
  1978. return body_element->get_attribute_value(HTML::AttributeNames::alink);
  1979. return ""_string;
  1980. }
  1981. void Document::set_alink_color(String const& value)
  1982. {
  1983. if (auto* body_element = body(); body_element && !is<HTML::HTMLFrameSetElement>(*body_element))
  1984. MUST(body_element->set_attribute(HTML::AttributeNames::alink, value));
  1985. }
  1986. String Document::bg_color() const
  1987. {
  1988. if (auto* body_element = body(); body_element && !is<HTML::HTMLFrameSetElement>(*body_element))
  1989. return body_element->get_attribute_value(HTML::AttributeNames::bgcolor);
  1990. return ""_string;
  1991. }
  1992. void Document::set_bg_color(String const& value)
  1993. {
  1994. if (auto* body_element = body(); body_element && !is<HTML::HTMLFrameSetElement>(*body_element))
  1995. MUST(body_element->set_attribute(HTML::AttributeNames::bgcolor, value));
  1996. }
  1997. String Document::dump_dom_tree_as_json() const
  1998. {
  1999. StringBuilder builder;
  2000. auto json = MUST(JsonObjectSerializer<>::try_create(builder));
  2001. serialize_tree_as_json(json);
  2002. MUST(json.finish());
  2003. return MUST(builder.to_string());
  2004. }
  2005. // https://html.spec.whatwg.org/multipage/semantics.html#has-a-style-sheet-that-is-blocking-scripts
  2006. bool Document::has_a_style_sheet_that_is_blocking_scripts() const
  2007. {
  2008. // FIXME: 1. If document's script-blocking style sheet set is not empty, then return true.
  2009. if (m_script_blocking_style_sheet_counter > 0)
  2010. return true;
  2011. // 2. If document's node navigable is null, then return false.
  2012. if (!navigable())
  2013. return false;
  2014. // 3. Let containerDocument be document's node navigable's container document.
  2015. auto container_document = navigable()->container_document();
  2016. // FIXME: 4. If containerDocument is non-null and containerDocument's script-blocking style sheet set is not empty, then return true.
  2017. if (container_document && container_document->m_script_blocking_style_sheet_counter > 0)
  2018. return true;
  2019. // 5. Return false
  2020. return false;
  2021. }
  2022. String Document::referrer() const
  2023. {
  2024. return m_referrer;
  2025. }
  2026. void Document::set_referrer(String referrer)
  2027. {
  2028. m_referrer = move(referrer);
  2029. }
  2030. // https://html.spec.whatwg.org/multipage/document-sequences.html#fully-active
  2031. bool Document::is_fully_active() const
  2032. {
  2033. // A Document d is said to be fully active when d is the active document of a navigable navigable, and either
  2034. // navigable is a top-level traversable or navigable's container document is fully active.
  2035. auto navigable = this->navigable();
  2036. if (!navigable)
  2037. return false;
  2038. auto traversable = navigable->traversable_navigable();
  2039. if (navigable == traversable && traversable->is_top_level_traversable())
  2040. return true;
  2041. auto container_document = navigable->container_document();
  2042. if (container_document && container_document->is_fully_active())
  2043. return true;
  2044. return false;
  2045. }
  2046. bool Document::is_active() const
  2047. {
  2048. auto navigable = this->navigable();
  2049. return navigable && navigable->active_document() == this;
  2050. }
  2051. // https://html.spec.whatwg.org/multipage/history.html#dom-document-location
  2052. JS::GCPtr<HTML::Location> Document::location()
  2053. {
  2054. // The Document object's location attribute's getter must return this Document object's relevant global object's Location object,
  2055. // if this Document object is fully active, and null otherwise.
  2056. if (!is_fully_active())
  2057. return nullptr;
  2058. return window()->location();
  2059. }
  2060. // https://html.spec.whatwg.org/multipage/interaction.html#dom-document-hidden
  2061. bool Document::hidden() const
  2062. {
  2063. return m_visibility_state == HTML::VisibilityState::Hidden;
  2064. }
  2065. // https://html.spec.whatwg.org/multipage/interaction.html#dom-document-visibilitystate
  2066. StringView Document::visibility_state() const
  2067. {
  2068. switch (m_visibility_state) {
  2069. case HTML::VisibilityState::Hidden:
  2070. return "hidden"sv;
  2071. case HTML::VisibilityState::Visible:
  2072. return "visible"sv;
  2073. }
  2074. VERIFY_NOT_REACHED();
  2075. }
  2076. void Document::set_visibility_state(Badge<HTML::BrowsingContext>, HTML::VisibilityState visibility_state)
  2077. {
  2078. m_visibility_state = visibility_state;
  2079. }
  2080. // https://html.spec.whatwg.org/multipage/interaction.html#update-the-visibility-state
  2081. void Document::update_the_visibility_state(HTML::VisibilityState visibility_state)
  2082. {
  2083. // 1. If document's visibility state equals visibilityState, then return.
  2084. if (m_visibility_state == visibility_state)
  2085. return;
  2086. // 2. Set document's visibility state to visibilityState.
  2087. m_visibility_state = visibility_state;
  2088. // FIXME: 3. Run any page visibility change steps which may be defined in other specifications, with visibility state and document.
  2089. // 4. Fire an event named visibilitychange at document, with its bubbles attribute initialized to true.
  2090. auto event = DOM::Event::create(realm(), HTML::EventNames::visibilitychange);
  2091. event->set_bubbles(true);
  2092. dispatch_event(event);
  2093. }
  2094. // https://drafts.csswg.org/cssom-view/#run-the-resize-steps
  2095. void Document::run_the_resize_steps()
  2096. {
  2097. // 1. If doc’s viewport has had its width or height changed
  2098. // (e.g. as a result of the user resizing the browser window, or changing the page zoom scale factor,
  2099. // or an iframe element’s dimensions are changed) since the last time these steps were run,
  2100. // fire an event named resize at the Window object associated with doc.
  2101. auto viewport_size = viewport_rect().size().to_type<int>();
  2102. if (m_last_viewport_size == viewport_size)
  2103. return;
  2104. m_last_viewport_size = viewport_size;
  2105. window()->dispatch_event(DOM::Event::create(realm(), UIEvents::EventNames::resize));
  2106. schedule_layout_update();
  2107. }
  2108. // https://w3c.github.io/csswg-drafts/cssom-view-1/#document-run-the-scroll-steps
  2109. void Document::run_the_scroll_steps()
  2110. {
  2111. // 1. For each item target in doc’s pending scroll event targets, in the order they were added to the list, run these substeps:
  2112. for (auto& target : m_pending_scroll_event_targets) {
  2113. // 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.
  2114. if (is<Document>(*target)) {
  2115. auto event = DOM::Event::create(realm(), HTML::EventNames::scroll);
  2116. event->set_bubbles(true);
  2117. target->dispatch_event(event);
  2118. // FIXME: Fire at the associated VisualViewport
  2119. }
  2120. // 2. Otherwise, fire an event named scroll at target.
  2121. else {
  2122. auto event = DOM::Event::create(realm(), HTML::EventNames::scroll);
  2123. target->dispatch_event(event);
  2124. }
  2125. }
  2126. // 2. Empty doc’s pending scroll event targets.
  2127. m_pending_scroll_event_targets.clear();
  2128. }
  2129. void Document::add_media_query_list(JS::NonnullGCPtr<CSS::MediaQueryList> media_query_list)
  2130. {
  2131. m_media_query_lists.append(*media_query_list);
  2132. }
  2133. // https://drafts.csswg.org/cssom-view/#evaluate-media-queries-and-report-changes
  2134. void Document::evaluate_media_queries_and_report_changes()
  2135. {
  2136. // NOTE: Not in the spec, but we take this opportunity to prune null WeakPtrs.
  2137. m_media_query_lists.remove_all_matching([](auto& it) {
  2138. return it.is_null();
  2139. });
  2140. // 1. For each MediaQueryList object target that has doc as its document,
  2141. // in the order they were created, oldest first, run these substeps:
  2142. for (auto& media_query_list_ptr : m_media_query_lists) {
  2143. // 1. If target’s matches state has changed since the last time these steps
  2144. // were run, fire an event at target using the MediaQueryListEvent constructor,
  2145. // with its type attribute initialized to change, its isTrusted attribute
  2146. // initialized to true, its media attribute initialized to target’s media,
  2147. // and its matches attribute initialized to target’s matches state.
  2148. if (media_query_list_ptr.is_null())
  2149. continue;
  2150. JS::GCPtr<CSS::MediaQueryList> media_query_list = media_query_list_ptr.ptr();
  2151. bool did_match = media_query_list->matches();
  2152. bool now_matches = media_query_list->evaluate();
  2153. if (did_match != now_matches) {
  2154. CSS::MediaQueryListEventInit init;
  2155. init.media = media_query_list->media();
  2156. init.matches = now_matches;
  2157. auto event = CSS::MediaQueryListEvent::create(realm(), HTML::EventNames::change, init);
  2158. event->set_is_trusted(true);
  2159. media_query_list->dispatch_event(*event);
  2160. }
  2161. }
  2162. // Also not in the spec, but this is as good a place as any to evaluate @media rules!
  2163. evaluate_media_rules();
  2164. }
  2165. void Document::evaluate_media_rules()
  2166. {
  2167. auto window = this->window();
  2168. if (!window)
  2169. return;
  2170. bool any_media_queries_changed_match_state = false;
  2171. for_each_css_style_sheet([&](CSS::CSSStyleSheet& style_sheet) {
  2172. if (style_sheet.evaluate_media_queries(*window))
  2173. any_media_queries_changed_match_state = true;
  2174. });
  2175. if (any_media_queries_changed_match_state) {
  2176. style_computer().invalidate_rule_cache();
  2177. invalidate_style();
  2178. invalidate_layout();
  2179. }
  2180. }
  2181. DOMImplementation* Document::implementation()
  2182. {
  2183. if (!m_implementation)
  2184. m_implementation = DOMImplementation::create(*this);
  2185. return m_implementation;
  2186. }
  2187. bool Document::has_focus() const
  2188. {
  2189. // FIXME: Return whether we actually have focus.
  2190. return true;
  2191. }
  2192. void Document::set_parser(Badge<HTML::HTMLParser>, HTML::HTMLParser& parser)
  2193. {
  2194. m_parser = parser;
  2195. }
  2196. void Document::detach_parser(Badge<HTML::HTMLParser>)
  2197. {
  2198. m_parser = nullptr;
  2199. }
  2200. // https://www.w3.org/TR/xml/#NT-NameStartChar
  2201. static bool is_valid_name_start_character(u32 code_point)
  2202. {
  2203. return code_point == ':'
  2204. || (code_point >= 'A' && code_point <= 'Z')
  2205. || code_point == '_'
  2206. || (code_point >= 'a' && code_point <= 'z')
  2207. || (code_point >= 0xc0 && code_point <= 0xd6)
  2208. || (code_point >= 0xd8 && code_point <= 0xf6)
  2209. || (code_point >= 0xf8 && code_point <= 0x2ff)
  2210. || (code_point >= 0x370 && code_point <= 0x37d)
  2211. || (code_point >= 0x37f && code_point <= 0x1fff)
  2212. || (code_point >= 0x200c && code_point <= 0x200d)
  2213. || (code_point >= 0x2070 && code_point <= 0x218f)
  2214. || (code_point >= 0x2c00 && code_point <= 0x2fef)
  2215. || (code_point >= 0x3001 && code_point <= 0xD7ff)
  2216. || (code_point >= 0xf900 && code_point <= 0xfdcf)
  2217. || (code_point >= 0xfdf0 && code_point <= 0xfffd)
  2218. || (code_point >= 0x10000 && code_point <= 0xeffff);
  2219. }
  2220. // https://www.w3.org/TR/xml/#NT-NameChar
  2221. static inline bool is_valid_name_character(u32 code_point)
  2222. {
  2223. return is_valid_name_start_character(code_point)
  2224. || code_point == '-'
  2225. || code_point == '.'
  2226. || (code_point >= '0' && code_point <= '9')
  2227. || code_point == 0xb7
  2228. || (code_point >= 0x300 && code_point <= 0x36f)
  2229. || (code_point >= 0x203f && code_point <= 0x2040);
  2230. }
  2231. // https://www.w3.org/TR/xml/#NT-Name
  2232. bool Document::is_valid_name(String const& name)
  2233. {
  2234. if (name.is_empty())
  2235. return false;
  2236. auto code_points = name.code_points();
  2237. auto it = code_points.begin();
  2238. if (!is_valid_name_start_character(*it))
  2239. return false;
  2240. ++it;
  2241. for (; it != code_points.end(); ++it) {
  2242. if (!is_valid_name_character(*it))
  2243. return false;
  2244. }
  2245. return true;
  2246. }
  2247. // https://dom.spec.whatwg.org/#validate
  2248. WebIDL::ExceptionOr<Document::PrefixAndTagName> Document::validate_qualified_name(JS::Realm& realm, FlyString const& qualified_name)
  2249. {
  2250. if (qualified_name.is_empty())
  2251. return WebIDL::InvalidCharacterError::create(realm, "Empty string is not a valid qualified name."_fly_string);
  2252. auto utf8view = qualified_name.code_points();
  2253. Optional<size_t> colon_offset;
  2254. bool at_start_of_name = true;
  2255. for (auto it = utf8view.begin(); it != utf8view.end(); ++it) {
  2256. auto code_point = *it;
  2257. if (code_point == ':') {
  2258. if (colon_offset.has_value())
  2259. return WebIDL::InvalidCharacterError::create(realm, "More than one colon (:) in qualified name."_fly_string);
  2260. colon_offset = utf8view.byte_offset_of(it);
  2261. at_start_of_name = true;
  2262. continue;
  2263. }
  2264. if (at_start_of_name) {
  2265. if (!is_valid_name_start_character(code_point))
  2266. return WebIDL::InvalidCharacterError::create(realm, "Invalid start of qualified name."_fly_string);
  2267. at_start_of_name = false;
  2268. continue;
  2269. }
  2270. if (!is_valid_name_character(code_point))
  2271. return WebIDL::InvalidCharacterError::create(realm, "Invalid character in qualified name."_fly_string);
  2272. }
  2273. if (!colon_offset.has_value())
  2274. return Document::PrefixAndTagName {
  2275. .prefix = {},
  2276. .tag_name = qualified_name,
  2277. };
  2278. if (*colon_offset == 0)
  2279. return WebIDL::InvalidCharacterError::create(realm, "Qualified name can't start with colon (:)."_fly_string);
  2280. if (*colon_offset >= (qualified_name.bytes_as_string_view().length() - 1))
  2281. return WebIDL::InvalidCharacterError::create(realm, "Qualified name can't end with colon (:)."_fly_string);
  2282. return Document::PrefixAndTagName {
  2283. .prefix = MUST(FlyString::from_utf8(qualified_name.bytes_as_string_view().substring_view(0, *colon_offset))),
  2284. .tag_name = MUST(FlyString::from_utf8(qualified_name.bytes_as_string_view().substring_view(*colon_offset + 1))),
  2285. };
  2286. }
  2287. // https://dom.spec.whatwg.org/#dom-document-createnodeiterator
  2288. JS::NonnullGCPtr<NodeIterator> Document::create_node_iterator(Node& root, unsigned what_to_show, JS::GCPtr<NodeFilter> filter)
  2289. {
  2290. return NodeIterator::create(root, what_to_show, filter).release_value_but_fixme_should_propagate_errors();
  2291. }
  2292. // https://dom.spec.whatwg.org/#dom-document-createtreewalker
  2293. JS::NonnullGCPtr<TreeWalker> Document::create_tree_walker(Node& root, unsigned what_to_show, JS::GCPtr<NodeFilter> filter)
  2294. {
  2295. return TreeWalker::create(root, what_to_show, filter);
  2296. }
  2297. void Document::register_node_iterator(Badge<NodeIterator>, NodeIterator& node_iterator)
  2298. {
  2299. auto result = m_node_iterators.set(&node_iterator);
  2300. VERIFY(result == AK::HashSetResult::InsertedNewEntry);
  2301. }
  2302. void Document::unregister_node_iterator(Badge<NodeIterator>, NodeIterator& node_iterator)
  2303. {
  2304. bool was_removed = m_node_iterators.remove(&node_iterator);
  2305. VERIFY(was_removed);
  2306. }
  2307. void Document::register_document_observer(Badge<DocumentObserver>, DocumentObserver& document_observer)
  2308. {
  2309. auto result = m_document_observers.set(document_observer);
  2310. VERIFY(result == AK::HashSetResult::InsertedNewEntry);
  2311. }
  2312. void Document::unregister_document_observer(Badge<DocumentObserver>, DocumentObserver& document_observer)
  2313. {
  2314. bool was_removed = m_document_observers.remove(document_observer);
  2315. VERIFY(was_removed);
  2316. }
  2317. void Document::increment_number_of_things_delaying_the_load_event(Badge<DocumentLoadEventDelayer>)
  2318. {
  2319. ++m_number_of_things_delaying_the_load_event;
  2320. page().client().page_did_update_resource_count(m_number_of_things_delaying_the_load_event);
  2321. }
  2322. void Document::decrement_number_of_things_delaying_the_load_event(Badge<DocumentLoadEventDelayer>)
  2323. {
  2324. VERIFY(m_number_of_things_delaying_the_load_event);
  2325. --m_number_of_things_delaying_the_load_event;
  2326. page().client().page_did_update_resource_count(m_number_of_things_delaying_the_load_event);
  2327. }
  2328. bool Document::anything_is_delaying_the_load_event() const
  2329. {
  2330. if (m_number_of_things_delaying_the_load_event > 0)
  2331. return true;
  2332. for (auto& navigable : descendant_navigables()) {
  2333. if (navigable->container()->currently_delays_the_load_event())
  2334. return true;
  2335. }
  2336. // FIXME: Track down anything else that is supposed to delay the load event.
  2337. return false;
  2338. }
  2339. void Document::invalidate_stacking_context_tree()
  2340. {
  2341. if (auto* paintable_box = this->paintable_box())
  2342. paintable_box->invalidate_stacking_context();
  2343. }
  2344. void Document::check_favicon_after_loading_link_resource()
  2345. {
  2346. // https://html.spec.whatwg.org/multipage/links.html#rel-icon
  2347. // NOTE: firefox also load favicons outside the head tag, which is against spec (see table 4.6.7)
  2348. auto* head_element = head();
  2349. if (!head_element)
  2350. return;
  2351. auto favicon_link_elements = HTMLCollection::create(*head_element, HTMLCollection::Scope::Descendants, [](Element const& element) {
  2352. if (!is<HTML::HTMLLinkElement>(element))
  2353. return false;
  2354. return static_cast<HTML::HTMLLinkElement const&>(element).has_loaded_icon();
  2355. });
  2356. if (favicon_link_elements->length() == 0) {
  2357. dbgln_if(SPAM_DEBUG, "No favicon found to be used");
  2358. return;
  2359. }
  2360. // 4.6.7.8 Link type "icon"
  2361. //
  2362. // If there are multiple equally appropriate icons, user agents must use the last one declared
  2363. // in tree order at the time that the user agent collected the list of icons.
  2364. //
  2365. // If multiple icons are provided, the user agent must select the most appropriate icon
  2366. // according to the type, media, and sizes attributes.
  2367. //
  2368. // FIXME: There is no selective behavior yet for favicons.
  2369. for (auto i = favicon_link_elements->length(); i-- > 0;) {
  2370. auto favicon_element = favicon_link_elements->item(i);
  2371. if (favicon_element == m_active_element.ptr())
  2372. return;
  2373. // If the user agent tries to use an icon but that icon is determined, upon closer examination,
  2374. // to in fact be inappropriate (...), then the user agent must try the next-most-appropriate icon
  2375. // as determined by the attributes.
  2376. if (static_cast<HTML::HTMLLinkElement*>(favicon_element)->load_favicon_and_use_if_window_is_active()) {
  2377. m_active_favicon = favicon_element;
  2378. return;
  2379. }
  2380. }
  2381. dbgln_if(SPAM_DEBUG, "No favicon found to be used");
  2382. }
  2383. void Document::set_window(HTML::Window& window)
  2384. {
  2385. m_window = &window;
  2386. }
  2387. // https://html.spec.whatwg.org/multipage/custom-elements.html#look-up-a-custom-element-definition
  2388. JS::GCPtr<HTML::CustomElementDefinition> Document::lookup_custom_element_definition(Optional<FlyString> const& namespace_, FlyString const& local_name, Optional<String> const& is) const
  2389. {
  2390. // 1. If namespace is not the HTML namespace, return null.
  2391. if (namespace_ != Namespace::HTML)
  2392. return nullptr;
  2393. // 2. If document's browsing context is null, return null.
  2394. if (!browsing_context())
  2395. return nullptr;
  2396. // 3. Let registry be document's relevant global object's CustomElementRegistry object.
  2397. auto registry = verify_cast<HTML::Window>(relevant_global_object(*this)).custom_elements();
  2398. // 4. If there is custom element definition in registry with name and local name both equal to localName, return that custom element definition.
  2399. auto converted_local_name = local_name;
  2400. auto maybe_definition = registry->get_definition_with_name_and_local_name(converted_local_name.to_string(), converted_local_name.to_string());
  2401. if (maybe_definition)
  2402. return maybe_definition;
  2403. // 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.
  2404. // 6. Return null.
  2405. // 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>)
  2406. if (!is.has_value())
  2407. return nullptr;
  2408. return registry->get_definition_with_name_and_local_name(is.value(), converted_local_name.to_string());
  2409. }
  2410. CSS::StyleSheetList& Document::style_sheets()
  2411. {
  2412. if (!m_style_sheets)
  2413. m_style_sheets = CSS::StyleSheetList::create(*this);
  2414. return *m_style_sheets;
  2415. }
  2416. CSS::StyleSheetList const& Document::style_sheets() const
  2417. {
  2418. return const_cast<Document*>(this)->style_sheets();
  2419. }
  2420. JS::NonnullGCPtr<HTML::History> Document::history()
  2421. {
  2422. if (!m_history)
  2423. m_history = HTML::History::create(realm(), *this);
  2424. return *m_history;
  2425. }
  2426. JS::NonnullGCPtr<HTML::History> Document::history() const
  2427. {
  2428. return const_cast<Document*>(this)->history();
  2429. }
  2430. // https://html.spec.whatwg.org/multipage/origin.html#dom-document-domain
  2431. String Document::domain() const
  2432. {
  2433. // 1. Let effectiveDomain be this's origin's effective domain.
  2434. auto effective_domain = origin().effective_domain();
  2435. // 2. If effectiveDomain is null, then return the empty string.
  2436. if (!effective_domain.has_value())
  2437. return String {};
  2438. // 3. Return effectiveDomain, serialized.
  2439. return MUST(URL::Parser::serialize_host(effective_domain.release_value()));
  2440. }
  2441. void Document::set_domain(String const& domain)
  2442. {
  2443. dbgln("(STUBBED) Document::set_domain(domain='{}')", domain);
  2444. }
  2445. void Document::set_navigation_id(Optional<String> navigation_id)
  2446. {
  2447. m_navigation_id = move(navigation_id);
  2448. }
  2449. Optional<String> Document::navigation_id() const
  2450. {
  2451. return m_navigation_id;
  2452. }
  2453. HTML::SandboxingFlagSet Document::active_sandboxing_flag_set() const
  2454. {
  2455. return m_active_sandboxing_flag_set;
  2456. }
  2457. HTML::PolicyContainer Document::policy_container() const
  2458. {
  2459. return m_policy_container;
  2460. }
  2461. void Document::set_policy_container(HTML::PolicyContainer policy_container)
  2462. {
  2463. m_policy_container = move(policy_container);
  2464. }
  2465. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#snapshotting-source-snapshot-params
  2466. HTML::SourceSnapshotParams Document::snapshot_source_snapshot_params() const
  2467. {
  2468. // To snapshot source snapshot params given a Document sourceDocument, return a new source snapshot params with
  2469. // has transient activation
  2470. // true if sourceDocument's relevant global object has transient activation; otherwise false
  2471. // sandboxing flags
  2472. // sourceDocument's active sandboxing flag set
  2473. // allows downloading
  2474. // false if sourceDocument's active sandboxing flag set has the sandboxed downloads browsing context flag set; otherwise true
  2475. // fetch client
  2476. // sourceDocument's relevant settings object
  2477. // source policy container
  2478. // sourceDocument's policy container
  2479. return HTML::SourceSnapshotParams {
  2480. .has_transient_activation = verify_cast<HTML::Window>(HTML::relevant_global_object(*this)).has_transient_activation(),
  2481. .sandboxing_flags = m_active_sandboxing_flag_set,
  2482. .allows_downloading = !has_flag(m_active_sandboxing_flag_set, HTML::SandboxingFlagSet::SandboxedDownloads),
  2483. .fetch_client = relevant_settings_object(),
  2484. .source_policy_container = m_policy_container
  2485. };
  2486. }
  2487. // https://html.spec.whatwg.org/multipage/document-sequences.html#descendant-navigables
  2488. Vector<JS::Handle<HTML::Navigable>> Document::descendant_navigables()
  2489. {
  2490. // 1. Let navigables be new list.
  2491. Vector<JS::Handle<HTML::Navigable>> navigables;
  2492. // 2. Let navigableContainers be a list of all shadow-including descendants of document that are navigable containers, in shadow-including tree order.
  2493. // 3. For each navigableContainer of navigableContainers:
  2494. for_each_shadow_including_descendant([&](DOM::Node& node) {
  2495. if (is<HTML::NavigableContainer>(node)) {
  2496. auto& navigable_container = static_cast<HTML::NavigableContainer&>(node);
  2497. // 1. If navigableContainer's content navigable is null, then continue.
  2498. if (!navigable_container.content_navigable())
  2499. return TraversalDecision::Continue;
  2500. // 2. Extend navigables with navigableContainer's content navigable's active document's inclusive descendant navigables.
  2501. navigables.extend(navigable_container.content_navigable()->active_document()->inclusive_descendant_navigables());
  2502. }
  2503. return TraversalDecision::Continue;
  2504. });
  2505. // 4. Return navigables.
  2506. return navigables;
  2507. }
  2508. Vector<JS::Handle<HTML::Navigable>> const Document::descendant_navigables() const
  2509. {
  2510. return const_cast<Document&>(*this).descendant_navigables();
  2511. }
  2512. // https://html.spec.whatwg.org/multipage/document-sequences.html#inclusive-descendant-navigables
  2513. Vector<JS::Handle<HTML::Navigable>> Document::inclusive_descendant_navigables()
  2514. {
  2515. // 1. Let navigables be « document's node navigable ».
  2516. Vector<JS::Handle<HTML::Navigable>> navigables;
  2517. navigables.append(*navigable());
  2518. // 2. Extend navigables with document's descendant navigables.
  2519. navigables.extend(descendant_navigables());
  2520. // 3. Return navigables.
  2521. return navigables;
  2522. }
  2523. // https://html.spec.whatwg.org/multipage/document-sequences.html#ancestor-navigables
  2524. Vector<JS::Handle<HTML::Navigable>> Document::ancestor_navigables()
  2525. {
  2526. // NOTE: This isn't in the spec, but if we don't have a navigable, we can't have ancestors either.
  2527. auto document_node_navigable = this->navigable();
  2528. if (!document_node_navigable)
  2529. return {};
  2530. // 1. Let navigable be document's node navigable's parent.
  2531. auto navigable = document_node_navigable->parent();
  2532. // 2. Let ancestors be an empty list.
  2533. Vector<JS::Handle<HTML::Navigable>> ancestors;
  2534. // 3. While navigable is not null:
  2535. while (navigable) {
  2536. // 1. Prepend navigable to ancestors.
  2537. ancestors.prepend(*navigable);
  2538. // 2. Set navigable to navigable's parent.
  2539. navigable = navigable->parent();
  2540. }
  2541. // 4. Return ancestors.
  2542. return ancestors;
  2543. }
  2544. Vector<JS::Handle<HTML::Navigable>> const Document::ancestor_navigables() const
  2545. {
  2546. return const_cast<Document&>(*this).ancestor_navigables();
  2547. }
  2548. // https://html.spec.whatwg.org/multipage/document-sequences.html#inclusive-ancestor-navigables
  2549. Vector<JS::Handle<HTML::Navigable>> Document::inclusive_ancestor_navigables()
  2550. {
  2551. // 1. Let navigables be document's ancestor navigables.
  2552. auto navigables = ancestor_navigables();
  2553. // 2. Append document's node navigable to navigables.
  2554. navigables.append(*navigable());
  2555. // 3. Return navigables.
  2556. return navigables;
  2557. }
  2558. // https://html.spec.whatwg.org/multipage/document-sequences.html#document-tree-child-navigables
  2559. Vector<JS::Handle<HTML::Navigable>> Document::document_tree_child_navigables()
  2560. {
  2561. // 1. If document's node navigable is null, then return the empty list.
  2562. if (!navigable())
  2563. return {};
  2564. // 2. Let navigables be new list.
  2565. Vector<JS::Handle<HTML::Navigable>> navigables;
  2566. // 3. Let navigableContainers be a list of all descendants of document that are navigable containers, in tree order.
  2567. // 4. For each navigableContainer of navigableContainers:
  2568. for_each_in_subtree_of_type<HTML::NavigableContainer>([&](HTML::NavigableContainer& navigable_container) {
  2569. // 1. If navigableContainer's content navigable is null, then continue.
  2570. if (!navigable_container.content_navigable())
  2571. return TraversalDecision::Continue;
  2572. // 2. Append navigableContainer's content navigable to navigables.
  2573. navigables.append(*navigable_container.content_navigable());
  2574. return TraversalDecision::Continue;
  2575. });
  2576. // 5. Return navigables.
  2577. return navigables;
  2578. }
  2579. // https://html.spec.whatwg.org/multipage/document-lifecycle.html#unloading-document-cleanup-steps
  2580. void Document::run_unloading_cleanup_steps()
  2581. {
  2582. // 1. Let window be document's relevant global object.
  2583. auto* window = dynamic_cast<HTML::WindowOrWorkerGlobalScopeMixin*>(&HTML::relevant_global_object(*this));
  2584. VERIFY(window);
  2585. // FIXME: 2. For each WebSocket object webSocket whose relevant global object is window, make disappear webSocket.
  2586. // If this affected any WebSocket objects, then set document's salvageable state to false.
  2587. // FIXME: 3. For each WebTransport object transport whose relevant global object is window, run the context cleanup steps given transport.
  2588. // 4. If document's salvageable state is false, then:
  2589. if (!m_salvageable) {
  2590. // 1. For each EventSource object eventSource whose relevant global object is equal to window, forcibly close eventSource.
  2591. window->forcibly_close_all_event_sources();
  2592. // 2. Clear window's map of active timers.
  2593. window->clear_map_of_active_timers();
  2594. }
  2595. FileAPI::run_unloading_cleanup_steps(*this);
  2596. }
  2597. // https://html.spec.whatwg.org/multipage/document-lifecycle.html#destroy-a-document
  2598. void Document::destroy()
  2599. {
  2600. // NOTE: Abort needs to happen before destory. There is currently bug in the spec: https://github.com/whatwg/html/issues/9148
  2601. // 4. Abort document.
  2602. abort();
  2603. // 2. Set document's salvageable state to false.
  2604. m_salvageable = false;
  2605. // 3. Run any unloading document cleanup steps for document that are defined by this specification and other applicable specifications.
  2606. run_unloading_cleanup_steps();
  2607. // 5. Remove any tasks whose document is document from any task queue (without running those tasks).
  2608. HTML::main_thread_event_loop().task_queue().remove_tasks_matching([this](auto& task) {
  2609. return task.document() == this;
  2610. });
  2611. // 6. Set document's browsing context to null.
  2612. m_browsing_context = nullptr;
  2613. // Not in the spec:
  2614. for (auto& navigable_container : HTML::NavigableContainer::all_instances()) {
  2615. if (&navigable_container->document() == this)
  2616. HTML::all_navigables().remove(navigable_container->content_navigable());
  2617. }
  2618. // 7. Set document's node navigable's active session history entry's document state's document to null.
  2619. if (navigable()) {
  2620. navigable()->active_session_history_entry()->document_state()->set_document(nullptr);
  2621. }
  2622. // FIXME: 8. Remove document from the owner set of each WorkerGlobalScope object whose set contains document.
  2623. // FIXME: 9. For each workletGlobalScope in document's worklet global scopes, terminate workletGlobalScope.
  2624. }
  2625. // https://html.spec.whatwg.org/multipage/document-lifecycle.html#destroy-a-document-and-its-descendants
  2626. void Document::destroy_a_document_and_its_descendants(JS::GCPtr<JS::HeapFunction<void()>> after_all_destruction)
  2627. {
  2628. // 1. Let childNavigables be document's child navigables.
  2629. auto child_navigables = document_tree_child_navigables();
  2630. // 3. Let numberDestroyed be 0.
  2631. IGNORE_USE_IN_ESCAPING_LAMBDA size_t number_destroyed = 0;
  2632. // 3. For each childNavigable of childNavigable's, queue a global task on the navigation and traversal task source
  2633. // given childNavigable's active window to perform the following steps:
  2634. for (auto& child_navigable : child_navigables) {
  2635. HTML::queue_global_task(HTML::Task::Source::NavigationAndTraversal, *child_navigable->active_window(), JS::create_heap_function(heap(), [&heap = heap(), &number_destroyed, child_navigable = child_navigable.ptr()] {
  2636. // 1. Let incrementDestroyed be an algorithm step which increments numberDestroyed.
  2637. auto increment_destroyed = JS::create_heap_function(heap, [&number_destroyed] { ++number_destroyed; });
  2638. // 2. Destroy a document and its descendants given childNavigable's active document and incrementDestroyed.
  2639. child_navigable->active_document()->destroy_a_document_and_its_descendants(move(increment_destroyed));
  2640. }));
  2641. }
  2642. // 4. Wait until numberDestroyed equals childNavigable's size.
  2643. HTML::main_thread_event_loop().spin_until([&] {
  2644. return number_destroyed == child_navigables.size();
  2645. });
  2646. // 4. Queue a global task on the navigation and traversal task source given document's relevant global object to perform the following steps:
  2647. HTML::queue_global_task(HTML::Task::Source::NavigationAndTraversal, relevant_global_object(*this), JS::create_heap_function(heap(), [after_all_destruction = move(after_all_destruction), this] {
  2648. // 1. Destroy document.
  2649. destroy();
  2650. // 2. If afterAllDestruction was given, then run it.
  2651. if (after_all_destruction)
  2652. after_all_destruction->function()();
  2653. }));
  2654. }
  2655. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#abort-a-document
  2656. void Document::abort()
  2657. {
  2658. // 1. Assert: this is running as part of a task queued on document's relevant agent's event loop.
  2659. // FIXME: 2. Cancel any instances of the fetch algorithm in the context of document,
  2660. // discarding any tasks queued for them, and discarding any further data received from the network for them.
  2661. // If this resulted in any instances of the fetch algorithm being canceled
  2662. // or any queued tasks or any network data getting discarded,
  2663. // then set document's salvageable state to false.
  2664. // 3. If document's during-loading navigation ID for WebDriver BiDi is non-null, then:
  2665. if (m_navigation_id.has_value()) {
  2666. // 1. FIXME: Invoke WebDriver BiDi navigation aborted with document's browsing context,
  2667. // and new WebDriver BiDi navigation status whose whose id is document's navigation id,
  2668. // status is "canceled", and url is document's URL.
  2669. // 2. Set document's during-loading navigation ID for WebDriver BiDi to null.
  2670. m_navigation_id = {};
  2671. }
  2672. // 4. If document has an active parser, then:
  2673. if (auto parser = active_parser()) {
  2674. // 1. Set document's active parser was aborted to true.
  2675. m_active_parser_was_aborted = true;
  2676. // 2. Abort that parser.
  2677. parser->abort();
  2678. // 3. Set document's salvageable state to false.
  2679. m_salvageable = false;
  2680. }
  2681. }
  2682. // https://html.spec.whatwg.org/multipage/document-lifecycle.html#abort-a-document-and-its-descendants
  2683. void Document::abort_a_document_and_its_descendants()
  2684. {
  2685. // FIXME 1. Assert: this is running as part of a task queued on document's relevant agent's event loop.
  2686. // 2. Let descendantNavigables be document's descendant navigables.
  2687. auto descendant_navigables = this->descendant_navigables();
  2688. // 3. For each descendantNavigable of descendantNavigables, queue a global task on the navigation and traversal task source given descendantNavigable's active window to perform the following steps:
  2689. for (auto& descendant_navigable : descendant_navigables) {
  2690. HTML::queue_global_task(HTML::Task::Source::NavigationAndTraversal, *descendant_navigable->active_window(), JS::create_heap_function(heap(), [this, descendant_navigable = descendant_navigable.ptr()] {
  2691. // NOTE: This is not in the spec but we need to abort ongoing navigations in all descendant navigables.
  2692. // See https://github.com/whatwg/html/issues/9711
  2693. descendant_navigable->set_ongoing_navigation({});
  2694. // 1. Abort descendantNavigable's active document.
  2695. descendant_navigable->active_document()->abort();
  2696. // 2. If descendantNavigable's active document's salvageable is false, then set document's salvageable to false.
  2697. if (!descendant_navigable->active_document()->m_salvageable)
  2698. m_salvageable = false;
  2699. }));
  2700. }
  2701. // 4. Abort document.
  2702. abort();
  2703. }
  2704. // https://html.spec.whatwg.org/multipage/dom.html#active-parser
  2705. JS::GCPtr<HTML::HTMLParser> Document::active_parser()
  2706. {
  2707. if (!m_parser)
  2708. return nullptr;
  2709. if (m_parser->aborted() || m_parser->stopped())
  2710. return nullptr;
  2711. return m_parser;
  2712. }
  2713. void Document::set_browsing_context(HTML::BrowsingContext* browsing_context)
  2714. {
  2715. m_browsing_context = browsing_context;
  2716. }
  2717. // https://html.spec.whatwg.org/multipage/document-lifecycle.html#unload-a-document
  2718. void Document::unload(JS::GCPtr<Document>)
  2719. {
  2720. auto& vm = this->vm();
  2721. // FIXME: 1. Assert: this is running as part of a task queued on oldDocument's event loop.
  2722. // FIXME: 2. Let unloadTimingInfo be a new document unload timing info.
  2723. // FIXME: 3. If newDocument is not given, then set unloadTimingInfo to null.
  2724. // 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
  2725. // set unloadTimingInfo to null.
  2726. // 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.
  2727. auto intend_to_store_in_bfcache = false;
  2728. // 6. Let eventLoop be oldDocument's relevant agent's event loop.
  2729. auto& event_loop = *verify_cast<Bindings::WebEngineCustomData>(*vm.custom_data()).event_loop;
  2730. // 7. Increase eventLoop's termination nesting level by 1.
  2731. event_loop.increment_termination_nesting_level();
  2732. // 8. Increase oldDocument's unload counter by 1.
  2733. m_unload_counter += 1;
  2734. // 9. If intendToKeepInBfcache is false, then set oldDocument's salvageable state to false.
  2735. if (!intend_to_store_in_bfcache) {
  2736. m_salvageable = false;
  2737. }
  2738. // 10. If oldDocument's page showing is true:
  2739. if (m_page_showing) {
  2740. // 1. Set oldDocument's page showing to false.
  2741. m_page_showing = false;
  2742. // 2. Fire a page transition event named pagehide at oldDocument's relevant global object with oldDocument's salvageable state.
  2743. verify_cast<HTML::Window>(relevant_global_object(*this)).fire_a_page_transition_event(HTML::EventNames::pagehide, m_salvageable);
  2744. // 3. Update the visibility state of oldDocument to "hidden".
  2745. update_the_visibility_state(HTML::VisibilityState::Hidden);
  2746. }
  2747. // 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
  2748. // given oldDocument's relevant settings object's cross-origin isolated capability.
  2749. // 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.
  2750. if (!m_salvageable) {
  2751. // then fire an event named unload at document's relevant global object, with legacy target override flag set.
  2752. // FIXME: The legacy target override flag is currently set by a virtual override of dispatch_event()
  2753. // We should reorganize this so that the flag appears explicitly here instead.
  2754. auto event = DOM::Event::create(realm(), HTML::EventNames::unload);
  2755. global_object().dispatch_event(event);
  2756. }
  2757. // 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
  2758. // given oldDocument's relevant settings object's cross-origin isolated capability.
  2759. // 14. Decrease eventLoop's termination nesting level by 1.
  2760. event_loop.decrement_termination_nesting_level();
  2761. // FIXME: 15. Set oldDocument's suspension time to the current high resolution time given document's relevant global object.
  2762. // FIXME: 16. Set oldDocument's suspended timer handles to the result of getting the keys for the map of active timers.
  2763. // FIXME: 17. Set oldDocument's has been scrolled by the user to false.
  2764. // FIXME: 18. Run any unloading document cleanup steps for oldDocument that are defined by this specification and other applicable specifications.
  2765. // 19. If oldDocument's salvageable state is false, then destroy oldDocument.
  2766. if (!m_salvageable) {
  2767. // NOTE: Document is destroyed from Document::unload_a_document_and_its_descendants()
  2768. }
  2769. // 20. Decrease oldDocument's unload counter by 1.
  2770. m_unload_counter -= 1;
  2771. // 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
  2772. // newDocument's previous document unload timing to unloadTimingInfo.
  2773. did_stop_being_active_document_in_navigable();
  2774. }
  2775. // https://html.spec.whatwg.org/multipage/document-lifecycle.html#unload-a-document-and-its-descendants
  2776. void Document::unload_a_document_and_its_descendants(JS::GCPtr<Document> new_document, JS::GCPtr<JS::HeapFunction<void()>> after_all_unloads)
  2777. {
  2778. // Specification defines this algorithm in the following steps:
  2779. // 1. Recursively unload (and destroy) documents in descendant navigables
  2780. // 2. Unload (and destroy) this document.
  2781. //
  2782. // Implementation of the spec will fail in the following scenario:
  2783. // 1. Unload iframe's (has attribute name="test") document
  2784. // 1.1. Destroy iframe's document
  2785. // 2. Unload iframe's parent document
  2786. // 2.1. Dispatch "unload" event
  2787. // 2.2. In "unload" event handler run `window["test"]`
  2788. // 2.2.1. Execute Window::document_tree_child_navigable_target_name_property_set()
  2789. // 2.2.1.1. Fail to access iframe's navigable active document because it was destroyed on step 1.1
  2790. //
  2791. // We change the algorithm to:
  2792. // 1. Unload all descendant documents without destroying them
  2793. // 2. Unload this document
  2794. // 3. Destroy all descendant documents
  2795. // 4. Destroy this document
  2796. //
  2797. // This way we maintain the invariant that all navigable containers present in the DOM tree
  2798. // have an active document while the document is being unloaded.
  2799. IGNORE_USE_IN_ESCAPING_LAMBDA size_t number_unloaded = 0;
  2800. auto navigable = this->navigable();
  2801. Vector<JS::Handle<HTML::Navigable>> descendant_navigables;
  2802. for (auto& other_navigable : HTML::all_navigables()) {
  2803. if (navigable->is_ancestor_of(*other_navigable))
  2804. descendant_navigables.append(other_navigable);
  2805. }
  2806. auto unloaded_documents_count = descendant_navigables.size() + 1;
  2807. HTML::queue_global_task(HTML::Task::Source::NavigationAndTraversal, HTML::relevant_global_object(*this), JS::create_heap_function(heap(), [&number_unloaded, this, new_document] {
  2808. unload(new_document);
  2809. ++number_unloaded;
  2810. }));
  2811. for (auto& descendant_navigable : descendant_navigables) {
  2812. HTML::queue_global_task(HTML::Task::Source::NavigationAndTraversal, *descendant_navigable->active_window(), JS::create_heap_function(heap(), [&number_unloaded, descendant_navigable = descendant_navigable.ptr()] {
  2813. descendant_navigable->active_document()->unload();
  2814. ++number_unloaded;
  2815. }));
  2816. }
  2817. HTML::main_thread_event_loop().spin_until([&] {
  2818. return number_unloaded == unloaded_documents_count;
  2819. });
  2820. destroy_a_document_and_its_descendants(move(after_all_unloads));
  2821. }
  2822. // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#allowed-to-use
  2823. bool Document::is_allowed_to_use_feature(PolicyControlledFeature feature) const
  2824. {
  2825. // 1. If document's browsing context is null, then return false.
  2826. if (browsing_context() == nullptr)
  2827. return false;
  2828. // 2. If document is not fully active, then return false.
  2829. if (!is_fully_active())
  2830. return false;
  2831. // 3. If the result of running is feature enabled in document for origin on feature, document, and document's origin
  2832. // is "Enabled", then return true.
  2833. // FIXME: This is ad-hoc. Implement the Permissions Policy specification.
  2834. switch (feature) {
  2835. case PolicyControlledFeature::Autoplay:
  2836. if (PermissionsPolicy::AutoplayAllowlist::the().is_allowed_for_origin(*this, origin()) == PermissionsPolicy::Decision::Enabled)
  2837. return true;
  2838. break;
  2839. }
  2840. // 4. Return false.
  2841. return false;
  2842. }
  2843. void Document::did_stop_being_active_document_in_navigable()
  2844. {
  2845. tear_down_layout_tree();
  2846. auto observers_to_notify = m_document_observers.values();
  2847. for (auto& document_observer : observers_to_notify) {
  2848. if (document_observer->document_became_inactive())
  2849. document_observer->document_became_inactive()->function()();
  2850. }
  2851. if (m_animation_driver_timer)
  2852. m_animation_driver_timer->stop();
  2853. }
  2854. void Document::increment_throw_on_dynamic_markup_insertion_counter(Badge<HTML::HTMLParser>)
  2855. {
  2856. ++m_throw_on_dynamic_markup_insertion_counter;
  2857. }
  2858. void Document::decrement_throw_on_dynamic_markup_insertion_counter(Badge<HTML::HTMLParser>)
  2859. {
  2860. VERIFY(m_throw_on_dynamic_markup_insertion_counter);
  2861. --m_throw_on_dynamic_markup_insertion_counter;
  2862. }
  2863. // https://html.spec.whatwg.org/multipage/scripting.html#appropriate-template-contents-owner-document
  2864. JS::NonnullGCPtr<DOM::Document> Document::appropriate_template_contents_owner_document()
  2865. {
  2866. // 1. If doc is not a Document created by this algorithm, then:
  2867. if (!created_for_appropriate_template_contents()) {
  2868. // 1. If doc does not yet have an associated inert template document, then:
  2869. if (!m_associated_inert_template_document) {
  2870. // 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.
  2871. auto new_document = HTML::HTMLDocument::create(realm());
  2872. new_document->m_created_for_appropriate_template_contents = true;
  2873. // 2. If doc is an HTML document, mark new doc as an HTML document also.
  2874. if (document_type() == Type::HTML)
  2875. new_document->set_document_type(Type::HTML);
  2876. // 3. Let doc's associated inert template document be new doc.
  2877. m_associated_inert_template_document = new_document;
  2878. }
  2879. // 2. Set doc to doc's associated inert template document.
  2880. return *m_associated_inert_template_document;
  2881. }
  2882. // 2. Return doc.
  2883. return *this;
  2884. }
  2885. String Document::dump_accessibility_tree_as_json()
  2886. {
  2887. StringBuilder builder;
  2888. auto accessibility_tree = AccessibilityTreeNode::create(this, nullptr);
  2889. build_accessibility_tree(*&accessibility_tree);
  2890. auto json = MUST(JsonObjectSerializer<>::try_create(builder));
  2891. // Empty document
  2892. if (!accessibility_tree->value()) {
  2893. MUST(json.add("type"sv, "element"sv));
  2894. MUST(json.add("role"sv, "document"sv));
  2895. } else {
  2896. accessibility_tree->serialize_tree_as_json(json, *this);
  2897. }
  2898. MUST(json.finish());
  2899. return MUST(builder.to_string());
  2900. }
  2901. // https://dom.spec.whatwg.org/#dom-document-createattribute
  2902. WebIDL::ExceptionOr<JS::NonnullGCPtr<Attr>> Document::create_attribute(String const& local_name)
  2903. {
  2904. // 1. If localName does not match the Name production in XML, then throw an "InvalidCharacterError" DOMException.
  2905. if (!is_valid_name(local_name))
  2906. return WebIDL::InvalidCharacterError::create(realm(), "Invalid character in attribute name."_fly_string);
  2907. // 2. If this is an HTML document, then set localName to localName in ASCII lowercase.
  2908. // 3. Return a new attribute whose local name is localName and node document is this.
  2909. return Attr::create(*this, is_html_document() ? MUST(Infra::to_ascii_lowercase(local_name)) : local_name);
  2910. }
  2911. // https://dom.spec.whatwg.org/#dom-document-createattributens
  2912. WebIDL::ExceptionOr<JS::NonnullGCPtr<Attr>> Document::create_attribute_ns(Optional<FlyString> const& namespace_, String const& qualified_name)
  2913. {
  2914. // 1. Let namespace, prefix, and localName be the result of passing namespace and qualifiedName to validate and extract.
  2915. auto extracted_qualified_name = TRY(validate_and_extract(realm(), namespace_, qualified_name));
  2916. // 2. Return a new attribute whose namespace is namespace, namespace prefix is prefix, local name is localName, and node document is this.
  2917. return Attr::create(*this, extracted_qualified_name);
  2918. }
  2919. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#make-active
  2920. void Document::make_active()
  2921. {
  2922. // 1. Let window be document's relevant global object.
  2923. auto& window = verify_cast<HTML::Window>(HTML::relevant_global_object(*this));
  2924. set_window(window);
  2925. // 2. Set document's browsing context's WindowProxy's [[Window]] internal slot value to window.
  2926. m_browsing_context->window_proxy()->set_window(window);
  2927. if (m_browsing_context->is_top_level()) {
  2928. page().client().page_did_change_active_document_in_top_level_browsing_context(*this);
  2929. }
  2930. // 3. Set document's visibility state to document's node navigable's traversable navigable's system visibility state.
  2931. if (navigable()) {
  2932. m_visibility_state = navigable()->traversable_navigable()->system_visibility_state();
  2933. }
  2934. // 4. Set window's relevant settings object's execution ready flag.
  2935. HTML::relevant_settings_object(window).execution_ready = true;
  2936. if (m_needs_to_call_page_did_load) {
  2937. navigable()->traversable_navigable()->page().client().page_did_finish_loading(url());
  2938. m_needs_to_call_page_did_load = false;
  2939. }
  2940. }
  2941. HTML::ListOfAvailableImages& Document::list_of_available_images()
  2942. {
  2943. return *m_list_of_available_images;
  2944. }
  2945. HTML::ListOfAvailableImages const& Document::list_of_available_images() const
  2946. {
  2947. return *m_list_of_available_images;
  2948. }
  2949. CSSPixelRect Document::viewport_rect() const
  2950. {
  2951. if (auto const navigable = this->navigable())
  2952. return navigable->viewport_rect();
  2953. return CSSPixelRect {};
  2954. }
  2955. JS::NonnullGCPtr<CSS::VisualViewport> Document::visual_viewport()
  2956. {
  2957. if (!m_visual_viewport)
  2958. m_visual_viewport = CSS::VisualViewport::create(*this);
  2959. return *m_visual_viewport;
  2960. }
  2961. void Document::register_viewport_client(ViewportClient& client)
  2962. {
  2963. auto result = m_viewport_clients.set(&client);
  2964. VERIFY(result == AK::HashSetResult::InsertedNewEntry);
  2965. }
  2966. void Document::unregister_viewport_client(ViewportClient& client)
  2967. {
  2968. bool was_removed = m_viewport_clients.remove(&client);
  2969. VERIFY(was_removed);
  2970. }
  2971. void Document::inform_all_viewport_clients_about_the_current_viewport_rect()
  2972. {
  2973. for (auto* client : m_viewport_clients)
  2974. client->did_set_viewport_rect(viewport_rect());
  2975. }
  2976. void Document::register_intersection_observer(Badge<IntersectionObserver::IntersectionObserver>, IntersectionObserver::IntersectionObserver& observer)
  2977. {
  2978. auto result = m_intersection_observers.set(observer);
  2979. VERIFY(result == AK::HashSetResult::InsertedNewEntry);
  2980. }
  2981. void Document::unregister_intersection_observer(Badge<IntersectionObserver::IntersectionObserver>, IntersectionObserver::IntersectionObserver& observer)
  2982. {
  2983. bool was_removed = m_intersection_observers.remove(observer);
  2984. VERIFY(was_removed);
  2985. }
  2986. void Document::register_resize_observer(Badge<ResizeObserver::ResizeObserver>, ResizeObserver::ResizeObserver& observer)
  2987. {
  2988. m_resize_observers.append(observer);
  2989. }
  2990. void Document::unregister_resize_observer(Badge<ResizeObserver::ResizeObserver>, ResizeObserver::ResizeObserver& observer)
  2991. {
  2992. m_resize_observers.remove_first_matching([&](auto& registered_observer) {
  2993. return registered_observer.ptr() == &observer;
  2994. });
  2995. }
  2996. // https://www.w3.org/TR/intersection-observer/#queue-an-intersection-observer-task
  2997. void Document::queue_intersection_observer_task()
  2998. {
  2999. auto window = this->window();
  3000. if (!window)
  3001. return;
  3002. // 1. If document’s IntersectionObserverTaskQueued flag is set to true, return.
  3003. if (m_intersection_observer_task_queued)
  3004. return;
  3005. // 2. Set document’s IntersectionObserverTaskQueued flag to true.
  3006. m_intersection_observer_task_queued = true;
  3007. // 3. Queue a task on the IntersectionObserver task source associated with the document's event loop to notify intersection observers.
  3008. HTML::queue_global_task(HTML::Task::Source::IntersectionObserver, *window, JS::create_heap_function(heap(), [this]() {
  3009. auto& realm = this->realm();
  3010. // https://www.w3.org/TR/intersection-observer/#notify-intersection-observers
  3011. // 1. Set document’s IntersectionObserverTaskQueued flag to false.
  3012. m_intersection_observer_task_queued = false;
  3013. // 2. Let notify list be a list of all IntersectionObservers whose root is in the DOM tree of document.
  3014. Vector<JS::Handle<IntersectionObserver::IntersectionObserver>> notify_list;
  3015. notify_list.try_ensure_capacity(m_intersection_observers.size()).release_value_but_fixme_should_propagate_errors();
  3016. for (auto& observer : m_intersection_observers) {
  3017. notify_list.append(JS::make_handle(observer));
  3018. }
  3019. // 3. For each IntersectionObserver object observer in notify list, run these steps:
  3020. for (auto& observer : notify_list) {
  3021. // 2. Let queue be a copy of observer’s internal [[QueuedEntries]] slot.
  3022. // 3. Clear observer’s internal [[QueuedEntries]] slot.
  3023. auto queue = observer->take_records();
  3024. // 1. If observer’s internal [[QueuedEntries]] slot is empty, continue.
  3025. if (queue.is_empty())
  3026. continue;
  3027. auto wrapped_queue = MUST(JS::Array::create(realm, 0));
  3028. for (size_t i = 0; i < queue.size(); ++i) {
  3029. auto& record = queue.at(i);
  3030. auto property_index = JS::PropertyKey { i };
  3031. MUST(wrapped_queue->create_data_property(property_index, record.ptr()));
  3032. }
  3033. // 4. Let callback be the value of observer’s internal [[callback]] slot.
  3034. auto& callback = observer->callback();
  3035. // 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.
  3036. auto completion = WebIDL::invoke_callback(callback, observer.ptr(), wrapped_queue, observer.ptr());
  3037. if (completion.is_abrupt())
  3038. HTML::report_exception(completion, realm);
  3039. }
  3040. }));
  3041. }
  3042. // https://www.w3.org/TR/intersection-observer/#queue-an-intersectionobserverentry
  3043. 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)
  3044. {
  3045. auto& realm = this->realm();
  3046. // 1. Construct an IntersectionObserverEntry, passing in time, rootBounds, boundingClientRect, intersectionRect, isIntersecting, and target.
  3047. auto entry = realm.heap().allocate<IntersectionObserver::IntersectionObserverEntry>(realm, realm, time, root_bounds, bounding_client_rect, intersection_rect, is_intersecting, intersection_ratio, target);
  3048. // 2. Append it to observer’s internal [[QueuedEntries]] slot.
  3049. observer.queue_entry({}, entry);
  3050. // 3. Queue an intersection observer task for document.
  3051. queue_intersection_observer_task();
  3052. }
  3053. // https://www.w3.org/TR/intersection-observer/#compute-the-intersection
  3054. static JS::NonnullGCPtr<Geometry::DOMRectReadOnly> compute_intersection(JS::NonnullGCPtr<Element> target, IntersectionObserver::IntersectionObserver const& observer)
  3055. {
  3056. // 1. Let intersectionRect be the result of getting the bounding box for target.
  3057. auto intersection_rect = target->get_bounding_client_rect();
  3058. // FIXME: 2. Let container be the containing block of target.
  3059. // FIXME: 3. While container is not root:
  3060. // FIXME: 1. If container is the document of a nested browsing context, update intersectionRect by clipping to
  3061. // the viewport of the document, and update container to be the browsing context container of container.
  3062. // FIXME: 2. Map intersectionRect to the coordinate space of container.
  3063. // FIXME: 3. If container has a content clip or a css clip-path property, update intersectionRect by applying
  3064. // container’s clip.
  3065. // FIXME: 4. If container is the root element of a browsing context, update container to be the browsing context’s
  3066. // document; otherwise, update container to be the containing block of container.
  3067. // FIXME: 4. Map intersectionRect to the coordinate space of root.
  3068. // 5. Update intersectionRect by intersecting it with the root intersection rectangle.
  3069. // FIXME: Pass in target so we can properly apply rootMargin.
  3070. auto root_intersection_rectangle = observer.root_intersection_rectangle();
  3071. CSSPixelRect intersection_rect_as_pixel_rect(intersection_rect->x(), intersection_rect->y(), intersection_rect->width(), intersection_rect->height());
  3072. intersection_rect_as_pixel_rect.intersect(root_intersection_rectangle);
  3073. intersection_rect->set_x(static_cast<double>(intersection_rect_as_pixel_rect.x()));
  3074. intersection_rect->set_y(static_cast<double>(intersection_rect_as_pixel_rect.y()));
  3075. intersection_rect->set_width(static_cast<double>(intersection_rect_as_pixel_rect.width()));
  3076. intersection_rect->set_height(static_cast<double>(intersection_rect_as_pixel_rect.height()));
  3077. // FIXME: 6. Map intersectionRect to the coordinate space of the viewport of the document containing target.
  3078. // 7. Return intersectionRect.
  3079. return intersection_rect;
  3080. }
  3081. // https://www.w3.org/TR/intersection-observer/#run-the-update-intersection-observations-steps
  3082. void Document::run_the_update_intersection_observations_steps(HighResolutionTime::DOMHighResTimeStamp time)
  3083. {
  3084. auto& realm = this->realm();
  3085. // 1. Let observer list be a list of all IntersectionObservers whose root is in the DOM tree of document.
  3086. // For the top-level browsing context, this includes implicit root observers.
  3087. // 2. For each observer in observer list:
  3088. for (auto& observer : m_intersection_observers) {
  3089. // 1. Let rootBounds be observer’s root intersection rectangle.
  3090. auto root_bounds = observer->root_intersection_rectangle();
  3091. // 2. For each target in observer’s internal [[ObservationTargets]] slot, processed in the same order that
  3092. // observe() was called on each target:
  3093. for (auto& target : observer->observation_targets()) {
  3094. // 1. Let:
  3095. // thresholdIndex be 0.
  3096. size_t threshold_index = 0;
  3097. // isIntersecting be false.
  3098. bool is_intersecting = false;
  3099. // targetRect be a DOMRectReadOnly with x, y, width, and height set to 0.
  3100. auto target_rect = Geometry::DOMRectReadOnly::construct_impl(realm, 0, 0, 0, 0).release_value_but_fixme_should_propagate_errors();
  3101. // intersectionRect be a DOMRectReadOnly with x, y, width, and height set to 0.
  3102. auto intersection_rect = Geometry::DOMRectReadOnly::construct_impl(realm, 0, 0, 0, 0).release_value_but_fixme_should_propagate_errors();
  3103. // SPEC ISSUE: It doesn't pass in intersection ratio to "queue an IntersectionObserverEntry" despite needing it.
  3104. // This is default 0, as isIntersecting is default false, see step 9.
  3105. double intersection_ratio = 0.0;
  3106. // 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.
  3107. // 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.
  3108. // FIXME: Actually use the containing block chain.
  3109. auto intersection_root = observer->intersection_root();
  3110. auto intersection_root_document = intersection_root.visit([](auto& node) -> JS::NonnullGCPtr<Document> {
  3111. return node->document();
  3112. });
  3113. if (!(observer->root().has<Empty>() && &target->document() == intersection_root_document.ptr())
  3114. || !(intersection_root.has<JS::Handle<DOM::Element>>() && !target->is_descendant_of(*intersection_root.get<JS::Handle<DOM::Element>>()))) {
  3115. // 4. Set targetRect to the DOMRectReadOnly obtained by getting the bounding box for target.
  3116. target_rect = target->get_bounding_client_rect();
  3117. // 5. Let intersectionRect be the result of running the compute the intersection algorithm on target and
  3118. // observer’s intersection root.
  3119. intersection_rect = compute_intersection(target, observer);
  3120. // 6. Let targetArea be targetRect’s area.
  3121. auto target_area = target_rect->width() * target_rect->height();
  3122. // 7. Let intersectionArea be intersectionRect’s area.
  3123. auto intersection_area = intersection_rect->width() * intersection_rect->height();
  3124. // 8. Let isIntersecting be true if targetRect and rootBounds intersect or are edge-adjacent, even if the
  3125. // intersection has zero area (because rootBounds or targetRect have zero area).
  3126. CSSPixelRect target_rect_as_pixel_rect(target_rect->x(), target_rect->y(), target_rect->width(), target_rect->height());
  3127. is_intersecting = target_rect_as_pixel_rect.intersects(root_bounds);
  3128. // 9. If targetArea is non-zero, let intersectionRatio be intersectionArea divided by targetArea.
  3129. // Otherwise, let intersectionRatio be 1 if isIntersecting is true, or 0 if isIntersecting is false.
  3130. if (target_area != 0.0)
  3131. intersection_ratio = intersection_area / target_area;
  3132. else
  3133. intersection_ratio = is_intersecting ? 1.0 : 0.0;
  3134. // 10. Set thresholdIndex to the index of the first entry in observer.thresholds whose value is greater
  3135. // than intersectionRatio, or the length of observer.thresholds if intersectionRatio is greater than
  3136. // or equal to the last entry in observer.thresholds.
  3137. threshold_index = observer->thresholds().find_first_index_if([&intersection_ratio](double threshold_value) {
  3138. return threshold_value > intersection_ratio;
  3139. })
  3140. .value_or(observer->thresholds().size());
  3141. }
  3142. // 11. Let intersectionObserverRegistration be the IntersectionObserverRegistration record in target’s
  3143. // internal [[RegisteredIntersectionObservers]] slot whose observer property is equal to observer.
  3144. auto& intersection_observer_registration = target->get_intersection_observer_registration({}, observer);
  3145. // 12. Let previousThresholdIndex be the intersectionObserverRegistration’s previousThresholdIndex property.
  3146. auto previous_threshold_index = intersection_observer_registration.previous_threshold_index;
  3147. // 13. Let previousIsIntersecting be the intersectionObserverRegistration’s previousIsIntersecting property.
  3148. auto previous_is_intersecting = intersection_observer_registration.previous_is_intersecting;
  3149. // 14. If thresholdIndex does not equal previousThresholdIndex or if isIntersecting does not equal
  3150. // previousIsIntersecting, queue an IntersectionObserverEntry, passing in observer, time,
  3151. // rootBounds, targetRect, intersectionRect, isIntersecting, and target.
  3152. if (threshold_index != previous_threshold_index || is_intersecting != previous_is_intersecting) {
  3153. 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();
  3154. // SPEC ISSUE: It doesn't pass in intersectionRatio, but it's required.
  3155. queue_an_intersection_observer_entry(observer, time, root_bounds_as_dom_rect, target_rect, intersection_rect, is_intersecting, intersection_ratio, target);
  3156. }
  3157. // 15. Assign thresholdIndex to intersectionObserverRegistration’s previousThresholdIndex property.
  3158. intersection_observer_registration.previous_threshold_index = threshold_index;
  3159. // 16. Assign isIntersecting to intersectionObserverRegistration’s previousIsIntersecting property.
  3160. intersection_observer_registration.previous_is_intersecting = is_intersecting;
  3161. }
  3162. }
  3163. }
  3164. // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#start-intersection-observing-a-lazy-loading-element
  3165. void Document::start_intersection_observing_a_lazy_loading_element(Element& element)
  3166. {
  3167. VERIFY(element.is_lazy_loading());
  3168. auto& realm = this->realm();
  3169. // 1. Let doc be element's node document.
  3170. VERIFY(&element.document() == this);
  3171. // 2. If doc's lazy load intersection observer is null, set it to a new IntersectionObserver instance, initialized as follows:
  3172. if (!m_lazy_load_intersection_observer) {
  3173. // - The callback is these steps, with arguments entries and observer:
  3174. auto callback = JS::NativeFunction::create(realm, "", [this](JS::VM& vm) -> JS::ThrowCompletionOr<JS::Value> {
  3175. // For each entry in entries using a method of iteration which does not trigger developer-modifiable array accessors or iteration hooks:
  3176. auto& entries = verify_cast<JS::Array>(vm.argument(0).as_object());
  3177. auto entries_length = MUST(MUST(entries.get(vm.names.length)).to_length(vm));
  3178. for (size_t i = 0; i < entries_length; ++i) {
  3179. auto property_key = JS::PropertyKey { i };
  3180. auto& entry = verify_cast<IntersectionObserver::IntersectionObserverEntry>(entries.get_without_side_effects(property_key).as_object());
  3181. // 1. Let resumptionSteps be null.
  3182. JS::GCPtr<JS::HeapFunction<void()>> resumption_steps;
  3183. // 2. If entry.isIntersecting is true, then set resumptionSteps to entry.target's lazy load resumption steps.
  3184. if (entry.is_intersecting()) {
  3185. // 5. Set entry.target's lazy load resumption steps to null.
  3186. VERIFY(entry.target()->is_lazy_loading());
  3187. resumption_steps = entry.target()->take_lazy_load_resumption_steps({});
  3188. }
  3189. // 3. If resumptionSteps is null, then return.
  3190. if (!resumption_steps) {
  3191. // NOTE: This is wrong in the spec, since we want to keep processing
  3192. // entries even if one of them doesn't have resumption steps.
  3193. // FIXME: Spec bug: https://github.com/whatwg/html/issues/10019
  3194. continue;
  3195. }
  3196. // 4. Stop intersection-observing a lazy loading element for entry.target.
  3197. // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#stop-intersection-observing-a-lazy-loading-element
  3198. // 1. Let doc be element's node document.
  3199. // NOTE: It's `this`.
  3200. // 2. Assert: doc's lazy load intersection observer is not null.
  3201. VERIFY(m_lazy_load_intersection_observer);
  3202. // 3. Call doc's lazy load intersection observer unobserve method with element as the argument.
  3203. m_lazy_load_intersection_observer->unobserve(entry.target());
  3204. // 6. Invoke resumptionSteps.
  3205. resumption_steps->function()();
  3206. }
  3207. return JS::js_undefined();
  3208. });
  3209. // FIXME: The options is an IntersectionObserverInit dictionary with the following dictionary members: «[ "rootMargin" → lazy load root margin ]»
  3210. // Spec Note: This allows for fetching the image during scrolling, when it does not yet — but is about to — intersect the viewport.
  3211. auto options = IntersectionObserver::IntersectionObserverInit {};
  3212. auto wrapped_callback = realm.heap().allocate_without_realm<WebIDL::CallbackType>(callback, Bindings::host_defined_environment_settings_object(realm));
  3213. m_lazy_load_intersection_observer = IntersectionObserver::IntersectionObserver::construct_impl(realm, wrapped_callback, options).release_value_but_fixme_should_propagate_errors();
  3214. }
  3215. // 3. Call doc's lazy load intersection observer's observe method with element as the argument.
  3216. VERIFY(m_lazy_load_intersection_observer);
  3217. m_lazy_load_intersection_observer->observe(element);
  3218. }
  3219. // https://html.spec.whatwg.org/multipage/semantics.html#shared-declarative-refresh-steps
  3220. void Document::shared_declarative_refresh_steps(StringView input, JS::GCPtr<HTML::HTMLMetaElement const> meta_element)
  3221. {
  3222. // 1. If document's will declaratively refresh is true, then return.
  3223. if (m_will_declaratively_refresh)
  3224. return;
  3225. // 2. Let position point at the first code point of input.
  3226. GenericLexer lexer(input);
  3227. // 3. Skip ASCII whitespace within input given position.
  3228. lexer.ignore_while(Infra::is_ascii_whitespace);
  3229. // 4. Let time be 0.
  3230. u32 time = 0;
  3231. // 5. Collect a sequence of code points that are ASCII digits from input given position, and let the result be timeString.
  3232. auto time_string = lexer.consume_while(is_ascii_digit);
  3233. // 6. If timeString is the empty string, then:
  3234. if (time_string.is_empty()) {
  3235. // 1. If the code point in input pointed to by position is not U+002E (.), then return.
  3236. if (lexer.peek() != '.')
  3237. return;
  3238. }
  3239. // 7. Otherwise, set time to the result of parsing timeString using the rules for parsing non-negative integers.
  3240. auto maybe_time = Web::HTML::parse_non_negative_integer(time_string);
  3241. // 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.
  3242. if (maybe_time.has_value() && maybe_time.value() < NumericLimits<int>::max() && !Checked<int>::multiplication_would_overflow(static_cast<int>(maybe_time.value()), 1000)) {
  3243. time = maybe_time.value();
  3244. }
  3245. // 8. Collect a sequence of code points that are ASCII digits and U+002E FULL STOP characters (.) from input given
  3246. // position. Ignore any collected characters.
  3247. lexer.ignore_while([](auto c) {
  3248. return is_ascii_digit(c) || c == '.';
  3249. });
  3250. // 9. Let urlRecord be document's URL.
  3251. auto url_record = url();
  3252. // 10. If position is not past the end of input, then:
  3253. if (!lexer.is_eof()) {
  3254. // 1. If the code point in input pointed to by position is not U+003B (;), U+002C (,), or ASCII whitespace, then return.
  3255. if (lexer.peek() != ';' && lexer.peek() != ',' && !Infra::is_ascii_whitespace(lexer.peek()))
  3256. return;
  3257. // 2. Skip ASCII whitespace within input given position.
  3258. lexer.ignore_while(Infra::is_ascii_whitespace);
  3259. // 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.
  3260. if (lexer.peek() == ';' || lexer.peek() == ',')
  3261. lexer.ignore(1);
  3262. // 4. Skip ASCII whitespace within input given position.
  3263. lexer.ignore_while(Infra::is_ascii_whitespace);
  3264. }
  3265. // 11. If position is not past the end of input, then:
  3266. if (!lexer.is_eof()) {
  3267. // 1. Let urlString be the substring of input from the code point at position to the end of the string.
  3268. auto url_string = lexer.remaining();
  3269. // 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.
  3270. if (lexer.peek() == 'U' || lexer.peek() == 'u')
  3271. lexer.ignore(1);
  3272. else
  3273. goto skip_quotes;
  3274. // 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.
  3275. if (lexer.peek() == 'R' || lexer.peek() == 'r')
  3276. lexer.ignore(1);
  3277. else
  3278. goto parse;
  3279. // 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.
  3280. if (lexer.peek() == 'L' || lexer.peek() == 'l')
  3281. lexer.ignore(1);
  3282. else
  3283. goto parse;
  3284. // 5. Skip ASCII whitespace within input given position.
  3285. lexer.ignore_while(Infra::is_ascii_whitespace);
  3286. // 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.
  3287. if (lexer.peek() == '=')
  3288. lexer.ignore(1);
  3289. else
  3290. goto parse;
  3291. // 7. Skip ASCII whitespace within input given position.
  3292. lexer.ignore_while(Infra::is_ascii_whitespace);
  3293. skip_quotes: {
  3294. // 8. Skip quotes: If the code point in input pointed to by position is U+0027 (') or U+0022 ("), then let
  3295. // quote be that code point, and advance position to the next code point. Otherwise, let quote be the empty
  3296. // string.
  3297. Optional<char> quote;
  3298. if (lexer.peek() == '\'' || lexer.peek() == '"')
  3299. quote = lexer.consume();
  3300. // 9. Set urlString to the substring of input from the code point at position to the end of the string.
  3301. // 10. If quote is not the empty string, and there is a code point in urlString equal to quote, then truncate
  3302. // urlString at that code point, so that it and all subsequent code points are removed.
  3303. url_string = lexer.consume_while([&quote](auto c) {
  3304. return !quote.has_value() || c != quote.value();
  3305. });
  3306. }
  3307. parse:
  3308. // 11. Parse: Parse urlString relative to document. If that fails, return. Otherwise, set urlRecord to the
  3309. // resulting URL record.
  3310. auto maybe_url_record = parse_url(url_string);
  3311. if (!maybe_url_record.is_valid())
  3312. return;
  3313. url_record = maybe_url_record;
  3314. }
  3315. // 12. Set document's will declaratively refresh to true.
  3316. m_will_declaratively_refresh = true;
  3317. // 13. Perform one or more of the following steps:
  3318. // - After the refresh has come due (as defined below), if the user has not canceled the redirect and, if meta is
  3319. // given, document's active sandboxing flag set does not have the sandboxed automatic features browsing context
  3320. // flag set, then navigate document's node navigable to urlRecord using document, with historyHandling set to
  3321. // "replace".
  3322. m_active_refresh_timer = Core::Timer::create_single_shot(time * 1000, [this, has_meta_element = !!meta_element, url_record = move(url_record)]() {
  3323. if (has_meta_element && has_flag(active_sandboxing_flag_set(), HTML::SandboxingFlagSet::SandboxedAutomaticFeatures))
  3324. return;
  3325. auto navigable = this->navigable();
  3326. if (!navigable || navigable->has_been_destroyed())
  3327. return;
  3328. MUST(navigable->navigate({ .url = url_record, .source_document = *this, .history_handling = Bindings::NavigationHistoryBehavior::Replace }));
  3329. });
  3330. // For the purposes of the previous paragraph, a refresh is said to have come due as soon as the later of the
  3331. // following two conditions occurs:
  3332. // - At least time seconds have elapsed since document's completely loaded time, adjusted to take into
  3333. // account user or user agent preferences.
  3334. // m_active_refresh_timer is started in completely_finished_loading after setting the completely loaded time.
  3335. // - If meta is given, at least time seconds have elapsed since meta was inserted into the document document,
  3336. // adjusted to take into account user or user agent preferences.
  3337. // NOTE: This is only done if completely loaded time has a value because shared_declarative_refresh_steps is called
  3338. // by HTMLMetaElement::inserted and if the document hasn't finished loading when the meta element was inserted,
  3339. // then the document completely finishing loading will _always_ come after inserting the meta element.
  3340. if (meta_element && m_completely_loaded_time.has_value()) {
  3341. m_active_refresh_timer->start();
  3342. }
  3343. }
  3344. Painting::ViewportPaintable const* Document::paintable() const
  3345. {
  3346. return static_cast<Painting::ViewportPaintable const*>(Node::paintable());
  3347. }
  3348. Painting::ViewportPaintable* Document::paintable()
  3349. {
  3350. return static_cast<Painting::ViewportPaintable*>(Node::paintable());
  3351. }
  3352. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#restore-the-history-object-state
  3353. void Document::restore_the_history_object_state(JS::NonnullGCPtr<HTML::SessionHistoryEntry> entry)
  3354. {
  3355. // 1. Let targetRealm be document's relevant realm.
  3356. auto& target_realm = HTML::relevant_realm(*this);
  3357. // 2. Let state be StructuredDeserialize(entry's classic history API state, targetRealm). If this throws an exception, catch it and let state be null.
  3358. // 3. Set document's history object's state to state.
  3359. auto state_or_error = HTML::structured_deserialize(target_realm.vm(), entry->classic_history_api_state(), target_realm, {});
  3360. if (state_or_error.is_error())
  3361. m_history->set_state(JS::js_null());
  3362. else
  3363. m_history->set_state(state_or_error.release_value());
  3364. }
  3365. // https://html.spec.whatwg.org/multipage/browsing-the-web.html#update-document-for-history-step-application
  3366. 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)
  3367. {
  3368. // 1. Let documentIsNew be true if document's latest entry is null; otherwise false.
  3369. auto document_is_new = !m_latest_entry;
  3370. // 2. Let documentsEntryChanged be true if document's latest entry is not entry; otherwise false.
  3371. auto documents_entry_changed = m_latest_entry != entry;
  3372. // 3. Set document's history object's index to scriptHistoryIndex.
  3373. history()->m_index = script_history_index;
  3374. // 4. Set document's history object's length to scriptHistoryLength.
  3375. history()->m_length = script_history_length;
  3376. // 5. If documentsEntryChanged is true, then:
  3377. // NOTE: documentsEntryChanged can be false for one of two reasons: either we are restoring from bfcache,
  3378. // or we are asynchronously finishing up a synchronous navigation which already synchronously set document's latest entry.
  3379. // The doNotReactivate argument distinguishes between these two cases.
  3380. if (documents_entry_changed) {
  3381. // 1. Let oldURL be document's latest entry's URL.
  3382. auto old_url = m_latest_entry ? m_latest_entry->url() : URL::URL {};
  3383. // 2. Set document's latest entry to entry.
  3384. m_latest_entry = entry;
  3385. // 3. Restore the history object state given document and entry.
  3386. restore_the_history_object_state(entry);
  3387. // 4. Let navigation be history's relevant global object's navigation API.
  3388. auto navigation = verify_cast<HTML::Window>(HTML::relevant_global_object(*this)).navigation();
  3389. // 5. If documentIsNew is false, then:
  3390. if (!document_is_new) {
  3391. // NOTE: Not in the spec, but otherwise document's url won't be updated in case of a same-document back/forward navigation.
  3392. set_url(entry->url());
  3393. // AD HOC: Skip this in situations the spec steps don't account for
  3394. if (update_navigation_api) {
  3395. // 1. Update the navigation API entries for a same-document navigation given navigation, entry, and "traverse".
  3396. navigation->update_the_navigation_api_entries_for_a_same_document_navigation(entry, Bindings::NavigationType::Traverse);
  3397. }
  3398. // 2. Fire an event named popstate at document's relevant global object, using PopStateEvent,
  3399. // with the state attribute initialized to document's history object's state and hasUAVisualTransition initialized to true
  3400. // if a visual transition, to display a cached rendered state of the latest entry, was done by the user agent.
  3401. // FIXME: Initialise hasUAVisualTransition
  3402. HTML::PopStateEventInit popstate_event_init;
  3403. popstate_event_init.state = history()->unsafe_state();
  3404. auto& relevant_global_object = verify_cast<HTML::Window>(HTML::relevant_global_object(*this));
  3405. auto pop_state_event = HTML::PopStateEvent::create(realm(), "popstate"_fly_string, popstate_event_init);
  3406. relevant_global_object.dispatch_event(pop_state_event);
  3407. // FIXME: 3. Restore persisted state given entry.
  3408. // 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
  3409. // given document's relevant global object to fire an event named hashchange at document's relevant global object,
  3410. // using HashChangeEvent, with the oldURL attribute initialized to the serialization of oldURL and the newURL attribute
  3411. // initialized to the serialization of entry's URL.
  3412. if (old_url.fragment() != entry->url().fragment()) {
  3413. HTML::HashChangeEventInit hashchange_event_init;
  3414. hashchange_event_init.old_url = MUST(String::from_byte_string(old_url.serialize()));
  3415. hashchange_event_init.new_url = MUST(String::from_byte_string(entry->url().serialize()));
  3416. auto hashchange_event = HTML::HashChangeEvent::create(realm(), "hashchange"_fly_string, hashchange_event_init);
  3417. HTML::queue_global_task(HTML::Task::Source::DOMManipulation, relevant_global_object, JS::create_heap_function(heap(), [hashchange_event, &relevant_global_object]() {
  3418. relevant_global_object.dispatch_event(hashchange_event);
  3419. }));
  3420. }
  3421. }
  3422. // 6. Otherwise:
  3423. else {
  3424. // 1. Assert: entriesForNavigationAPI is given.
  3425. VERIFY(entries_for_navigation_api.has_value());
  3426. // FIXME: 2. Restore persisted state given entry.
  3427. // 3. Initialize the navigation API entries for a new document given navigation, entriesForNavigationAPI, and entry.
  3428. navigation->initialize_the_navigation_api_entries_for_a_new_document(*entries_for_navigation_api, entry);
  3429. }
  3430. }
  3431. // 6. If documentIsNew is true, then:
  3432. if (document_is_new) {
  3433. // FIXME: 1. Try to scroll to the fragment for document.
  3434. // FIXME: According to the spec we should only scroll here if document has no parser or parsing has stopped.
  3435. // It should be ok to remove this after we implement navigation events and scrolling will happen in
  3436. // "process scroll behavior".
  3437. scroll_to_the_fragment();
  3438. // 2. At this point scripts may run for the newly-created document document.
  3439. m_ready_to_run_scripts = true;
  3440. }
  3441. // 7. Otherwise, if documentsEntryChanged is false and doNotReactivate is false, then:
  3442. // NOTE: This is for bfcache restoration
  3443. if (!documents_entry_changed && !do_not_reactivate) {
  3444. // FIXME: 1. Assert: entriesForNavigationAPI is given.
  3445. // FIXME: 2. Reactivate document given entry and entriesForNavigationAPI.
  3446. }
  3447. }
  3448. HashMap<URL::URL, JS::GCPtr<HTML::SharedImageRequest>>& Document::shared_image_requests()
  3449. {
  3450. return m_shared_image_requests;
  3451. }
  3452. // https://www.w3.org/TR/web-animations-1/#dom-document-timeline
  3453. JS::NonnullGCPtr<Animations::DocumentTimeline> Document::timeline()
  3454. {
  3455. // The DocumentTimeline object representing the default document timeline. The default document timeline has an
  3456. // origin time of zero.
  3457. if (!m_default_timeline)
  3458. m_default_timeline = Animations::DocumentTimeline::create(realm(), *this, 0.0);
  3459. return *m_default_timeline;
  3460. }
  3461. void Document::associate_with_timeline(JS::NonnullGCPtr<Animations::AnimationTimeline> timeline)
  3462. {
  3463. m_associated_animation_timelines.set(timeline);
  3464. }
  3465. void Document::disassociate_with_timeline(JS::NonnullGCPtr<Animations::AnimationTimeline> timeline)
  3466. {
  3467. m_associated_animation_timelines.remove(timeline);
  3468. }
  3469. void Document::append_pending_animation_event(Web::DOM::Document::PendingAnimationEvent const& event)
  3470. {
  3471. m_pending_animation_event_queue.append(event);
  3472. }
  3473. // https://www.w3.org/TR/web-animations-1/#update-animations-and-send-events
  3474. void Document::update_animations_and_send_events(Optional<double> const& timestamp)
  3475. {
  3476. // 1. Update the current time of all timelines associated with doc passing now as the timestamp.
  3477. //
  3478. // Note: Due to the hierarchical nature of the timing model, updating the current time of a timeline also involves:
  3479. // - Updating the current time of any animations associated with the timeline.
  3480. // - Running the update an animation’s finished state procedure for any animations whose current time has been
  3481. // updated.
  3482. // - Queueing animation events for any such animations.
  3483. m_last_animation_frame_timestamp = timestamp;
  3484. for (auto const& timeline : m_associated_animation_timelines)
  3485. timeline->set_current_time(timestamp);
  3486. // 2. Remove replaced animations for doc.
  3487. remove_replaced_animations();
  3488. // 3. Perform a microtask checkpoint.
  3489. HTML::perform_a_microtask_checkpoint();
  3490. // 4. Let events to dispatch be a copy of doc’s pending animation event queue.
  3491. // 5. Clear doc’s pending animation event queue.
  3492. auto events_to_dispatch = move(m_pending_animation_event_queue);
  3493. // 6. Perform a stable sort of the animation events in events to dispatch as follows:
  3494. auto sort_events_by_composite_order = [](auto const& a, auto const& b) {
  3495. auto& a_effect = verify_cast<Animations::KeyframeEffect>(*a.animation->effect());
  3496. auto& b_effect = verify_cast<Animations::KeyframeEffect>(*b.animation->effect());
  3497. return Animations::KeyframeEffect::composite_order(a_effect, b_effect) < 0;
  3498. };
  3499. insertion_sort(events_to_dispatch, [&](auto const& a, auto const& b) {
  3500. // Sort the events by their scheduled event time such that events that were scheduled to occur earlier, sort
  3501. // before events scheduled to occur later and events whose scheduled event time is unresolved sort before events
  3502. // with a resolved scheduled event time.
  3503. //
  3504. // Within events with equal scheduled event times, sort by their composite order.
  3505. if (b.scheduled_event_time.has_value()) {
  3506. if (!a.scheduled_event_time.has_value())
  3507. return true;
  3508. auto a_time = a.scheduled_event_time.value();
  3509. auto b_time = b.scheduled_event_time.value();
  3510. if (a_time == b_time)
  3511. return sort_events_by_composite_order(a, b);
  3512. return a.scheduled_event_time.value() < b.scheduled_event_time.value();
  3513. }
  3514. if (a.scheduled_event_time.has_value())
  3515. return false;
  3516. return sort_events_by_composite_order(a, b);
  3517. });
  3518. // 7. Dispatch each of the events in events to dispatch at their corresponding target using the order established in
  3519. // the previous step.
  3520. for (auto const& event : events_to_dispatch)
  3521. event.target->dispatch_event(event.event);
  3522. for (auto& timeline : m_associated_animation_timelines) {
  3523. for (auto& animation : timeline->associated_animations())
  3524. dispatch_events_for_animation_if_necessary(animation);
  3525. }
  3526. }
  3527. // https://www.w3.org/TR/web-animations-1/#remove-replaced-animations
  3528. void Document::remove_replaced_animations()
  3529. {
  3530. // When asked to remove replaced animations for a Document, doc, then for every animation, animation, that:
  3531. // - has an associated animation effect whose effect target is a descendant of doc, and
  3532. // - is replaceable, and
  3533. // - has a replace state of active, and
  3534. // - for which there exists for each target property of every animation effect associated with animation, an
  3535. // animation effect associated with a replaceable animation with a higher composite order than animation that
  3536. // includes the same target property
  3537. Vector<JS::NonnullGCPtr<Animations::Animation>> replaceable_animations;
  3538. for (auto const& timeline : m_associated_animation_timelines) {
  3539. for (auto const& animation : timeline->associated_animations()) {
  3540. if (!animation->effect() || !animation->effect()->target() || &animation->effect()->target()->document() != this)
  3541. continue;
  3542. if (!animation->is_replaceable())
  3543. continue;
  3544. if (animation->replace_state() != Bindings::AnimationReplaceState::Active)
  3545. continue;
  3546. // Composite order is only defined for KeyframeEffects
  3547. if (!animation->effect()->is_keyframe_effect())
  3548. continue;
  3549. replaceable_animations.append(animation);
  3550. }
  3551. }
  3552. quick_sort(replaceable_animations, [](JS::NonnullGCPtr<Animations::Animation>& a, JS::NonnullGCPtr<Animations::Animation>& b) {
  3553. VERIFY(a->effect()->is_keyframe_effect());
  3554. VERIFY(b->effect()->is_keyframe_effect());
  3555. auto& a_effect = *static_cast<Animations::KeyframeEffect*>(a->effect().ptr());
  3556. auto& b_effect = *static_cast<Animations::KeyframeEffect*>(b->effect().ptr());
  3557. return Animations::KeyframeEffect::composite_order(a_effect, b_effect) < 0;
  3558. });
  3559. // Lower value = higher priority
  3560. HashMap<CSS::PropertyID, size_t> highest_property_composite_orders;
  3561. for (int i = replaceable_animations.size() - 1; i >= 0; i--) {
  3562. auto animation = replaceable_animations[i];
  3563. bool has_any_highest_priority_property = false;
  3564. for (auto const& property : animation->effect()->target_properties()) {
  3565. if (!highest_property_composite_orders.contains(property)) {
  3566. has_any_highest_priority_property = true;
  3567. highest_property_composite_orders.set(property, i);
  3568. }
  3569. }
  3570. if (!has_any_highest_priority_property) {
  3571. // perform the following steps:
  3572. // - Set animation’s replace state to removed.
  3573. animation->set_replace_state(Bindings::AnimationReplaceState::Removed);
  3574. // - Create an AnimationPlaybackEvent, removeEvent.
  3575. // - Set removeEvent’s type attribute to remove.
  3576. // - Set removeEvent’s currentTime attribute to the current time of animation.
  3577. // - Set removeEvent’s timelineTime attribute to the current time of the timeline with which animation is
  3578. // associated.
  3579. Animations::AnimationPlaybackEventInit init;
  3580. init.current_time = animation->current_time();
  3581. init.timeline_time = animation->timeline()->current_time();
  3582. auto remove_event = Animations::AnimationPlaybackEvent::create(realm(), HTML::EventNames::remove, init);
  3583. // - If animation has a document for timing, then append removeEvent to its document for timing's pending
  3584. // animation event queue along with its target, animation. For the scheduled event time, use the result of
  3585. // applying the procedure to convert timeline time to origin-relative time to the current time of the
  3586. // timeline with which animation is associated.
  3587. if (auto document = animation->document_for_timing()) {
  3588. PendingAnimationEvent pending_animation_event {
  3589. .event = remove_event,
  3590. .animation = animation,
  3591. .target = animation,
  3592. .scheduled_event_time = animation->timeline()->convert_a_timeline_time_to_an_origin_relative_time(init.timeline_time),
  3593. };
  3594. document->append_pending_animation_event(pending_animation_event);
  3595. }
  3596. // Otherwise, queue a task to dispatch removeEvent at animation. The task source for this task is the DOM
  3597. // manipulation task source.
  3598. else {
  3599. HTML::queue_global_task(HTML::Task::Source::DOMManipulation, realm().global_object(), JS::create_heap_function(heap(), [animation, remove_event]() {
  3600. animation->dispatch_event(remove_event);
  3601. }));
  3602. }
  3603. }
  3604. }
  3605. }
  3606. void Document::ensure_animation_timer()
  3607. {
  3608. constexpr static auto timer_delay_ms = 1000 / 60;
  3609. if (!m_animation_driver_timer) {
  3610. m_animation_driver_timer = Core::Timer::create_repeating(timer_delay_ms, [this] {
  3611. bool has_animations = false;
  3612. for (auto& timeline : m_associated_animation_timelines) {
  3613. if (!timeline->associated_animations().is_empty()) {
  3614. has_animations = true;
  3615. break;
  3616. }
  3617. }
  3618. if (!has_animations) {
  3619. m_animation_driver_timer->stop();
  3620. return;
  3621. }
  3622. auto* window_or_worker = dynamic_cast<HTML::WindowOrWorkerGlobalScopeMixin*>(&realm().global_object());
  3623. VERIFY(window_or_worker);
  3624. update_animations_and_send_events(window_or_worker->performance()->now());
  3625. });
  3626. }
  3627. m_animation_driver_timer->start();
  3628. }
  3629. Vector<JS::NonnullGCPtr<Animations::Animation>> Document::get_animations()
  3630. {
  3631. Vector<JS::NonnullGCPtr<Animations::Animation>> relevant_animations;
  3632. for_each_child_of_type<Element>([&](auto& child) {
  3633. relevant_animations.extend(child.get_animations({ .subtree = true }));
  3634. return IterationDecision::Continue;
  3635. });
  3636. return relevant_animations;
  3637. }
  3638. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-nameditem-filter
  3639. static bool is_potentially_named_element(DOM::Element const& element)
  3640. {
  3641. return is<HTML::HTMLEmbedElement>(element) || is<HTML::HTMLFormElement>(element) || is<HTML::HTMLIFrameElement>(element) || is<HTML::HTMLImageElement>(element) || is<HTML::HTMLObjectElement>(element);
  3642. }
  3643. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-nameditem-filter
  3644. static bool is_potentially_named_element_by_id(DOM::Element const& element)
  3645. {
  3646. return is<HTML::HTMLObjectElement>(element) || is<HTML::HTMLImageElement>(element);
  3647. }
  3648. static void insert_in_tree_order(Vector<JS::NonnullGCPtr<DOM::Element>>& elements, DOM::Element& element)
  3649. {
  3650. for (auto& el : elements) {
  3651. if (el == &element)
  3652. return;
  3653. }
  3654. auto index = elements.find_first_index_if([&](auto& existing_element) {
  3655. return existing_element->compare_document_position(element) & Node::DOCUMENT_POSITION_FOLLOWING;
  3656. });
  3657. if (index.has_value())
  3658. elements.insert(index.value(), element);
  3659. else
  3660. elements.append(element);
  3661. }
  3662. void Document::element_id_changed(Badge<DOM::Element>, JS::NonnullGCPtr<DOM::Element> element)
  3663. {
  3664. for (auto* form_associated_element : m_form_associated_elements_with_form_attribute)
  3665. form_associated_element->element_id_changed({});
  3666. if (element->id().has_value())
  3667. insert_in_tree_order(m_potentially_named_elements, element);
  3668. else
  3669. (void)m_potentially_named_elements.remove_first_matching([element](auto& e) { return e == element; });
  3670. }
  3671. void Document::element_with_id_was_added(Badge<DOM::Element>, JS::NonnullGCPtr<DOM::Element> element)
  3672. {
  3673. for (auto* form_associated_element : m_form_associated_elements_with_form_attribute)
  3674. form_associated_element->element_with_id_was_added_or_removed({});
  3675. if (is_potentially_named_element_by_id(*element))
  3676. insert_in_tree_order(m_potentially_named_elements, element);
  3677. }
  3678. void Document::element_with_id_was_removed(Badge<DOM::Element>, JS::NonnullGCPtr<DOM::Element> element)
  3679. {
  3680. for (auto* form_associated_element : m_form_associated_elements_with_form_attribute)
  3681. form_associated_element->element_with_id_was_added_or_removed({});
  3682. if (is_potentially_named_element_by_id(*element))
  3683. (void)m_potentially_named_elements.remove_first_matching([element](auto& e) { return e == element; });
  3684. }
  3685. void Document::element_name_changed(Badge<DOM::Element>, JS::NonnullGCPtr<DOM::Element> element)
  3686. {
  3687. if (element->name().has_value()) {
  3688. insert_in_tree_order(m_potentially_named_elements, element);
  3689. } else {
  3690. if (is_potentially_named_element_by_id(element) && element->id().has_value())
  3691. return;
  3692. (void)m_potentially_named_elements.remove_first_matching([element](auto& e) { return e == element; });
  3693. }
  3694. }
  3695. void Document::element_with_name_was_added(Badge<DOM::Element>, JS::NonnullGCPtr<DOM::Element> element)
  3696. {
  3697. if (is_potentially_named_element(element))
  3698. insert_in_tree_order(m_potentially_named_elements, element);
  3699. }
  3700. void Document::element_with_name_was_removed(Badge<DOM::Element>, JS::NonnullGCPtr<DOM::Element> element)
  3701. {
  3702. if (is_potentially_named_element(element)) {
  3703. if (is_potentially_named_element_by_id(element) && element->id().has_value())
  3704. return;
  3705. (void)m_potentially_named_elements.remove_first_matching([element](auto& e) { return e == element; });
  3706. }
  3707. }
  3708. void Document::add_form_associated_element_with_form_attribute(HTML::FormAssociatedElement& form_associated_element)
  3709. {
  3710. m_form_associated_elements_with_form_attribute.append(&form_associated_element);
  3711. }
  3712. void Document::remove_form_associated_element_with_form_attribute(HTML::FormAssociatedElement& form_associated_element)
  3713. {
  3714. m_form_associated_elements_with_form_attribute.remove_all_matching([&](auto* element) {
  3715. return element == &form_associated_element;
  3716. });
  3717. }
  3718. void Document::set_design_mode_enabled_state(bool design_mode_enabled)
  3719. {
  3720. m_design_mode_enabled = design_mode_enabled;
  3721. set_editable(design_mode_enabled);
  3722. }
  3723. // https://html.spec.whatwg.org/multipage/interaction.html#making-entire-documents-editable:-the-designmode-idl-attribute
  3724. String Document::design_mode() const
  3725. {
  3726. // The designMode getter steps are to return "on" if this's design mode enabled is true; otherwise "off".
  3727. return design_mode_enabled_state() ? "on"_string : "off"_string;
  3728. }
  3729. WebIDL::ExceptionOr<void> Document::set_design_mode(String const& design_mode)
  3730. {
  3731. // 1. Let value be the given value, converted to ASCII lowercase.
  3732. auto value = MUST(design_mode.to_lowercase());
  3733. // 2. If value is "on" and this's design mode enabled is false, then:
  3734. if (value == "on"sv && !m_design_mode_enabled) {
  3735. // 1. Set this's design mode enabled to true.
  3736. set_design_mode_enabled_state(true);
  3737. // 2. Reset this's active range's start and end boundary points to be at the start of this.
  3738. if (auto selection = get_selection(); selection) {
  3739. if (auto active_range = selection->range(); active_range) {
  3740. TRY(active_range->set_start(*this, 0));
  3741. TRY(active_range->set_end(*this, 0));
  3742. update_layout();
  3743. }
  3744. }
  3745. // 3. Run the focusing steps for this's document element, if non-null.
  3746. if (auto* document_element = this->document_element(); document_element)
  3747. HTML::run_focusing_steps(document_element);
  3748. }
  3749. // 3. If value is "off", then set this's design mode enabled to false.
  3750. else if (value == "off"sv) {
  3751. set_design_mode_enabled_state(false);
  3752. }
  3753. return {};
  3754. }
  3755. // https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint
  3756. Element const* Document::element_from_point(double x, double y)
  3757. {
  3758. // 1. If either argument is negative, x is greater than the viewport width excluding the size of a rendered scroll
  3759. // bar (if any), or y is greater than the viewport height excluding the size of a rendered scroll bar (if any), or
  3760. // there is no viewport associated with the document, return null and terminate these steps.
  3761. auto viewport_rect = this->viewport_rect();
  3762. CSSPixelPoint position { x, y };
  3763. // FIXME: This should account for the size of the scroll bar.
  3764. if (x < 0 || y < 0 || position.x() > viewport_rect.width() || position.y() > viewport_rect.height())
  3765. return nullptr;
  3766. // Ensure the layout tree exists prior to hit testing.
  3767. update_layout();
  3768. // 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
  3769. // that apply to the descendants of the viewport, return the associated element and terminate these steps.
  3770. Optional<Painting::HitTestResult> hit_test_result;
  3771. if (auto const* paintable_box = this->paintable_box(); paintable_box) {
  3772. (void)paintable_box->hit_test(position, Painting::HitTestType::Exact, [&](Painting::HitTestResult result) {
  3773. auto* dom_node = result.dom_node();
  3774. if (dom_node && dom_node->is_element()) {
  3775. hit_test_result = result;
  3776. return TraversalDecision::Break;
  3777. }
  3778. return TraversalDecision::Continue;
  3779. });
  3780. }
  3781. if (hit_test_result.has_value())
  3782. return static_cast<Element*>(hit_test_result->dom_node());
  3783. // 3. If the document has a root element, return the root element and terminate these steps.
  3784. if (auto const* document_root_element = first_child_of_type<Element>(); document_root_element)
  3785. return document_root_element;
  3786. // 4. Return null.
  3787. return nullptr;
  3788. }
  3789. // https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint
  3790. Vector<JS::NonnullGCPtr<Element>> Document::elements_from_point(double x, double y)
  3791. {
  3792. // 1. Let sequence be a new empty sequence.
  3793. Vector<JS::NonnullGCPtr<Element>> sequence;
  3794. // 2. If either argument is negative, x is greater than the viewport width excluding the size of a rendered scroll bar (if any),
  3795. // or y is greater than the viewport height excluding the size of a rendered scroll bar (if any),
  3796. // or there is no viewport associated with the document, return sequence and terminate these steps.
  3797. auto viewport_rect = this->viewport_rect();
  3798. CSSPixelPoint position { x, y };
  3799. // FIXME: This should account for the size of the scroll bar.
  3800. if (x < 0 || y < 0 || position.x() > viewport_rect.width() || position.y() > viewport_rect.height())
  3801. return sequence;
  3802. // Ensure the layout tree exists prior to hit testing.
  3803. update_layout();
  3804. // 3. For each box in the viewport, in paint order, starting with the topmost box, that would be a target for
  3805. // hit testing at coordinates x,y even if nothing would be overlapping it, when applying the transforms that
  3806. // apply to the descendants of the viewport, append the associated element to sequence.
  3807. if (auto const* paintable_box = this->paintable_box(); paintable_box) {
  3808. (void)paintable_box->hit_test(position, Painting::HitTestType::Exact, [&](Painting::HitTestResult result) {
  3809. auto* dom_node = result.dom_node();
  3810. if (dom_node && dom_node->is_element())
  3811. sequence.append(*static_cast<Element*>(dom_node));
  3812. return TraversalDecision::Continue;
  3813. });
  3814. }
  3815. // 4. If the document has a root element, and the last item in sequence is not the root element,
  3816. // append the root element to sequence.
  3817. if (auto* root_element = document_element(); root_element && (sequence.is_empty() || (sequence.last() != root_element)))
  3818. sequence.append(*root_element);
  3819. // 5. Return sequence.
  3820. return sequence;
  3821. }
  3822. // https://drafts.csswg.org/cssom-view/#dom-document-scrollingelement
  3823. JS::GCPtr<Element const> Document::scrolling_element() const
  3824. {
  3825. // 1. If the Document is in quirks mode, follow these substeps:
  3826. if (in_quirks_mode()) {
  3827. // 1. If the body element exists, and it is not potentially scrollable, return the body element and abort these steps.
  3828. // For this purpose, a value of overflow:clip on the the body element’s parent element must be treated as overflow:hidden.
  3829. if (auto const* body_element = body(); body_element && !body_element->is_potentially_scrollable())
  3830. return body_element;
  3831. // 2. Return null and abort these steps.
  3832. return nullptr;
  3833. }
  3834. // 2. If there is a root element, return the root element and abort these steps.
  3835. if (auto const* root_element = document_element(); root_element)
  3836. return root_element;
  3837. // 3. Return null.
  3838. return nullptr;
  3839. }
  3840. // https://html.spec.whatwg.org/multipage/dom.html#exposed
  3841. static bool is_exposed(Element const& element)
  3842. {
  3843. VERIFY(is<HTML::HTMLEmbedElement>(element) || is<HTML::HTMLObjectElement>(element));
  3844. // FIXME: An embed or object element is said to be exposed if it has no exposed object ancestor, and,
  3845. // for object elements, is additionally either not showing its fallback content or has no object or embed descendants.
  3846. return true;
  3847. }
  3848. // https://html.spec.whatwg.org/multipage/dom.html#dom-tree-accessors:supported-property-names
  3849. Vector<FlyString> Document::supported_property_names() const
  3850. {
  3851. // The supported property names of a Document object document at any moment consist of the following,
  3852. // in tree order according to the element that contributed them, ignoring later duplicates,
  3853. // and with values from id attributes coming before values from name attributes when the same element contributes both:
  3854. OrderedHashTable<FlyString> names;
  3855. for (auto const& element : m_potentially_named_elements) {
  3856. // - the value of the name content attribute for all exposed embed, form, iframe, img, and exposed object elements
  3857. // that have a non-empty name content attribute and are in a document tree with document as their root;
  3858. if ((is<HTML::HTMLEmbedElement>(*element) && is_exposed(element))
  3859. || is<HTML::HTMLFormElement>(*element)
  3860. || is<HTML::HTMLIFrameElement>(*element)
  3861. || is<HTML::HTMLImageElement>(*element)
  3862. || (is<HTML::HTMLObjectElement>(*element) && is_exposed(element))) {
  3863. if (auto name = element->name(); name.has_value()) {
  3864. names.set(name.value());
  3865. }
  3866. }
  3867. // - the value of the id content attribute for all exposed object elements that have a non-empty id content attribute
  3868. // and are in a document tree with document as their root; and
  3869. if (is<HTML::HTMLObjectElement>(*element) && is_exposed(element)) {
  3870. if (auto id = element->id(); id.has_value()) {
  3871. names.set(id.value());
  3872. }
  3873. }
  3874. // - the value of the id content attribute for all img elements that have both a non-empty id content attribute
  3875. // and a non-empty name content attribute, and are in a document tree with document as their root.
  3876. if (is<HTML::HTMLImageElement>(*element)) {
  3877. if (auto id = element->id(); id.has_value() && element->name().has_value()) {
  3878. names.set(id.value());
  3879. }
  3880. }
  3881. }
  3882. return names.values();
  3883. }
  3884. static bool is_named_element_with_name(Element const& element, FlyString const& name)
  3885. {
  3886. // Named elements with the name name, for the purposes of the above algorithm, are those that are either:
  3887. // - Exposed embed, form, iframe, img, or exposed object elements that have a name content attribute whose value
  3888. // is name, or
  3889. if ((is<HTML::HTMLEmbedElement>(element) && is_exposed(element))
  3890. || is<HTML::HTMLFormElement>(element)
  3891. || is<HTML::HTMLIFrameElement>(element)
  3892. || is<HTML::HTMLImageElement>(element)
  3893. || (is<HTML::HTMLObjectElement>(element) && is_exposed(element))) {
  3894. if (element.name() == name)
  3895. return true;
  3896. }
  3897. // - Exposed object elements that have an id content attribute whose value is name, or
  3898. if (is<HTML::HTMLObjectElement>(element) && is_exposed(element)) {
  3899. if (element.id() == name)
  3900. return true;
  3901. }
  3902. // - img elements that have an id content attribute whose value is name, and that have a non-empty name content
  3903. // attribute present also.
  3904. if (is<HTML::HTMLImageElement>(element)) {
  3905. if (element.id() == name && element.name().has_value())
  3906. return true;
  3907. }
  3908. return false;
  3909. }
  3910. static Vector<JS::NonnullGCPtr<DOM::Element>> named_elements_with_name(Document const& document, FlyString const& name)
  3911. {
  3912. Vector<JS::NonnullGCPtr<DOM::Element>> named_elements;
  3913. for (auto const& element : document.potentially_named_elements()) {
  3914. if (is_named_element_with_name(element, name))
  3915. named_elements.append(element);
  3916. }
  3917. return named_elements;
  3918. }
  3919. // https://html.spec.whatwg.org/multipage/dom.html#dom-document-nameditem
  3920. WebIDL::ExceptionOr<JS::Value> Document::named_item_value(FlyString const& name) const
  3921. {
  3922. // 1. Let elements be the list of named elements with the name name that are in a document tree with the Document as their root.
  3923. // NOTE: There will be at least one such element, since the algorithm would otherwise not have been invoked by Web IDL.
  3924. auto elements = named_elements_with_name(*this, name);
  3925. // 2. If elements has only one element, and that element is an iframe element, and that iframe element's content navigable is not null,
  3926. // then return the active WindowProxy of the element's content navigable.
  3927. if (elements.size() == 1 && is<HTML::HTMLIFrameElement>(*elements.first())) {
  3928. auto& iframe_element = static_cast<HTML::HTMLIFrameElement&>(*elements.first());
  3929. if (iframe_element.content_navigable() != nullptr)
  3930. return iframe_element.content_navigable()->active_window_proxy();
  3931. }
  3932. // 3. Otherwise, if elements has only one element, return that element.
  3933. if (elements.size() == 1)
  3934. return elements.first();
  3935. // 4. Otherwise return an HTMLCollection rooted at the Document node, whose filter matches only named elements with the name name.
  3936. return HTMLCollection::create(*const_cast<Document*>(this), HTMLCollection::Scope::Descendants, [name](auto& element) {
  3937. return is_named_element_with_name(element, name);
  3938. });
  3939. }
  3940. // https://w3c.github.io/editing/docs/execCommand/#execcommand()
  3941. bool Document::exec_command(String, bool, String)
  3942. {
  3943. return false;
  3944. }
  3945. // https://w3c.github.io/editing/docs/execCommand/#querycommandenabled()
  3946. bool Document::query_command_enabled(String)
  3947. {
  3948. return false;
  3949. }
  3950. // https://w3c.github.io/editing/docs/execCommand/#querycommandindeterm()
  3951. bool Document::query_command_indeterm(String)
  3952. {
  3953. return false;
  3954. }
  3955. // https://w3c.github.io/editing/docs/execCommand/#querycommandstate()
  3956. bool Document::query_command_state(String)
  3957. {
  3958. return false;
  3959. }
  3960. // https://w3c.github.io/editing/docs/execCommand/#querycommandsupported()
  3961. bool Document::query_command_supported(String)
  3962. {
  3963. return false;
  3964. }
  3965. // https://w3c.github.io/editing/docs/execCommand/#querycommandvalue()
  3966. String Document::query_command_value(String)
  3967. {
  3968. return String {};
  3969. }
  3970. // https://drafts.csswg.org/resize-observer-1/#calculate-depth-for-node
  3971. static size_t calculate_depth_for_node(Node const& node)
  3972. {
  3973. // 1. Let p be the parent-traversal path from node to a root Element of this element’s flattened DOM tree.
  3974. // 2. Return number of nodes in p.
  3975. size_t depth = 0;
  3976. for (auto const* current = &node; current; current = current->parent())
  3977. ++depth;
  3978. return depth;
  3979. }
  3980. // https://drafts.csswg.org/resize-observer-1/#gather-active-observations-h
  3981. void Document::gather_active_observations_at_depth(size_t depth)
  3982. {
  3983. // 1. Let depth be the depth passed in.
  3984. // 2. For each observer in [[resizeObservers]] run these steps:
  3985. for (auto const& observer : m_resize_observers) {
  3986. // 1. Clear observer’s [[activeTargets]], and [[skippedTargets]].
  3987. observer->active_targets().clear();
  3988. observer->skipped_targets().clear();
  3989. // 2. For each observation in observer.[[observationTargets]] run this step:
  3990. for (auto const& observation : observer->observation_targets()) {
  3991. // 1. If observation.isActive() is true
  3992. if (observation->is_active()) {
  3993. // 1. Let targetDepth be result of calculate depth for node for observation.target.
  3994. auto target_depth = calculate_depth_for_node(*observation->target());
  3995. // 2. If targetDepth is greater than depth then add observation to [[activeTargets]].
  3996. if (target_depth > depth) {
  3997. observer->active_targets().append(observation);
  3998. } else {
  3999. // 3. Else add observation to [[skippedTargets]].
  4000. observer->skipped_targets().append(observation);
  4001. }
  4002. }
  4003. }
  4004. }
  4005. }
  4006. // https://drafts.csswg.org/resize-observer-1/#broadcast-active-resize-observations
  4007. size_t Document::broadcast_active_resize_observations()
  4008. {
  4009. // 1. Let shallowestTargetDepth be ∞
  4010. auto shallowest_target_depth = NumericLimits<size_t>::max();
  4011. // 2. For each observer in document.[[resizeObservers]] run these steps:
  4012. for (auto const& observer : m_resize_observers) {
  4013. // 1. If observer.[[activeTargets]] slot is empty, continue.
  4014. if (observer->active_targets().is_empty()) {
  4015. continue;
  4016. }
  4017. // 2. Let entries be an empty list of ResizeObserverEntryies.
  4018. Vector<JS::NonnullGCPtr<ResizeObserver::ResizeObserverEntry>> entries;
  4019. // 3. For each observation in [[activeTargets]] perform these steps:
  4020. for (auto const& observation : observer->active_targets()) {
  4021. // 1. Let entry be the result of running create and populate a ResizeObserverEntry given observation.target.
  4022. auto entry = ResizeObserver::ResizeObserverEntry::create_and_populate(realm(), *observation->target()).release_value_but_fixme_should_propagate_errors();
  4023. // 2. Add entry to entries.
  4024. entries.append(entry);
  4025. // 3. Set observation.lastReportedSizes to matching entry sizes.
  4026. switch (observation->observed_box()) {
  4027. case Bindings::ResizeObserverBoxOptions::BorderBox:
  4028. // Matching sizes are entry.borderBoxSize if observation.observedBox is "border-box"
  4029. observation->last_reported_sizes() = entry->border_box_size();
  4030. break;
  4031. case Bindings::ResizeObserverBoxOptions::ContentBox:
  4032. // Matching sizes are entry.contentBoxSize if observation.observedBox is "content-box"
  4033. observation->last_reported_sizes() = entry->content_box_size();
  4034. break;
  4035. case Bindings::ResizeObserverBoxOptions::DevicePixelContentBox:
  4036. // Matching sizes are entry.devicePixelContentBoxSize if observation.observedBox is "device-pixel-content-box"
  4037. observation->last_reported_sizes() = entry->device_pixel_content_box_size();
  4038. break;
  4039. default:
  4040. VERIFY_NOT_REACHED();
  4041. }
  4042. // 4. Set targetDepth to the result of calculate depth for node for observation.target.
  4043. auto target_depth = calculate_depth_for_node(*observation->target());
  4044. // 5. Set shallowestTargetDepth to targetDepth if targetDepth < shallowestTargetDepth
  4045. if (target_depth < shallowest_target_depth)
  4046. shallowest_target_depth = target_depth;
  4047. }
  4048. // 4. Invoke observer.[[callback]] with entries.
  4049. observer->invoke_callback(entries);
  4050. // 5. Clear observer.[[activeTargets]].
  4051. observer->active_targets().clear();
  4052. }
  4053. return shallowest_target_depth;
  4054. }
  4055. // https://drafts.csswg.org/resize-observer-1/#has-active-observations-h
  4056. bool Document::has_active_resize_observations()
  4057. {
  4058. // 1. For each observer in [[resizeObservers]] run this step:
  4059. for (auto const& observer : m_resize_observers) {
  4060. // 1. If observer.[[activeTargets]] is not empty, return true.
  4061. if (!observer->active_targets().is_empty())
  4062. return true;
  4063. }
  4064. // 2. Return false.
  4065. return false;
  4066. }
  4067. // https://drafts.csswg.org/resize-observer-1/#has-skipped-observations-h
  4068. bool Document::has_skipped_resize_observations()
  4069. {
  4070. // 1. For each observer in [[resizeObservers]] run this step:
  4071. for (auto const& observer : m_resize_observers) {
  4072. // 1. If observer.[[skippedTargets]] is not empty, return true.
  4073. if (!observer->skipped_targets().is_empty())
  4074. return true;
  4075. }
  4076. // 2. Return false.
  4077. return false;
  4078. }
  4079. JS::NonnullGCPtr<WebIDL::ObservableArray> Document::adopted_style_sheets() const
  4080. {
  4081. if (!m_adopted_style_sheets)
  4082. m_adopted_style_sheets = create_adopted_style_sheets_list(const_cast<Document&>(*this));
  4083. return *m_adopted_style_sheets;
  4084. }
  4085. WebIDL::ExceptionOr<void> Document::set_adopted_style_sheets(JS::Value new_value)
  4086. {
  4087. if (!m_adopted_style_sheets)
  4088. m_adopted_style_sheets = create_adopted_style_sheets_list(const_cast<Document&>(*this));
  4089. m_adopted_style_sheets->clear();
  4090. auto iterator_record = TRY(get_iterator(vm(), new_value, JS::IteratorHint::Sync));
  4091. while (true) {
  4092. auto next = TRY(iterator_step_value(vm(), iterator_record));
  4093. if (!next.has_value())
  4094. break;
  4095. TRY(m_adopted_style_sheets->append(*next));
  4096. }
  4097. return {};
  4098. }
  4099. void Document::for_each_css_style_sheet(Function<void(CSS::CSSStyleSheet&)>&& callback) const
  4100. {
  4101. if (m_style_sheets) {
  4102. for (auto& style_sheet : m_style_sheets->sheets()) {
  4103. if (!(style_sheet->is_alternate() && style_sheet->disabled()))
  4104. callback(*style_sheet);
  4105. }
  4106. }
  4107. if (m_adopted_style_sheets) {
  4108. m_adopted_style_sheets->for_each<CSS::CSSStyleSheet>([&](auto& style_sheet) {
  4109. if (!style_sheet.disabled())
  4110. callback(style_sheet);
  4111. });
  4112. }
  4113. }
  4114. void Document::register_shadow_root(Badge<DOM::ShadowRoot>, DOM::ShadowRoot& shadow_root)
  4115. {
  4116. m_shadow_roots.append(shadow_root);
  4117. }
  4118. void Document::unregister_shadow_root(Badge<DOM::ShadowRoot>, DOM::ShadowRoot& shadow_root)
  4119. {
  4120. m_shadow_roots.remove_all_matching([&](auto& item) {
  4121. return item.ptr() == &shadow_root;
  4122. });
  4123. }
  4124. void Document::for_each_shadow_root(Function<void(DOM::ShadowRoot&)>&& callback)
  4125. {
  4126. for (auto& shadow_root : m_shadow_roots)
  4127. callback(shadow_root);
  4128. }
  4129. bool Document::is_decoded_svg() const
  4130. {
  4131. return is<Web::SVG::SVGDecodedImageData::SVGPageClient>(page().client());
  4132. }
  4133. // https://drafts.csswg.org/css-position-4/#add-an-element-to-the-top-layer
  4134. void Document::add_an_element_to_the_top_layer(JS::NonnullGCPtr<Element> element)
  4135. {
  4136. // 1. Let doc be el’s node document.
  4137. // 2. If el is already contained in doc’s top layer:
  4138. if (m_top_layer_elements.contains(element)) {
  4139. // Assert: el is also in doc’s pending top layer removals. (Otherwise, this is a spec error.)
  4140. VERIFY(m_top_layer_pending_removals.contains(element));
  4141. // Remove el from both doc’s top layer and pending top layer removals.
  4142. m_top_layer_elements.remove(element);
  4143. m_top_layer_pending_removals.remove(element);
  4144. }
  4145. // 3. Append el to doc’s top layer.
  4146. m_top_layer_elements.set(element);
  4147. element->set_in_top_layer(true);
  4148. // FIXME: 4. At the UA !important cascade origin, add a rule targeting el containing an overlay: auto declaration.
  4149. }
  4150. // https://drafts.csswg.org/css-position-4/#request-an-element-to-be-removed-from-the-top-layer
  4151. void Document::request_an_element_to_be_remove_from_the_top_layer(JS::NonnullGCPtr<Element> element)
  4152. {
  4153. // 1. Let doc be el’s node document.
  4154. // 2. If el is not contained doc’s top layer, or el is already contained in doc’s pending top layer removals, return.
  4155. if (!m_top_layer_elements.contains(element) || m_top_layer_pending_removals.contains(element))
  4156. return;
  4157. // FIXME: 3. Remove the UA !important overlay: auto rule targeting el.
  4158. // 4. Append el to doc’s pending top layer removals.
  4159. m_top_layer_pending_removals.set(element);
  4160. }
  4161. // https://drafts.csswg.org/css-position-4/#remove-an-element-from-the-top-layer-immediately
  4162. void Document::remove_an_element_from_the_top_layer_immediately(JS::NonnullGCPtr<Element> element)
  4163. {
  4164. // 1. Let doc be el’s node document.
  4165. // 2. Remove el from doc’s top layer and pending top layer removals.
  4166. m_top_layer_elements.remove(element);
  4167. element->set_in_top_layer(false);
  4168. // FIXME: 3. Remove the UA !important overlay: auto rule targeting el, if it exists.
  4169. }
  4170. // https://drafts.csswg.org/css-position-4/#process-top-layer-removals
  4171. void Document::process_top_layer_removals()
  4172. {
  4173. // 1. For each element el in doc’s pending top layer removals: if el’s computed value of overlay is none, or el is
  4174. // not rendered, remove el from doc’s top layer and pending top layer removals.
  4175. for (auto& element : m_top_layer_pending_removals) {
  4176. // FIXME: Check overlay property
  4177. if (!element->paintable()) {
  4178. m_top_layer_elements.remove(element);
  4179. m_top_layer_pending_removals.remove(element);
  4180. element->set_in_top_layer(false);
  4181. }
  4182. }
  4183. }
  4184. void Document::set_needs_to_refresh_clip_state(bool b)
  4185. {
  4186. if (auto* paintable = this->paintable())
  4187. paintable->set_needs_to_refresh_clip_state(b);
  4188. }
  4189. void Document::set_needs_to_refresh_scroll_state(bool b)
  4190. {
  4191. if (auto* paintable = this->paintable())
  4192. paintable->set_needs_to_refresh_scroll_state(b);
  4193. }
  4194. Vector<JS::Handle<DOM::Range>> Document::find_matching_text(String const& query, CaseSensitivity case_sensitivity)
  4195. {
  4196. if (!document_element() || !document_element()->layout_node())
  4197. return {};
  4198. struct TextPosition {
  4199. DOM::Text& dom_node;
  4200. size_t start_offset { 0 };
  4201. };
  4202. struct TextBlock {
  4203. String text;
  4204. Vector<TextPosition> positions;
  4205. };
  4206. auto gather_text_blocks = [&]() -> Vector<TextBlock> {
  4207. StringBuilder builder;
  4208. size_t current_start_position = 0;
  4209. Vector<TextPosition> text_positions;
  4210. Vector<TextBlock> text_blocks;
  4211. document_element()->layout_node()->for_each_in_inclusive_subtree([&](auto const& layout_node) {
  4212. if (layout_node.display().is_none() || !layout_node.paintable() || !layout_node.paintable()->is_visible())
  4213. return TraversalDecision::Continue;
  4214. if (layout_node.is_block_container()) {
  4215. if (!builder.is_empty()) {
  4216. text_blocks.append({ builder.to_string_without_validation(), text_positions });
  4217. current_start_position = 0;
  4218. text_positions.clear_with_capacity();
  4219. builder.clear();
  4220. }
  4221. return TraversalDecision::Continue;
  4222. }
  4223. if (layout_node.is_text_node()) {
  4224. auto const& text_node = verify_cast<Layout::TextNode>(layout_node);
  4225. auto& dom_node = const_cast<DOM::Text&>(text_node.dom_node());
  4226. if (text_positions.is_empty()) {
  4227. text_positions.empend(dom_node);
  4228. } else {
  4229. text_positions.empend(dom_node, current_start_position);
  4230. }
  4231. auto const& current_node_text = text_node.text_for_rendering();
  4232. current_start_position += current_node_text.bytes_as_string_view().length();
  4233. builder.append(move(current_node_text));
  4234. }
  4235. return TraversalDecision::Continue;
  4236. });
  4237. if (!builder.is_empty())
  4238. text_blocks.append({ builder.to_string_without_validation(), text_positions });
  4239. return text_blocks;
  4240. };
  4241. auto text_blocks = gather_text_blocks();
  4242. if (text_blocks.is_empty())
  4243. return {};
  4244. Vector<JS::Handle<DOM::Range>> matches;
  4245. for (auto const& text_block : text_blocks) {
  4246. size_t offset = 0;
  4247. size_t i = 0;
  4248. auto const& text = text_block.text;
  4249. auto* match_start_position = &text_block.positions[0];
  4250. while (true) {
  4251. auto match_index = case_sensitivity == CaseSensitivity::CaseInsensitive
  4252. ? text.find_byte_offset_ignoring_case(query, offset)
  4253. : text.find_byte_offset(query, offset);
  4254. if (!match_index.has_value())
  4255. break;
  4256. for (; i < text_block.positions.size() - 1 && match_index.value() > text_block.positions[i + 1].start_offset; ++i)
  4257. match_start_position = &text_block.positions[i + 1];
  4258. auto range = create_range();
  4259. auto start_position = match_index.value() - match_start_position->start_offset;
  4260. auto& start_dom_node = match_start_position->dom_node;
  4261. (void)range->set_start(start_dom_node, start_position);
  4262. auto* match_end_position = match_start_position;
  4263. for (; i < text_block.positions.size() - 1 && (match_index.value() + query.bytes_as_string_view().length() > text_block.positions[i + 1].start_offset); ++i)
  4264. match_end_position = &text_block.positions[i + 1];
  4265. auto& end_dom_node = match_end_position->dom_node;
  4266. auto end_position = match_index.value() + query.bytes_as_string_view().length() - match_end_position->start_offset;
  4267. (void)range->set_end(end_dom_node, end_position);
  4268. matches.append(range);
  4269. match_start_position = match_end_position;
  4270. offset = match_index.value() + query.bytes_as_string_view().length() + 1;
  4271. if (offset >= text.bytes_as_string_view().length())
  4272. break;
  4273. }
  4274. }
  4275. return matches;
  4276. }
  4277. // https://dom.spec.whatwg.org/#document-allow-declarative-shadow-roots
  4278. bool Document::allow_declarative_shadow_roots() const
  4279. {
  4280. return m_allow_declarative_shadow_roots;
  4281. }
  4282. // https://dom.spec.whatwg.org/#document-allow-declarative-shadow-roots
  4283. void Document::set_allow_declarative_shadow_roots(bool allow)
  4284. {
  4285. m_allow_declarative_shadow_roots = allow;
  4286. }
  4287. // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#parse-html-from-a-string
  4288. void Document::parse_html_from_a_string(StringView html)
  4289. {
  4290. // 1. Set document's type to "html".
  4291. set_document_type(DOM::Document::Type::HTML);
  4292. // 2. Create an HTML parser parser, associated with document.
  4293. // 3. Place html into the input stream for parser. The encoding confidence is irrelevant.
  4294. // FIXME: We don't have the concept of encoding confidence yet.
  4295. auto parser = HTML::HTMLParser::create(*this, html, "UTF-8"sv);
  4296. // 4. Start parser and let it run until it has consumed all the characters just inserted into the input stream.
  4297. // FIXME: This is to match the default URL. Instead, pass in this's relevant global object's associated Document's URL.
  4298. parser->run("about:blank"sv);
  4299. }
  4300. // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-parsehtmlunsafe
  4301. JS::NonnullGCPtr<Document> Document::parse_html_unsafe(JS::VM& vm, StringView html)
  4302. {
  4303. auto& realm = *vm.current_realm();
  4304. // FIXME: 1. Let compliantHTML to the result of invoking the Get Trusted Type compliant string algorithm with TrustedHTML, this's relevant global object, html, "Document parseHTMLUnsafe", and "script".
  4305. // 2. Let document be a new Document, whose content type is "text/html".
  4306. JS::NonnullGCPtr<DOM::Document> document = Document::create(realm);
  4307. document->set_content_type("text/html"_string);
  4308. // 3. Set document's allow declarative shadow roots to true.
  4309. document->set_allow_declarative_shadow_roots(true);
  4310. // 4. Parse HTML from a string given document and compliantHTML. // FIXME: Use compliantHTML.
  4311. document->parse_html_from_a_string(html);
  4312. // 5. Return document.
  4313. return document;
  4314. }
  4315. }