Document.cpp 250 KB

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