Parser.cpp 376 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192
  1. /*
  2. * Copyright (c) 2018-2024, Andreas Kling <andreas@ladybird.org>
  3. * Copyright (c) 2020-2021, the SerenityOS developers.
  4. * Copyright (c) 2021-2024, Sam Atkins <sam@ladybird.org>
  5. * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
  6. * Copyright (c) 2022, MacDue <macdue@dueutil.tech>
  7. * Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
  8. * Copyright (c) 2024, Tommy van der Vorst <tommy@pixelspark.nl>
  9. * Copyright (c) 2024, Matthew Olsson <mattco@serenityos.org>
  10. * Copyright (c) 2024, Glenn Skrzypczak <glenn.skrzypczak@gmail.com>
  11. *
  12. * SPDX-License-Identifier: BSD-2-Clause
  13. */
  14. #include <AK/CharacterTypes.h>
  15. #include <AK/Debug.h>
  16. #include <AK/GenericLexer.h>
  17. #include <AK/NonnullRawPtr.h>
  18. #include <AK/SourceLocation.h>
  19. #include <AK/TemporaryChange.h>
  20. #include <LibWeb/CSS/CSSStyleDeclaration.h>
  21. #include <LibWeb/CSS/CSSStyleSheet.h>
  22. #include <LibWeb/CSS/CSSStyleValue.h>
  23. #include <LibWeb/CSS/CalculatedOr.h>
  24. #include <LibWeb/CSS/EdgeRect.h>
  25. #include <LibWeb/CSS/MediaList.h>
  26. #include <LibWeb/CSS/Parser/Parser.h>
  27. #include <LibWeb/CSS/PropertyName.h>
  28. #include <LibWeb/CSS/Selector.h>
  29. #include <LibWeb/CSS/Sizing.h>
  30. #include <LibWeb/CSS/StyleComputer.h>
  31. #include <LibWeb/CSS/StyleValues/AngleStyleValue.h>
  32. #include <LibWeb/CSS/StyleValues/BackgroundRepeatStyleValue.h>
  33. #include <LibWeb/CSS/StyleValues/BackgroundSizeStyleValue.h>
  34. #include <LibWeb/CSS/StyleValues/BasicShapeStyleValue.h>
  35. #include <LibWeb/CSS/StyleValues/BorderRadiusStyleValue.h>
  36. #include <LibWeb/CSS/StyleValues/CSSColor.h>
  37. #include <LibWeb/CSS/StyleValues/CSSColorValue.h>
  38. #include <LibWeb/CSS/StyleValues/CSSHSL.h>
  39. #include <LibWeb/CSS/StyleValues/CSSHWB.h>
  40. #include <LibWeb/CSS/StyleValues/CSSKeywordValue.h>
  41. #include <LibWeb/CSS/StyleValues/CSSLCHLike.h>
  42. #include <LibWeb/CSS/StyleValues/CSSLabLike.h>
  43. #include <LibWeb/CSS/StyleValues/CSSRGB.h>
  44. #include <LibWeb/CSS/StyleValues/ContentStyleValue.h>
  45. #include <LibWeb/CSS/StyleValues/CounterDefinitionsStyleValue.h>
  46. #include <LibWeb/CSS/StyleValues/CounterStyleValue.h>
  47. #include <LibWeb/CSS/StyleValues/CustomIdentStyleValue.h>
  48. #include <LibWeb/CSS/StyleValues/DisplayStyleValue.h>
  49. #include <LibWeb/CSS/StyleValues/EasingStyleValue.h>
  50. #include <LibWeb/CSS/StyleValues/EdgeStyleValue.h>
  51. #include <LibWeb/CSS/StyleValues/FilterValueListStyleValue.h>
  52. #include <LibWeb/CSS/StyleValues/FlexStyleValue.h>
  53. #include <LibWeb/CSS/StyleValues/FrequencyStyleValue.h>
  54. #include <LibWeb/CSS/StyleValues/GridAutoFlowStyleValue.h>
  55. #include <LibWeb/CSS/StyleValues/GridTemplateAreaStyleValue.h>
  56. #include <LibWeb/CSS/StyleValues/GridTrackPlacementStyleValue.h>
  57. #include <LibWeb/CSS/StyleValues/GridTrackSizeListStyleValue.h>
  58. #include <LibWeb/CSS/StyleValues/ImageStyleValue.h>
  59. #include <LibWeb/CSS/StyleValues/IntegerStyleValue.h>
  60. #include <LibWeb/CSS/StyleValues/LengthStyleValue.h>
  61. #include <LibWeb/CSS/StyleValues/MathDepthStyleValue.h>
  62. #include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
  63. #include <LibWeb/CSS/StyleValues/OpenTypeTaggedStyleValue.h>
  64. #include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
  65. #include <LibWeb/CSS/StyleValues/PositionStyleValue.h>
  66. #include <LibWeb/CSS/StyleValues/RatioStyleValue.h>
  67. #include <LibWeb/CSS/StyleValues/RectStyleValue.h>
  68. #include <LibWeb/CSS/StyleValues/ResolutionStyleValue.h>
  69. #include <LibWeb/CSS/StyleValues/RotationStyleValue.h>
  70. #include <LibWeb/CSS/StyleValues/ScrollbarGutterStyleValue.h>
  71. #include <LibWeb/CSS/StyleValues/ShadowStyleValue.h>
  72. #include <LibWeb/CSS/StyleValues/ShorthandStyleValue.h>
  73. #include <LibWeb/CSS/StyleValues/StringStyleValue.h>
  74. #include <LibWeb/CSS/StyleValues/StyleValueList.h>
  75. #include <LibWeb/CSS/StyleValues/TimeStyleValue.h>
  76. #include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
  77. #include <LibWeb/CSS/StyleValues/TransitionStyleValue.h>
  78. #include <LibWeb/CSS/StyleValues/TranslationStyleValue.h>
  79. #include <LibWeb/CSS/StyleValues/URLStyleValue.h>
  80. #include <LibWeb/CSS/StyleValues/UnresolvedStyleValue.h>
  81. #include <LibWeb/Dump.h>
  82. #include <LibWeb/HTML/HTMLImageElement.h>
  83. #include <LibWeb/Infra/CharacterTypes.h>
  84. #include <LibWeb/Infra/Strings.h>
  85. static void log_parse_error(SourceLocation const& location = SourceLocation::current())
  86. {
  87. dbgln_if(CSS_PARSER_DEBUG, "Parse error (CSS) {}", location);
  88. }
  89. namespace Web::CSS::Parser {
  90. Parser Parser::create(ParsingContext const& context, StringView input, StringView encoding)
  91. {
  92. auto tokens = Tokenizer::tokenize(input, encoding);
  93. return Parser { context, move(tokens) };
  94. }
  95. Parser::Parser(ParsingContext const& context, Vector<Token> tokens)
  96. : m_context(context)
  97. , m_tokens(move(tokens))
  98. , m_token_stream(m_tokens)
  99. {
  100. }
  101. Parser::Parser(Parser&& other)
  102. : m_context(other.m_context)
  103. , m_tokens(move(other.m_tokens))
  104. , m_token_stream(m_tokens)
  105. {
  106. // Moving the TokenStream directly from `other` would break it, because TokenStream holds
  107. // a reference to the Vector<Token>, so it would be pointing at the old Parser's tokens.
  108. // So instead, we create a new TokenStream from this Parser's tokens, and then tell it to
  109. // copy the other TokenStream's state. This is quite hacky.
  110. m_token_stream.copy_state({}, other.m_token_stream);
  111. }
  112. // https://drafts.csswg.org/css-syntax/#parse-stylesheet
  113. template<typename T>
  114. Parser::ParsedStyleSheet Parser::parse_a_stylesheet(TokenStream<T>& input, Optional<URL::URL> location)
  115. {
  116. // To parse a stylesheet from an input given an optional url location:
  117. // 1. If input is a byte stream for a stylesheet, decode bytes from input, and set input to the result.
  118. // 2. Normalize input, and set input to the result.
  119. // NOTE: These are done automatically when creating the Parser.
  120. // 3. Create a new stylesheet, with its location set to location (or null, if location was not passed).
  121. ParsedStyleSheet style_sheet;
  122. style_sheet.location = move(location);
  123. // 4. Consume a stylesheet’s contents from input, and set the stylesheet’s rules to the result.
  124. style_sheet.rules = consume_a_stylesheets_contents(input);
  125. // 5. Return the stylesheet.
  126. return style_sheet;
  127. }
  128. // https://drafts.csswg.org/css-syntax/#parse-a-stylesheets-contents
  129. template<typename T>
  130. Vector<Rule> Parser::parse_a_stylesheets_contents(TokenStream<T>& input)
  131. {
  132. // To parse a stylesheet’s contents from input:
  133. // 1. Normalize input, and set input to the result.
  134. // NOTE: This is done automatically when creating the Parser.
  135. // 2. Consume a stylesheet’s contents from input, and return the result.
  136. return consume_a_stylesheets_contents(input);
  137. }
  138. // https://drafts.csswg.org/css-syntax/#parse-a-css-stylesheet
  139. CSSStyleSheet* Parser::parse_as_css_stylesheet(Optional<URL::URL> location)
  140. {
  141. // To parse a CSS stylesheet, first parse a stylesheet.
  142. auto const& style_sheet = parse_a_stylesheet(m_token_stream, {});
  143. // Interpret all of the resulting top-level qualified rules as style rules, defined below.
  144. GC::MarkedVector<CSSRule*> rules(m_context.realm().heap());
  145. for (auto const& raw_rule : style_sheet.rules) {
  146. auto rule = convert_to_rule(raw_rule, Nested::No);
  147. // If any style rule is invalid, or any at-rule is not recognized or is invalid according to its grammar or context, it’s a parse error.
  148. // Discard that rule.
  149. if (!rule) {
  150. log_parse_error();
  151. continue;
  152. }
  153. rules.append(rule);
  154. }
  155. auto rule_list = CSSRuleList::create(m_context.realm(), rules);
  156. auto media_list = MediaList::create(m_context.realm(), {});
  157. return CSSStyleSheet::create(m_context.realm(), rule_list, media_list, move(location));
  158. }
  159. RefPtr<Supports> Parser::parse_as_supports()
  160. {
  161. return parse_a_supports(m_token_stream);
  162. }
  163. template<typename T>
  164. RefPtr<Supports> Parser::parse_a_supports(TokenStream<T>& tokens)
  165. {
  166. auto component_values = parse_a_list_of_component_values(tokens);
  167. TokenStream<ComponentValue> token_stream { component_values };
  168. m_rule_context.append(ContextType::SupportsCondition);
  169. auto maybe_condition = parse_supports_condition(token_stream);
  170. m_rule_context.take_last();
  171. token_stream.discard_whitespace();
  172. if (maybe_condition && !token_stream.has_next_token())
  173. return Supports::create(m_context.realm(), maybe_condition.release_nonnull());
  174. return {};
  175. }
  176. template RefPtr<Supports> Parser::parse_a_supports(TokenStream<ComponentValue>&);
  177. template RefPtr<Supports> Parser::parse_a_supports(TokenStream<Token>&);
  178. OwnPtr<Supports::Condition> Parser::parse_supports_condition(TokenStream<ComponentValue>& tokens)
  179. {
  180. auto transaction = tokens.begin_transaction();
  181. tokens.discard_whitespace();
  182. auto const& peeked_token = tokens.next_token();
  183. // `not <supports-in-parens>`
  184. if (peeked_token.is_ident("not"sv)) {
  185. tokens.discard_a_token();
  186. tokens.discard_whitespace();
  187. auto child = parse_supports_in_parens(tokens);
  188. if (!child.has_value())
  189. return {};
  190. transaction.commit();
  191. auto condition = make<Supports::Condition>();
  192. condition->type = Supports::Condition::Type::Not;
  193. condition->children.append(child.release_value());
  194. return condition;
  195. }
  196. // ` <supports-in-parens> [ and <supports-in-parens> ]*
  197. // | <supports-in-parens> [ or <supports-in-parens> ]*`
  198. Vector<Supports::InParens> children;
  199. Optional<Supports::Condition::Type> condition_type {};
  200. auto as_condition_type = [](auto& token) -> Optional<Supports::Condition::Type> {
  201. if (!token.is(Token::Type::Ident))
  202. return {};
  203. auto ident = token.token().ident();
  204. if (ident.equals_ignoring_ascii_case("and"sv))
  205. return Supports::Condition::Type::And;
  206. if (ident.equals_ignoring_ascii_case("or"sv))
  207. return Supports::Condition::Type::Or;
  208. return {};
  209. };
  210. while (tokens.has_next_token()) {
  211. if (!children.is_empty()) {
  212. // Expect `and` or `or` here
  213. auto maybe_combination = as_condition_type(tokens.consume_a_token());
  214. if (!maybe_combination.has_value())
  215. return {};
  216. if (!condition_type.has_value()) {
  217. condition_type = maybe_combination.value();
  218. } else if (maybe_combination != condition_type) {
  219. return {};
  220. }
  221. }
  222. tokens.discard_whitespace();
  223. if (auto in_parens = parse_supports_in_parens(tokens); in_parens.has_value()) {
  224. children.append(in_parens.release_value());
  225. } else {
  226. return {};
  227. }
  228. tokens.discard_whitespace();
  229. }
  230. if (children.is_empty())
  231. return {};
  232. transaction.commit();
  233. auto condition = make<Supports::Condition>();
  234. condition->type = condition_type.value_or(Supports::Condition::Type::Or);
  235. condition->children = move(children);
  236. return condition;
  237. }
  238. Optional<Supports::InParens> Parser::parse_supports_in_parens(TokenStream<ComponentValue>& tokens)
  239. {
  240. // `( <supports-condition> )`
  241. auto const& first_token = tokens.next_token();
  242. if (first_token.is_block() && first_token.block().is_paren()) {
  243. auto transaction = tokens.begin_transaction();
  244. tokens.discard_a_token();
  245. tokens.discard_whitespace();
  246. TokenStream child_tokens { first_token.block().value };
  247. if (auto condition = parse_supports_condition(child_tokens)) {
  248. if (child_tokens.has_next_token())
  249. return {};
  250. transaction.commit();
  251. return Supports::InParens {
  252. .value = { condition.release_nonnull() }
  253. };
  254. }
  255. }
  256. // `<supports-feature>`
  257. if (auto feature = parse_supports_feature(tokens); feature.has_value()) {
  258. return Supports::InParens {
  259. .value = { feature.release_value() }
  260. };
  261. }
  262. // `<general-enclosed>`
  263. if (auto general_enclosed = parse_general_enclosed(tokens); general_enclosed.has_value()) {
  264. return Supports::InParens {
  265. .value = general_enclosed.release_value()
  266. };
  267. }
  268. return {};
  269. }
  270. Optional<Supports::Feature> Parser::parse_supports_feature(TokenStream<ComponentValue>& tokens)
  271. {
  272. auto transaction = tokens.begin_transaction();
  273. tokens.discard_whitespace();
  274. auto const& first_token = tokens.consume_a_token();
  275. // `<supports-decl>`
  276. if (first_token.is_block() && first_token.block().is_paren()) {
  277. TokenStream block_tokens { first_token.block().value };
  278. // FIXME: Parsing and then converting back to a string is weird.
  279. if (auto declaration = consume_a_declaration(block_tokens); declaration.has_value()) {
  280. transaction.commit();
  281. return Supports::Feature {
  282. Supports::Declaration { declaration->to_string() }
  283. };
  284. }
  285. }
  286. // `<supports-selector-fn>`
  287. if (first_token.is_function("selector"sv)) {
  288. // FIXME: Parsing and then converting back to a string is weird.
  289. StringBuilder builder;
  290. for (auto const& item : first_token.function().value)
  291. builder.append(item.to_string());
  292. transaction.commit();
  293. return Supports::Feature {
  294. Supports::Selector { builder.to_string().release_value_but_fixme_should_propagate_errors() }
  295. };
  296. }
  297. return {};
  298. }
  299. // https://www.w3.org/TR/mediaqueries-4/#typedef-general-enclosed
  300. Optional<GeneralEnclosed> Parser::parse_general_enclosed(TokenStream<ComponentValue>& tokens)
  301. {
  302. auto transaction = tokens.begin_transaction();
  303. tokens.discard_whitespace();
  304. auto const& first_token = tokens.consume_a_token();
  305. // `[ <function-token> <any-value>? ) ]`
  306. if (first_token.is_function()) {
  307. transaction.commit();
  308. return GeneralEnclosed { first_token.to_string() };
  309. }
  310. // `( <any-value>? )`
  311. if (first_token.is_block() && first_token.block().is_paren()) {
  312. transaction.commit();
  313. return GeneralEnclosed { first_token.to_string() };
  314. }
  315. return {};
  316. }
  317. // https://drafts.csswg.org/css-syntax/#consume-stylesheet-contents
  318. template<typename T>
  319. Vector<Rule> Parser::consume_a_stylesheets_contents(TokenStream<T>& input)
  320. {
  321. // To consume a stylesheet’s contents from a token stream input:
  322. // Let rules be an initially empty list of rules.
  323. Vector<Rule> rules;
  324. // Process input:
  325. for (;;) {
  326. auto& token = input.next_token();
  327. // <whitespace-token>
  328. if (token.is(Token::Type::Whitespace)) {
  329. // Discard a token from input.
  330. input.discard_a_token();
  331. continue;
  332. }
  333. // <EOF-token>
  334. if (token.is(Token::Type::EndOfFile)) {
  335. // Return rules.
  336. return rules;
  337. }
  338. // <CDO-token>
  339. // <CDC-token>
  340. if (token.is(Token::Type::CDO) || token.is(Token::Type::CDC)) {
  341. // Discard a token from input.
  342. input.discard_a_token();
  343. continue;
  344. }
  345. // <at-keyword-token>
  346. if (token.is(Token::Type::AtKeyword)) {
  347. // Consume an at-rule from input. If anything is returned, append it to rules.
  348. if (auto maybe_at_rule = consume_an_at_rule(input); maybe_at_rule.has_value())
  349. rules.append(*maybe_at_rule);
  350. continue;
  351. }
  352. // anything else
  353. {
  354. // Consume a qualified rule from input. If a rule is returned, append it to rules.
  355. consume_a_qualified_rule(input).visit(
  356. [&](QualifiedRule qualified_rule) { rules.append(move(qualified_rule)); },
  357. [](auto&) {});
  358. }
  359. }
  360. }
  361. // https://drafts.csswg.org/css-syntax/#consume-at-rule
  362. template<typename T>
  363. Optional<AtRule> Parser::consume_an_at_rule(TokenStream<T>& input, Nested nested)
  364. {
  365. // To consume an at-rule from a token stream input, given an optional bool nested (default false):
  366. // Assert: The next token is an <at-keyword-token>.
  367. VERIFY(input.next_token().is(Token::Type::AtKeyword));
  368. // Consume a token from input, and let rule be a new at-rule with its name set to the returned token’s value,
  369. // its prelude initially set to an empty list, and no declarations or child rules.
  370. AtRule rule {
  371. .name = ((Token)input.consume_a_token()).at_keyword(),
  372. .prelude = {},
  373. .child_rules_and_lists_of_declarations = {},
  374. };
  375. // Process input:
  376. for (;;) {
  377. auto& token = input.next_token();
  378. // <semicolon-token>
  379. // <EOF-token>
  380. if (token.is(Token::Type::Semicolon) || token.is(Token::Type::EndOfFile)) {
  381. // Discard a token from input. If rule is valid in the current context, return it; otherwise return nothing.
  382. input.discard_a_token();
  383. if (is_valid_in_the_current_context(rule))
  384. return rule;
  385. return {};
  386. }
  387. // <}-token>
  388. if (token.is(Token::Type::CloseCurly)) {
  389. // If nested is true:
  390. if (nested == Nested::Yes) {
  391. // If rule is valid in the current context, return it.
  392. if (is_valid_in_the_current_context(rule))
  393. return rule;
  394. // Otherwise, return nothing.
  395. return {};
  396. }
  397. // Otherwise, consume a token and append the result to rule’s prelude.
  398. else {
  399. rule.prelude.append(input.consume_a_token());
  400. }
  401. continue;
  402. }
  403. // <{-token>
  404. if (token.is(Token::Type::OpenCurly)) {
  405. // Consume a block from input, and assign the result to rule’s child rules.
  406. m_rule_context.append(context_type_for_at_rule(rule.name));
  407. rule.child_rules_and_lists_of_declarations = consume_a_block(input);
  408. m_rule_context.take_last();
  409. // If rule is valid in the current context, return it. Otherwise, return nothing.
  410. if (is_valid_in_the_current_context(rule))
  411. return rule;
  412. return {};
  413. }
  414. // anything else
  415. {
  416. // Consume a component value from input and append the returned value to rule’s prelude.
  417. rule.prelude.append(consume_a_component_value(input));
  418. }
  419. }
  420. }
  421. // https://drafts.csswg.org/css-syntax/#consume-qualified-rule
  422. template<typename T>
  423. Variant<Empty, QualifiedRule, Parser::InvalidRuleError> Parser::consume_a_qualified_rule(TokenStream<T>& input, Optional<Token::Type> stop_token, Nested nested)
  424. {
  425. // To consume a qualified rule, from a token stream input, given an optional token stop token and an optional bool nested (default false):
  426. // Let rule be a new qualified rule with its prelude, declarations, and child rules all initially set to empty lists.
  427. QualifiedRule rule {
  428. .prelude = {},
  429. .declarations = {},
  430. .child_rules = {},
  431. };
  432. // NOTE: Qualified rules inside @keyframes are a keyframe rule.
  433. // We'll assume all others are style rules.
  434. auto type_of_qualified_rule = (!m_rule_context.is_empty() && m_rule_context.last() == ContextType::AtKeyframes)
  435. ? ContextType::Keyframe
  436. : ContextType::Style;
  437. // Process input:
  438. for (;;) {
  439. auto& token = input.next_token();
  440. // <EOF-token>
  441. // stop token (if passed)
  442. if (token.is(Token::Type::EndOfFile) || (stop_token.has_value() && token.is(*stop_token))) {
  443. // This is a parse error. Return nothing.
  444. log_parse_error();
  445. return {};
  446. }
  447. // <}-token>
  448. if (token.is(Token::Type::CloseCurly)) {
  449. // This is a parse error. If nested is true, return nothing. Otherwise, consume a token and append the result to rule’s prelude.
  450. log_parse_error();
  451. if (nested == Nested::Yes)
  452. return {};
  453. rule.prelude.append(input.consume_a_token());
  454. continue;
  455. }
  456. // <{-token>
  457. if (token.is(Token::Type::OpenCurly)) {
  458. // If the first two non-<whitespace-token> values of rule’s prelude are an <ident-token> whose value starts with "--"
  459. // followed by a <colon-token>, then:
  460. TokenStream prelude_tokens { rule.prelude };
  461. prelude_tokens.discard_whitespace();
  462. auto& first_non_whitespace = prelude_tokens.consume_a_token();
  463. prelude_tokens.discard_whitespace();
  464. auto& second_non_whitespace = prelude_tokens.consume_a_token();
  465. if (first_non_whitespace.is(Token::Type::Ident) && first_non_whitespace.token().ident().starts_with_bytes("--"sv)
  466. && second_non_whitespace.is(Token::Type::Colon)) {
  467. // If nested is true, consume the remnants of a bad declaration from input, with nested set to true, and return nothing.
  468. if (nested == Nested::Yes) {
  469. consume_the_remnants_of_a_bad_declaration(input, Nested::Yes);
  470. return {};
  471. }
  472. // If nested is false, consume a block from input, and return nothing.
  473. (void)consume_a_block(input);
  474. return {};
  475. }
  476. // Otherwise, consume a block from input, and let child rules be the result.
  477. m_rule_context.append(type_of_qualified_rule);
  478. rule.child_rules = consume_a_block(input);
  479. m_rule_context.take_last();
  480. // If the first item of child rules is a list of declarations, remove it from child rules and assign it to rule’s declarations.
  481. if (!rule.child_rules.is_empty() && rule.child_rules.first().has<Vector<Declaration>>()) {
  482. auto first = rule.child_rules.take_first();
  483. rule.declarations = move(first.get<Vector<Declaration>>());
  484. }
  485. // If any remaining items of child rules are lists of declarations, replace them with nested declarations rules
  486. // containing the list as its sole child. Assign child rules to rule’s child rules.
  487. // NOTE: We do this later, when converting the QualifiedRule to a CSSRule type.
  488. // If rule is valid in the current context, return it; otherwise return an invalid rule error.
  489. if (is_valid_in_the_current_context(rule))
  490. return rule;
  491. return InvalidRuleError {};
  492. }
  493. // anything else
  494. {
  495. // Consume a component value from input and append the result to rule’s prelude.
  496. rule.prelude.append(consume_a_component_value(input));
  497. }
  498. }
  499. }
  500. // https://drafts.csswg.org/css-syntax/#consume-block
  501. template<typename T>
  502. Vector<RuleOrListOfDeclarations> Parser::consume_a_block(TokenStream<T>& input)
  503. {
  504. // To consume a block, from a token stream input:
  505. // Assert: The next token is a <{-token>.
  506. VERIFY(input.next_token().is(Token::Type::OpenCurly));
  507. // Discard a token from input.
  508. input.discard_a_token();
  509. // Consume a block’s contents from input and let rules be the result.
  510. auto rules = consume_a_blocks_contents(input);
  511. // Discard a token from input.
  512. input.discard_a_token();
  513. // Return rules.
  514. return rules;
  515. }
  516. // https://drafts.csswg.org/css-syntax/#consume-block-contents
  517. template<typename T>
  518. Vector<RuleOrListOfDeclarations> Parser::consume_a_blocks_contents(TokenStream<T>& input)
  519. {
  520. // To consume a block’s contents from a token stream input:
  521. // Let rules be an empty list, containing either rules or lists of declarations.
  522. Vector<RuleOrListOfDeclarations> rules;
  523. // Let decls be an empty list of declarations.
  524. Vector<Declaration> declarations;
  525. // Process input:
  526. for (;;) {
  527. auto& token = input.next_token();
  528. // <whitespace-token>
  529. // <semicolon-token>
  530. if (token.is(Token::Type::Whitespace) || token.is(Token::Type::Semicolon)) {
  531. // Discard a token from input.
  532. input.discard_a_token();
  533. continue;
  534. }
  535. // <EOF-token>
  536. // <}-token>
  537. if (token.is(Token::Type::EndOfFile) || token.is(Token::Type::CloseCurly)) {
  538. // AD-HOC: If decls is not empty, append it to rules.
  539. // Spec issue: https://github.com/w3c/csswg-drafts/issues/11017
  540. if (!declarations.is_empty())
  541. rules.append(move(declarations));
  542. // Return rules.
  543. return rules;
  544. }
  545. // <at-keyword-token>
  546. if (token.is(Token::Type::AtKeyword)) {
  547. // If decls is not empty, append it to rules, and set decls to a fresh empty list of declarations.
  548. if (!declarations.is_empty()) {
  549. rules.append(move(declarations));
  550. declarations = {};
  551. }
  552. // Consume an at-rule from input, with nested set to true.
  553. // If a rule was returned, append it to rules.
  554. if (auto at_rule = consume_an_at_rule(input, Nested::Yes); at_rule.has_value())
  555. rules.append({ at_rule.release_value() });
  556. continue;
  557. }
  558. // anything else
  559. {
  560. // Mark input.
  561. input.mark();
  562. // Consume a declaration from input, with nested set to true.
  563. // If a declaration was returned, append it to decls, and discard a mark from input.
  564. if (auto declaration = consume_a_declaration(input, Nested::Yes); declaration.has_value()) {
  565. declarations.append(declaration.release_value());
  566. input.discard_a_mark();
  567. }
  568. // Otherwise, restore a mark from input, then consume a qualified rule from input,
  569. // with nested set to true, and <semicolon-token> as the stop token.
  570. else {
  571. input.restore_a_mark();
  572. consume_a_qualified_rule(input, Token::Type::Semicolon, Nested::Yes).visit(
  573. // -> If nothing was returned
  574. [](Empty&) {
  575. // Do nothing
  576. },
  577. // -> If an invalid rule error was returned
  578. [&](InvalidRuleError&) {
  579. // If decls is not empty, append decls to rules, and set decls to a fresh empty list of declarations. (Otherwise, do nothing.)
  580. if (!declarations.is_empty()) {
  581. rules.append(move(declarations));
  582. declarations = {};
  583. }
  584. },
  585. // -> If a rule was returned
  586. [&](QualifiedRule rule) {
  587. // If decls is not empty, append decls to rules, and set decls to a fresh empty list of declarations.
  588. if (!declarations.is_empty()) {
  589. rules.append(move(declarations));
  590. declarations = {};
  591. }
  592. // Append the rule to rules.
  593. rules.append({ move(rule) });
  594. });
  595. }
  596. }
  597. }
  598. }
  599. template<>
  600. ComponentValue Parser::consume_a_component_value<ComponentValue>(TokenStream<ComponentValue>& tokens)
  601. {
  602. // Note: This overload is called once tokens have already been converted into component values,
  603. // so we do not need to do the work in the more general overload.
  604. return tokens.consume_a_token();
  605. }
  606. // 5.4.7. Consume a component value
  607. // https://drafts.csswg.org/css-syntax/#consume-component-value
  608. template<typename T>
  609. ComponentValue Parser::consume_a_component_value(TokenStream<T>& input)
  610. {
  611. // To consume a component value from a token stream input:
  612. // Process input:
  613. for (;;) {
  614. auto& token = input.next_token();
  615. // <{-token>
  616. // <[-token>
  617. // <(-token>
  618. if (token.is(Token::Type::OpenCurly) || token.is(Token::Type::OpenSquare) || token.is(Token::Type::OpenParen)) {
  619. // Consume a simple block from input and return the result.
  620. return ComponentValue { consume_a_simple_block(input) };
  621. }
  622. // <function-token>
  623. if (token.is(Token::Type::Function)) {
  624. // Consume a function from input and return the result.
  625. return ComponentValue { consume_a_function(input) };
  626. }
  627. // anything else
  628. {
  629. // Consume a token from input and return the result.
  630. return ComponentValue { input.consume_a_token() };
  631. }
  632. }
  633. }
  634. template<typename T>
  635. Vector<ComponentValue> Parser::consume_a_list_of_component_values(TokenStream<T>& input, Optional<Token::Type> stop_token, Nested nested)
  636. {
  637. // To consume a list of component values from a token stream input, given an optional token stop token
  638. // and an optional boolean nested (default false):
  639. // Let values be an empty list of component values.
  640. Vector<ComponentValue> values;
  641. // Process input:
  642. for (;;) {
  643. auto& token = input.next_token();
  644. // <eof-token>
  645. // stop token (if passed)
  646. if (token.is(Token::Type::EndOfFile) || (stop_token.has_value() && token.is(*stop_token))) {
  647. // Return values.
  648. return values;
  649. }
  650. // <}-token>
  651. if (token.is(Token::Type::CloseCurly)) {
  652. // If nested is true, return values.
  653. if (nested == Nested::Yes) {
  654. return values;
  655. }
  656. // Otherwise, this is a parse error. Consume a token from input and append the result to values.
  657. else {
  658. log_parse_error();
  659. values.append(input.consume_a_token());
  660. }
  661. }
  662. // anything else
  663. {
  664. // Consume a component value from input, and append the result to values.
  665. values.append(consume_a_component_value(input));
  666. }
  667. }
  668. }
  669. // https://drafts.csswg.org/css-syntax/#consume-simple-block
  670. template<typename T>
  671. SimpleBlock Parser::consume_a_simple_block(TokenStream<T>& input)
  672. {
  673. // To consume a simple block from a token stream input:
  674. // Assert: the next token of input is <{-token>, <[-token>, or <(-token>.
  675. auto& next = input.next_token();
  676. VERIFY(next.is(Token::Type::OpenCurly) || next.is(Token::Type::OpenSquare) || next.is(Token::Type::OpenParen));
  677. // Let ending token be the mirror variant of the next token. (E.g. if it was called with <[-token>, the ending token is <]-token>.)
  678. auto ending_token = ((Token)input.next_token()).mirror_variant();
  679. // Let block be a new simple block with its associated token set to the next token and with its value initially set to an empty list.
  680. SimpleBlock block {
  681. .token = input.next_token(),
  682. .value = {},
  683. };
  684. // Discard a token from input.
  685. input.discard_a_token();
  686. // Process input:
  687. for (;;) {
  688. auto& token = input.next_token();
  689. // <eof-token>
  690. // ending token
  691. if (token.is(Token::Type::EndOfFile) || token.is(ending_token)) {
  692. // Discard a token from input. Return block.
  693. // AD-HOC: Store the token instead as the "end token"
  694. block.end_token = input.consume_a_token();
  695. return block;
  696. }
  697. // anything else
  698. {
  699. // Consume a component value from input and append the result to block’s value.
  700. block.value.empend(move(consume_a_component_value(input)));
  701. }
  702. }
  703. }
  704. // https://drafts.csswg.org/css-syntax/#consume-function
  705. template<typename T>
  706. Function Parser::consume_a_function(TokenStream<T>& input)
  707. {
  708. // To consume a function from a token stream input:
  709. // Assert: The next token is a <function-token>.
  710. VERIFY(input.next_token().is(Token::Type::Function));
  711. // Consume a token from input, and let function be a new function with its name equal the returned token’s value,
  712. // and a value set to an empty list.
  713. auto name_token = ((Token)input.consume_a_token());
  714. Function function {
  715. .name = name_token.function(),
  716. .value = {},
  717. .name_token = name_token,
  718. };
  719. // Process input:
  720. for (;;) {
  721. auto& token = input.next_token();
  722. // <eof-token>
  723. // <)-token>
  724. if (token.is(Token::Type::EndOfFile) || token.is(Token::Type::CloseParen)) {
  725. // Discard a token from input. Return function.
  726. // AD-HOC: Store the token instead as the "end token"
  727. function.end_token = input.consume_a_token();
  728. return function;
  729. }
  730. // anything else
  731. {
  732. // Consume a component value from input and append the result to function’s value.
  733. function.value.append(consume_a_component_value(input));
  734. }
  735. }
  736. }
  737. // https://drafts.csswg.org/css-syntax/#consume-declaration
  738. template<typename T>
  739. Optional<Declaration> Parser::consume_a_declaration(TokenStream<T>& input, Nested nested)
  740. {
  741. // To consume a declaration from a token stream input, given an optional bool nested (default false):
  742. // TODO: As noted in the "Implementation note" below https://drafts.csswg.org/css-syntax/#consume-block-contents
  743. // there are ways we can optimise this by early-exiting.
  744. // Let decl be a new declaration, with an initially empty name and a value set to an empty list.
  745. Declaration declaration {
  746. .name {},
  747. .value {},
  748. };
  749. // 1. If the next token is an <ident-token>, consume a token from input and set decl’s name to the token’s value.
  750. if (input.next_token().is(Token::Type::Ident)) {
  751. declaration.name = ((Token)input.consume_a_token()).ident();
  752. }
  753. // Otherwise, consume the remnants of a bad declaration from input, with nested, and return nothing.
  754. else {
  755. consume_the_remnants_of_a_bad_declaration(input, nested);
  756. return {};
  757. }
  758. // 2. Discard whitespace from input.
  759. input.discard_whitespace();
  760. // 3. If the next token is a <colon-token>, discard a token from input.
  761. if (input.next_token().is(Token::Type::Colon)) {
  762. input.discard_a_token();
  763. }
  764. // Otherwise, consume the remnants of a bad declaration from input, with nested, and return nothing.
  765. else {
  766. consume_the_remnants_of_a_bad_declaration(input, nested);
  767. return {};
  768. }
  769. // 4. Discard whitespace from input.
  770. input.discard_whitespace();
  771. // 5. Consume a list of component values from input, with nested, and with <semicolon-token> as the stop token,
  772. // and set decl’s value to the result.
  773. declaration.value = consume_a_list_of_component_values(input, Token::Type::Semicolon, nested);
  774. // 6. If the last two non-<whitespace-token>s in decl’s value are a <delim-token> with the value "!"
  775. // followed by an <ident-token> with a value that is an ASCII case-insensitive match for "important",
  776. // remove them from decl’s value and set decl’s important flag.
  777. if (declaration.value.size() >= 2) {
  778. // NOTE: Walk backwards from the end until we find "important"
  779. Optional<size_t> important_index;
  780. for (size_t i = declaration.value.size() - 1; i > 0; i--) {
  781. auto const& value = declaration.value[i];
  782. if (value.is_ident("important"sv)) {
  783. important_index = i;
  784. break;
  785. }
  786. if (!value.is(Token::Type::Whitespace))
  787. break;
  788. }
  789. // NOTE: Walk backwards from important until we find "!"
  790. if (important_index.has_value()) {
  791. Optional<size_t> bang_index;
  792. for (size_t i = important_index.value() - 1; i > 0; i--) {
  793. auto const& value = declaration.value[i];
  794. if (value.is_delim('!')) {
  795. bang_index = i;
  796. break;
  797. }
  798. if (value.is(Token::Type::Whitespace))
  799. continue;
  800. break;
  801. }
  802. if (bang_index.has_value()) {
  803. declaration.value.remove(important_index.value());
  804. declaration.value.remove(bang_index.value());
  805. declaration.important = Important::Yes;
  806. }
  807. }
  808. }
  809. // 7. While the last item in decl’s value is a <whitespace-token>, remove that token.
  810. while (!declaration.value.is_empty() && declaration.value.last().is(Token::Type::Whitespace)) {
  811. declaration.value.take_last();
  812. }
  813. // See second clause of step 8.
  814. auto contains_a_curly_block_and_non_whitespace = [](Vector<ComponentValue> const& declaration_value) {
  815. bool contains_curly_block = false;
  816. bool contains_non_whitespace = false;
  817. for (auto const& value : declaration_value) {
  818. if (value.is_block() && value.block().is_curly()) {
  819. if (contains_non_whitespace)
  820. return true;
  821. contains_curly_block = true;
  822. continue;
  823. }
  824. if (!value.is(Token::Type::Whitespace)) {
  825. if (contains_curly_block)
  826. return true;
  827. contains_non_whitespace = true;
  828. continue;
  829. }
  830. }
  831. return false;
  832. };
  833. // 8. If decl’s name is a custom property name string, then set decl’s original text to the segment
  834. // of the original source text string corresponding to the tokens of decl’s value.
  835. if (is_a_custom_property_name_string(declaration.name)) {
  836. // TODO: If we could reach inside the source string that the TokenStream uses, we could grab this as
  837. // a single substring instead of having to reconstruct it.
  838. StringBuilder original_text;
  839. for (auto const& value : declaration.value) {
  840. original_text.append(value.original_source_text());
  841. }
  842. declaration.original_text = original_text.to_string_without_validation();
  843. }
  844. // Otherwise, if decl’s value contains a top-level simple block with an associated token of <{-token>,
  845. // and also contains any other non-<whitespace-token> value, return nothing.
  846. // (That is, a top-level {}-block is only allowed as the entire value of a non-custom property.)
  847. else if (contains_a_curly_block_and_non_whitespace(declaration.value)) {
  848. return {};
  849. }
  850. // Otherwise, if decl’s name is an ASCII case-insensitive match for "unicode-range", consume the value of
  851. // a unicode-range descriptor from the segment of the original source text string corresponding to the
  852. // tokens returned by the consume a list of component values call, and replace decl’s value with the result.
  853. else if (declaration.name.equals_ignoring_ascii_case("unicode-range"sv)) {
  854. // FIXME: Special unicode-range handling
  855. }
  856. // 9. If decl is valid in the current context, return it; otherwise return nothing.
  857. if (is_valid_in_the_current_context(declaration))
  858. return declaration;
  859. return {};
  860. }
  861. // https://drafts.csswg.org/css-syntax/#consume-the-remnants-of-a-bad-declaration
  862. template<typename T>
  863. void Parser::consume_the_remnants_of_a_bad_declaration(TokenStream<T>& input, Nested nested)
  864. {
  865. // To consume the remnants of a bad declaration from a token stream input, given a bool nested:
  866. // Process input:
  867. for (;;) {
  868. auto& token = input.next_token();
  869. // <eof-token>
  870. // <semicolon-token>
  871. if (token.is(Token::Type::EndOfFile) || token.is(Token::Type::Semicolon)) {
  872. // Discard a token from input, and return nothing.
  873. input.discard_a_token();
  874. return;
  875. }
  876. // <}-token>
  877. if (token.is(Token::Type::CloseCurly)) {
  878. // If nested is true, return nothing. Otherwise, discard a token.
  879. if (nested == Nested::Yes)
  880. return;
  881. input.discard_a_token();
  882. continue;
  883. }
  884. // anything else
  885. {
  886. // Consume a component value from input, and do nothing.
  887. (void)consume_a_component_value(input);
  888. continue;
  889. }
  890. }
  891. }
  892. CSSRule* Parser::parse_as_css_rule()
  893. {
  894. if (auto maybe_rule = parse_a_rule(m_token_stream); maybe_rule.has_value())
  895. return convert_to_rule(maybe_rule.value(), Nested::No);
  896. return {};
  897. }
  898. // https://drafts.csswg.org/css-syntax/#parse-rule
  899. template<typename T>
  900. Optional<Rule> Parser::parse_a_rule(TokenStream<T>& input)
  901. {
  902. // To parse a rule from input:
  903. Optional<Rule> rule;
  904. // 1. Normalize input, and set input to the result.
  905. // NOTE: This is done when initializing the Parser.
  906. // 2. Discard whitespace from input.
  907. input.discard_whitespace();
  908. // 3. If the next token from input is an <EOF-token>, return a syntax error.
  909. if (input.next_token().is(Token::Type::EndOfFile)) {
  910. return {};
  911. }
  912. // Otherwise, if the next token from input is an <at-keyword-token>,
  913. // consume an at-rule from input, and let rule be the return value.
  914. else if (input.next_token().is(Token::Type::AtKeyword)) {
  915. rule = consume_an_at_rule(m_token_stream).map([](auto& it) { return Rule { it }; });
  916. }
  917. // Otherwise, consume a qualified rule from input and let rule be the return value.
  918. // If nothing or an invalid rule error was returned, return a syntax error.
  919. else {
  920. consume_a_qualified_rule(input).visit(
  921. [&](QualifiedRule qualified_rule) { rule = move(qualified_rule); },
  922. [](auto&) {});
  923. if (!rule.has_value())
  924. return {};
  925. }
  926. // 4. Discard whitespace from input.
  927. input.discard_whitespace();
  928. // 5. If the next token from input is an <EOF-token>, return rule. Otherwise, return a syntax error.
  929. if (input.next_token().is(Token::Type::EndOfFile))
  930. return rule;
  931. return {};
  932. }
  933. // https://drafts.csswg.org/css-syntax/#parse-block-contents
  934. template<typename T>
  935. Vector<RuleOrListOfDeclarations> Parser::parse_a_blocks_contents(TokenStream<T>& input)
  936. {
  937. // To parse a block’s contents from input:
  938. // 1. Normalize input, and set input to the result.
  939. // NOTE: Done by constructing the Parser.
  940. // 2. Consume a block’s contents from input, and return the result.
  941. return consume_a_blocks_contents(input);
  942. }
  943. Optional<StyleProperty> Parser::parse_as_supports_condition()
  944. {
  945. m_rule_context.append(ContextType::SupportsCondition);
  946. auto maybe_declaration = parse_a_declaration(m_token_stream);
  947. m_rule_context.take_last();
  948. if (maybe_declaration.has_value())
  949. return convert_to_style_property(maybe_declaration.release_value());
  950. return {};
  951. }
  952. // https://drafts.csswg.org/css-syntax/#parse-declaration
  953. template<typename T>
  954. Optional<Declaration> Parser::parse_a_declaration(TokenStream<T>& input)
  955. {
  956. // To parse a declaration from input:
  957. // 1. Normalize input, and set input to the result.
  958. // Note: This is done when initializing the Parser.
  959. // 2. Discard whitespace from input.
  960. input.discard_whitespace();
  961. // 3. Consume a declaration from input. If anything was returned, return it. Otherwise, return a syntax error.
  962. if (auto declaration = consume_a_declaration(input); declaration.has_value())
  963. return declaration.release_value();
  964. // FIXME: Syntax error
  965. return {};
  966. }
  967. Optional<ComponentValue> Parser::parse_as_component_value()
  968. {
  969. return parse_a_component_value(m_token_stream);
  970. }
  971. // https://drafts.csswg.org/css-syntax/#parse-component-value
  972. template<typename T>
  973. Optional<ComponentValue> Parser::parse_a_component_value(TokenStream<T>& input)
  974. {
  975. // To parse a component value from input:
  976. // 1. Normalize input, and set input to the result.
  977. // Note: This is done when initializing the Parser.
  978. // 2. Discard whitespace from input.
  979. input.discard_whitespace();
  980. // 3. If input is empty, return a syntax error.
  981. // FIXME: Syntax error
  982. if (input.is_empty())
  983. return {};
  984. // 4. Consume a component value from input and let value be the return value.
  985. auto value = consume_a_component_value(input);
  986. // 5. Discard whitespace from input.
  987. input.discard_whitespace();
  988. // 6. If input is empty, return value. Otherwise, return a syntax error.
  989. if (input.is_empty())
  990. return move(value);
  991. // FIXME: Syntax error
  992. return {};
  993. }
  994. // https://drafts.csswg.org/css-syntax/#parse-list-of-component-values
  995. template<typename T>
  996. Vector<ComponentValue> Parser::parse_a_list_of_component_values(TokenStream<T>& input)
  997. {
  998. // To parse a list of component values from input:
  999. // 1. Normalize input, and set input to the result.
  1000. // Note: This is done when initializing the Parser.
  1001. // 2. Consume a list of component values from input, and return the result.
  1002. return consume_a_list_of_component_values(input);
  1003. }
  1004. // https://drafts.csswg.org/css-syntax/#parse-comma-separated-list-of-component-values
  1005. template<typename T>
  1006. Vector<Vector<ComponentValue>> Parser::parse_a_comma_separated_list_of_component_values(TokenStream<T>& input)
  1007. {
  1008. // To parse a comma-separated list of component values from input:
  1009. // 1. Normalize input, and set input to the result.
  1010. // Note: This is done when initializing the Parser.
  1011. // 2. Let groups be an empty list.
  1012. Vector<Vector<ComponentValue>> groups;
  1013. // 3. While input is not empty:
  1014. while (!input.is_empty()) {
  1015. // 1. Consume a list of component values from input, with <comma-token> as the stop token, and append the result to groups.
  1016. groups.append(consume_a_list_of_component_values(input, Token::Type::Comma));
  1017. // 2. Discard a token from input.
  1018. input.discard_a_token();
  1019. }
  1020. // 4. Return groups.
  1021. return groups;
  1022. }
  1023. template Vector<Vector<ComponentValue>> Parser::parse_a_comma_separated_list_of_component_values(TokenStream<ComponentValue>&);
  1024. template Vector<Vector<ComponentValue>> Parser::parse_a_comma_separated_list_of_component_values(TokenStream<Token>&);
  1025. ElementInlineCSSStyleDeclaration* Parser::parse_as_style_attribute(DOM::Element& element)
  1026. {
  1027. auto expand_shorthands = [&](Vector<StyleProperty>& properties) -> Vector<StyleProperty> {
  1028. Vector<StyleProperty> expanded_properties;
  1029. for (auto& property : properties) {
  1030. if (property_is_shorthand(property.property_id)) {
  1031. StyleComputer::for_each_property_expanding_shorthands(property.property_id, *property.value, StyleComputer::AllowUnresolved::Yes, [&](PropertyID longhand_property_id, CSSStyleValue const& longhand_value) {
  1032. expanded_properties.append(CSS::StyleProperty {
  1033. .important = property.important,
  1034. .property_id = longhand_property_id,
  1035. .value = longhand_value,
  1036. });
  1037. });
  1038. } else {
  1039. expanded_properties.append(property);
  1040. }
  1041. }
  1042. return expanded_properties;
  1043. };
  1044. m_rule_context.append(ContextType::Style);
  1045. auto declarations_and_at_rules = parse_a_blocks_contents(m_token_stream);
  1046. m_rule_context.take_last();
  1047. auto [properties, custom_properties] = extract_properties(declarations_and_at_rules);
  1048. auto expanded_properties = expand_shorthands(properties);
  1049. return ElementInlineCSSStyleDeclaration::create(element, move(expanded_properties), move(custom_properties));
  1050. }
  1051. Optional<URL::URL> Parser::parse_url_function(TokenStream<ComponentValue>& tokens)
  1052. {
  1053. auto transaction = tokens.begin_transaction();
  1054. auto& component_value = tokens.consume_a_token();
  1055. auto convert_string_to_url = [&](StringView url_string) -> Optional<URL::URL> {
  1056. auto url = m_context.complete_url(url_string);
  1057. if (url.is_valid()) {
  1058. transaction.commit();
  1059. return url;
  1060. }
  1061. return {};
  1062. };
  1063. if (component_value.is(Token::Type::Url)) {
  1064. auto url_string = component_value.token().url();
  1065. return convert_string_to_url(url_string);
  1066. }
  1067. if (component_value.is_function("url"sv)) {
  1068. auto const& function_values = component_value.function().value;
  1069. // FIXME: Handle url-modifiers. https://www.w3.org/TR/css-values-4/#url-modifiers
  1070. for (size_t i = 0; i < function_values.size(); ++i) {
  1071. auto const& value = function_values[i];
  1072. if (value.is(Token::Type::Whitespace))
  1073. continue;
  1074. if (value.is(Token::Type::String)) {
  1075. auto url_string = value.token().string();
  1076. return convert_string_to_url(url_string);
  1077. }
  1078. break;
  1079. }
  1080. }
  1081. return {};
  1082. }
  1083. RefPtr<CSSStyleValue> Parser::parse_url_value(TokenStream<ComponentValue>& tokens)
  1084. {
  1085. auto url = parse_url_function(tokens);
  1086. if (!url.has_value())
  1087. return nullptr;
  1088. return URLStyleValue::create(*url);
  1089. }
  1090. // https://www.w3.org/TR/css-shapes-1/#typedef-shape-radius
  1091. Optional<ShapeRadius> Parser::parse_shape_radius(TokenStream<ComponentValue>& tokens)
  1092. {
  1093. auto transaction = tokens.begin_transaction();
  1094. tokens.discard_whitespace();
  1095. auto maybe_radius = parse_length_percentage(tokens);
  1096. if (maybe_radius.has_value()) {
  1097. // Negative radius is invalid.
  1098. auto radius = maybe_radius.value();
  1099. if ((radius.is_length() && radius.length().raw_value() < 0) || (radius.is_percentage() && radius.percentage().value() < 0))
  1100. return {};
  1101. transaction.commit();
  1102. return radius;
  1103. }
  1104. if (tokens.next_token().is_ident("closest-side"sv)) {
  1105. tokens.discard_a_token();
  1106. transaction.commit();
  1107. return FitSide::ClosestSide;
  1108. }
  1109. if (tokens.next_token().is_ident("farthest-side"sv)) {
  1110. tokens.discard_a_token();
  1111. transaction.commit();
  1112. return FitSide::FarthestSide;
  1113. }
  1114. return {};
  1115. }
  1116. RefPtr<CSSStyleValue> Parser::parse_basic_shape_value(TokenStream<ComponentValue>& tokens)
  1117. {
  1118. auto transaction = tokens.begin_transaction();
  1119. auto& component_value = tokens.consume_a_token();
  1120. if (!component_value.is_function())
  1121. return nullptr;
  1122. auto function_name = component_value.function().name.bytes_as_string_view();
  1123. // FIXME: Implement path(). See: https://www.w3.org/TR/css-shapes-1/#basic-shape-functions
  1124. if (function_name.equals_ignoring_ascii_case("inset"sv)) {
  1125. // inset() = inset( <length-percentage>{1,4} [ round <'border-radius'> ]? )
  1126. // FIXME: Parse the border-radius.
  1127. auto arguments_tokens = TokenStream { component_value.function().value };
  1128. // If less than four <length-percentage> values are provided,
  1129. // the omitted values default in the same way as the margin shorthand:
  1130. // an omitted second or third value defaults to the first, and an omitted fourth value defaults to the second.
  1131. // The four <length-percentage>s define the position of the top, right, bottom, and left edges of a rectangle.
  1132. arguments_tokens.discard_whitespace();
  1133. auto top = parse_length_percentage(arguments_tokens);
  1134. if (!top.has_value())
  1135. return nullptr;
  1136. arguments_tokens.discard_whitespace();
  1137. auto right = parse_length_percentage(arguments_tokens);
  1138. if (!right.has_value())
  1139. right = top;
  1140. arguments_tokens.discard_whitespace();
  1141. auto bottom = parse_length_percentage(arguments_tokens);
  1142. if (!bottom.has_value())
  1143. bottom = top;
  1144. arguments_tokens.discard_whitespace();
  1145. auto left = parse_length_percentage(arguments_tokens);
  1146. if (!left.has_value())
  1147. left = right;
  1148. arguments_tokens.discard_whitespace();
  1149. if (arguments_tokens.has_next_token())
  1150. return nullptr;
  1151. transaction.commit();
  1152. return BasicShapeStyleValue::create(Inset { LengthBox(top.value(), right.value(), bottom.value(), left.value()) });
  1153. }
  1154. if (function_name.equals_ignoring_ascii_case("xywh"sv)) {
  1155. // xywh() = xywh( <length-percentage>{2} <length-percentage [0,∞]>{2} [ round <'border-radius'> ]? )
  1156. // FIXME: Parse the border-radius.
  1157. auto arguments_tokens = TokenStream { component_value.function().value };
  1158. arguments_tokens.discard_whitespace();
  1159. auto x = parse_length_percentage(arguments_tokens);
  1160. if (!x.has_value())
  1161. return nullptr;
  1162. arguments_tokens.discard_whitespace();
  1163. auto y = parse_length_percentage(arguments_tokens);
  1164. if (!y.has_value())
  1165. return nullptr;
  1166. arguments_tokens.discard_whitespace();
  1167. auto width = parse_length_percentage(arguments_tokens);
  1168. if (!width.has_value())
  1169. return nullptr;
  1170. arguments_tokens.discard_whitespace();
  1171. auto height = parse_length_percentage(arguments_tokens);
  1172. if (!height.has_value())
  1173. return nullptr;
  1174. arguments_tokens.discard_whitespace();
  1175. if (arguments_tokens.has_next_token())
  1176. return nullptr;
  1177. // Negative width or height is invalid.
  1178. if ((width->is_length() && width->length().raw_value() < 0)
  1179. || (width->is_percentage() && width->percentage().value() < 0)
  1180. || (height->is_length() && height->length().raw_value() < 0)
  1181. || (height->is_percentage() && height->percentage().value() < 0))
  1182. return nullptr;
  1183. transaction.commit();
  1184. return BasicShapeStyleValue::create(Xywh { x.value(), y.value(), width.value(), height.value() });
  1185. }
  1186. if (function_name.equals_ignoring_ascii_case("rect"sv)) {
  1187. // rect() = rect( [ <length-percentage> | auto ]{4} [ round <'border-radius'> ]? )
  1188. // FIXME: Parse the border-radius.
  1189. auto arguments_tokens = TokenStream { component_value.function().value };
  1190. auto parse_length_percentage_or_auto = [this](TokenStream<ComponentValue>& tokens) -> Optional<LengthPercentage> {
  1191. tokens.discard_whitespace();
  1192. auto value = parse_length_percentage(tokens);
  1193. if (!value.has_value()) {
  1194. if (tokens.consume_a_token().is_ident("auto"sv)) {
  1195. value = Length::make_auto();
  1196. }
  1197. }
  1198. return value;
  1199. };
  1200. auto top = parse_length_percentage_or_auto(arguments_tokens);
  1201. auto right = parse_length_percentage_or_auto(arguments_tokens);
  1202. auto bottom = parse_length_percentage_or_auto(arguments_tokens);
  1203. auto left = parse_length_percentage_or_auto(arguments_tokens);
  1204. if (!top.has_value() || !right.has_value() || !bottom.has_value() || !left.has_value())
  1205. return nullptr;
  1206. arguments_tokens.discard_whitespace();
  1207. if (arguments_tokens.has_next_token())
  1208. return nullptr;
  1209. transaction.commit();
  1210. return BasicShapeStyleValue::create(Rect { LengthBox(top.value(), right.value(), bottom.value(), left.value()) });
  1211. }
  1212. if (function_name.equals_ignoring_ascii_case("circle"sv)) {
  1213. // circle() = circle( <shape-radius>? [ at <position> ]? )
  1214. auto arguments_tokens = TokenStream { component_value.function().value };
  1215. auto radius = parse_shape_radius(arguments_tokens).value_or(FitSide::ClosestSide);
  1216. auto position = PositionStyleValue::create_center();
  1217. arguments_tokens.discard_whitespace();
  1218. if (arguments_tokens.next_token().is_ident("at"sv)) {
  1219. arguments_tokens.discard_a_token();
  1220. arguments_tokens.discard_whitespace();
  1221. auto maybe_position = parse_position_value(arguments_tokens);
  1222. if (maybe_position.is_null())
  1223. return nullptr;
  1224. position = maybe_position.release_nonnull();
  1225. }
  1226. arguments_tokens.discard_whitespace();
  1227. if (arguments_tokens.has_next_token())
  1228. return nullptr;
  1229. transaction.commit();
  1230. return BasicShapeStyleValue::create(Circle { radius, position });
  1231. }
  1232. if (function_name.equals_ignoring_ascii_case("ellipse"sv)) {
  1233. // ellipse() = ellipse( [ <shape-radius>{2} ]? [ at <position> ]? )
  1234. auto arguments_tokens = TokenStream { component_value.function().value };
  1235. Optional<ShapeRadius> radius_x = parse_shape_radius(arguments_tokens);
  1236. Optional<ShapeRadius> radius_y = parse_shape_radius(arguments_tokens);
  1237. if (radius_x.has_value() && !radius_y.has_value())
  1238. return nullptr;
  1239. if (!radius_x.has_value()) {
  1240. radius_x = FitSide::ClosestSide;
  1241. radius_y = FitSide::ClosestSide;
  1242. }
  1243. auto position = PositionStyleValue::create_center();
  1244. arguments_tokens.discard_whitespace();
  1245. if (arguments_tokens.next_token().is_ident("at"sv)) {
  1246. arguments_tokens.discard_a_token();
  1247. arguments_tokens.discard_whitespace();
  1248. auto maybe_position = parse_position_value(arguments_tokens);
  1249. if (maybe_position.is_null())
  1250. return nullptr;
  1251. position = maybe_position.release_nonnull();
  1252. }
  1253. arguments_tokens.discard_whitespace();
  1254. if (arguments_tokens.has_next_token())
  1255. return nullptr;
  1256. transaction.commit();
  1257. return BasicShapeStyleValue::create(Ellipse { radius_x.value(), radius_y.value(), position });
  1258. }
  1259. if (function_name.equals_ignoring_ascii_case("polygon"sv)) {
  1260. // polygon() = polygon( <'fill-rule'>? , [<length-percentage> <length-percentage>]# )
  1261. auto arguments_tokens = TokenStream { component_value.function().value };
  1262. auto arguments = parse_a_comma_separated_list_of_component_values(arguments_tokens);
  1263. if (arguments.size() < 1)
  1264. return nullptr;
  1265. Optional<Gfx::WindingRule> fill_rule;
  1266. auto const& first_argument = arguments[0];
  1267. TokenStream first_argument_tokens { first_argument };
  1268. first_argument_tokens.discard_whitespace();
  1269. if (first_argument_tokens.next_token().is_ident("nonzero"sv)) {
  1270. fill_rule = Gfx::WindingRule::Nonzero;
  1271. } else if (first_argument_tokens.next_token().is_ident("evenodd"sv)) {
  1272. fill_rule = Gfx::WindingRule::EvenOdd;
  1273. }
  1274. if (fill_rule.has_value()) {
  1275. first_argument_tokens.discard_a_token();
  1276. if (first_argument_tokens.has_next_token())
  1277. return nullptr;
  1278. arguments.remove(0);
  1279. } else {
  1280. fill_rule = Gfx::WindingRule::Nonzero;
  1281. }
  1282. if (arguments.size() < 1)
  1283. return nullptr;
  1284. Vector<Polygon::Point> points;
  1285. for (auto& argument : arguments) {
  1286. TokenStream argument_tokens { argument };
  1287. argument_tokens.discard_whitespace();
  1288. auto x_pos = parse_length_percentage(argument_tokens);
  1289. if (!x_pos.has_value())
  1290. return nullptr;
  1291. argument_tokens.discard_whitespace();
  1292. auto y_pos = parse_length_percentage(argument_tokens);
  1293. if (!y_pos.has_value())
  1294. return nullptr;
  1295. argument_tokens.discard_whitespace();
  1296. if (argument_tokens.has_next_token())
  1297. return nullptr;
  1298. points.append(Polygon::Point { *x_pos, *y_pos });
  1299. }
  1300. transaction.commit();
  1301. return BasicShapeStyleValue::create(Polygon { fill_rule.value(), move(points) });
  1302. }
  1303. return nullptr;
  1304. }
  1305. bool Parser::is_valid_in_the_current_context(Declaration const&) const
  1306. {
  1307. // TODO: Determine if this *particular* declaration is valid here, not just declarations in general.
  1308. // Declarations can't appear at the top level
  1309. if (m_rule_context.is_empty())
  1310. return false;
  1311. switch (m_rule_context.last()) {
  1312. case ContextType::Unknown:
  1313. // If the context is an unknown type, we don't accept anything.
  1314. return false;
  1315. case ContextType::Style:
  1316. case ContextType::Keyframe:
  1317. // Style and keyframe rules contain property declarations
  1318. return true;
  1319. case ContextType::AtLayer:
  1320. case ContextType::AtMedia:
  1321. case ContextType::AtSupports:
  1322. // Grouping rules can contain declarations if they are themselves inside a style rule
  1323. return m_rule_context.contains_slow(ContextType::Style);
  1324. case ContextType::AtFontFace:
  1325. case ContextType::AtProperty:
  1326. // @font-face and @property have descriptor declarations
  1327. return true;
  1328. case ContextType::AtKeyframes:
  1329. // @keyframes can only contain keyframe rules
  1330. return false;
  1331. case ContextType::SupportsCondition:
  1332. // @supports conditions accept all declarations
  1333. return true;
  1334. }
  1335. VERIFY_NOT_REACHED();
  1336. }
  1337. bool Parser::is_valid_in_the_current_context(AtRule const& at_rule) const
  1338. {
  1339. // All at-rules can appear at the top level
  1340. if (m_rule_context.is_empty())
  1341. return true;
  1342. switch (m_rule_context.last()) {
  1343. case ContextType::Unknown:
  1344. // If the context is an unknown type, we don't accept anything.
  1345. return false;
  1346. case ContextType::Style:
  1347. // Style rules can contain grouping rules
  1348. return first_is_one_of(at_rule.name, "layer", "media", "supports");
  1349. case ContextType::AtLayer:
  1350. case ContextType::AtMedia:
  1351. case ContextType::AtSupports:
  1352. // Grouping rules can contain anything except @import or @namespace
  1353. return !first_is_one_of(at_rule.name, "import", "namespace");
  1354. case ContextType::SupportsCondition:
  1355. // @supports cannot check for at-rules
  1356. return false;
  1357. case ContextType::AtFontFace:
  1358. case ContextType::AtKeyframes:
  1359. case ContextType::Keyframe:
  1360. case ContextType::AtProperty:
  1361. // These can't contain any at-rules
  1362. return false;
  1363. }
  1364. VERIFY_NOT_REACHED();
  1365. }
  1366. bool Parser::is_valid_in_the_current_context(QualifiedRule const&) const
  1367. {
  1368. // TODO: Different places accept different kinds of qualified rules. How do we tell them apart? Can we?
  1369. // Top level can contain style rules
  1370. if (m_rule_context.is_empty())
  1371. return true;
  1372. switch (m_rule_context.last()) {
  1373. case ContextType::Unknown:
  1374. // If the context is an unknown type, we don't accept anything.
  1375. return false;
  1376. case ContextType::Style:
  1377. // Style rules can contain style rules
  1378. return true;
  1379. case ContextType::AtLayer:
  1380. case ContextType::AtMedia:
  1381. case ContextType::AtSupports:
  1382. // Grouping rules can contain style rules
  1383. return true;
  1384. case ContextType::AtKeyframes:
  1385. // @keyframes can contain keyframe rules
  1386. return true;
  1387. case ContextType::SupportsCondition:
  1388. // @supports cannot check qualified rules
  1389. return false;
  1390. case ContextType::AtFontFace:
  1391. case ContextType::AtProperty:
  1392. case ContextType::Keyframe:
  1393. // These can't contain qualified rules
  1394. return false;
  1395. }
  1396. VERIFY_NOT_REACHED();
  1397. }
  1398. Parser::PropertiesAndCustomProperties Parser::extract_properties(Vector<RuleOrListOfDeclarations> const& rules_and_lists_of_declarations)
  1399. {
  1400. PropertiesAndCustomProperties result;
  1401. for (auto const& rule_or_list : rules_and_lists_of_declarations) {
  1402. if (rule_or_list.has<Rule>())
  1403. continue;
  1404. auto& declarations = rule_or_list.get<Vector<Declaration>>();
  1405. PropertiesAndCustomProperties& dest = result;
  1406. for (auto const& declaration : declarations) {
  1407. extract_property(declaration, dest);
  1408. }
  1409. }
  1410. return result;
  1411. }
  1412. void Parser::extract_property(Declaration const& declaration, PropertiesAndCustomProperties& dest)
  1413. {
  1414. if (auto maybe_property = convert_to_style_property(declaration); maybe_property.has_value()) {
  1415. auto property = maybe_property.release_value();
  1416. if (property.property_id == PropertyID::Custom) {
  1417. dest.custom_properties.set(property.custom_name, property);
  1418. } else {
  1419. dest.properties.append(move(property));
  1420. }
  1421. }
  1422. }
  1423. PropertyOwningCSSStyleDeclaration* Parser::convert_to_style_declaration(Vector<Declaration> const& declarations)
  1424. {
  1425. PropertiesAndCustomProperties properties;
  1426. PropertiesAndCustomProperties& dest = properties;
  1427. for (auto const& declaration : declarations) {
  1428. extract_property(declaration, dest);
  1429. }
  1430. return PropertyOwningCSSStyleDeclaration::create(m_context.realm(), move(properties.properties), move(properties.custom_properties));
  1431. }
  1432. Optional<StyleProperty> Parser::convert_to_style_property(Declaration const& declaration)
  1433. {
  1434. auto const& property_name = declaration.name;
  1435. auto property_id = property_id_from_string(property_name);
  1436. if (!property_id.has_value()) {
  1437. if (property_name.bytes_as_string_view().starts_with("--"sv)) {
  1438. property_id = PropertyID::Custom;
  1439. } else if (has_ignored_vendor_prefix(property_name)) {
  1440. return {};
  1441. } else if (!property_name.bytes_as_string_view().starts_with('-')) {
  1442. dbgln_if(CSS_PARSER_DEBUG, "Unrecognized CSS property '{}'", property_name);
  1443. return {};
  1444. }
  1445. }
  1446. auto value_token_stream = TokenStream(declaration.value);
  1447. auto value = parse_css_value(property_id.value(), value_token_stream, declaration.original_text);
  1448. if (value.is_error()) {
  1449. if (value.error() == ParseError::SyntaxError) {
  1450. dbgln_if(CSS_PARSER_DEBUG, "Unable to parse value for CSS property '{}'.", property_name);
  1451. if constexpr (CSS_PARSER_DEBUG) {
  1452. value_token_stream.dump_all_tokens();
  1453. }
  1454. }
  1455. return {};
  1456. }
  1457. if (property_id.value() == PropertyID::Custom)
  1458. return StyleProperty { declaration.important, property_id.value(), value.release_value(), declaration.name };
  1459. return StyleProperty { declaration.important, property_id.value(), value.release_value(), {} };
  1460. }
  1461. RefPtr<CSSStyleValue> Parser::parse_builtin_value(TokenStream<ComponentValue>& tokens)
  1462. {
  1463. auto transaction = tokens.begin_transaction();
  1464. auto& component_value = tokens.consume_a_token();
  1465. if (component_value.is(Token::Type::Ident)) {
  1466. auto ident = component_value.token().ident();
  1467. if (ident.equals_ignoring_ascii_case("inherit"sv)) {
  1468. transaction.commit();
  1469. return CSSKeywordValue::create(Keyword::Inherit);
  1470. }
  1471. if (ident.equals_ignoring_ascii_case("initial"sv)) {
  1472. transaction.commit();
  1473. return CSSKeywordValue::create(Keyword::Initial);
  1474. }
  1475. if (ident.equals_ignoring_ascii_case("unset"sv)) {
  1476. transaction.commit();
  1477. return CSSKeywordValue::create(Keyword::Unset);
  1478. }
  1479. if (ident.equals_ignoring_ascii_case("revert"sv)) {
  1480. transaction.commit();
  1481. return CSSKeywordValue::create(Keyword::Revert);
  1482. }
  1483. if (ident.equals_ignoring_ascii_case("revert-layer"sv)) {
  1484. transaction.commit();
  1485. return CSSKeywordValue::create(Keyword::RevertLayer);
  1486. }
  1487. }
  1488. return nullptr;
  1489. }
  1490. // https://www.w3.org/TR/css-values-4/#custom-idents
  1491. RefPtr<CustomIdentStyleValue> Parser::parse_custom_ident_value(TokenStream<ComponentValue>& tokens, std::initializer_list<StringView> blacklist)
  1492. {
  1493. auto transaction = tokens.begin_transaction();
  1494. tokens.discard_whitespace();
  1495. auto const& token = tokens.consume_a_token();
  1496. if (!token.is(Token::Type::Ident))
  1497. return nullptr;
  1498. auto custom_ident = token.token().ident();
  1499. // The CSS-wide keywords are not valid <custom-ident>s.
  1500. if (is_css_wide_keyword(custom_ident))
  1501. return nullptr;
  1502. // The default keyword is reserved and is also not a valid <custom-ident>.
  1503. if (custom_ident.equals_ignoring_ascii_case("default"sv))
  1504. return nullptr;
  1505. // Specifications using <custom-ident> must specify clearly what other keywords are excluded from <custom-ident>,
  1506. // if any—for example by saying that any pre-defined keywords in that property’s value definition are excluded.
  1507. // Excluded keywords are excluded in all ASCII case permutations.
  1508. for (auto& value : blacklist) {
  1509. if (custom_ident.equals_ignoring_ascii_case(value))
  1510. return nullptr;
  1511. }
  1512. transaction.commit();
  1513. return CustomIdentStyleValue::create(custom_ident);
  1514. }
  1515. RefPtr<CSSMathValue> Parser::parse_calculated_value(ComponentValue const& component_value)
  1516. {
  1517. if (!component_value.is_function())
  1518. return nullptr;
  1519. auto const& function = component_value.function();
  1520. auto function_node = parse_a_calc_function_node(function);
  1521. if (!function_node)
  1522. return nullptr;
  1523. auto function_type = function_node->determine_type(m_context.current_property_id());
  1524. if (!function_type.has_value())
  1525. return nullptr;
  1526. return CSSMathValue::create(function_node.release_nonnull(), function_type.release_value());
  1527. }
  1528. OwnPtr<CalculationNode> Parser::parse_a_calc_function_node(Function const& function)
  1529. {
  1530. if (function.name.equals_ignoring_ascii_case("calc"sv))
  1531. return parse_a_calculation(function.value);
  1532. if (auto maybe_function = parse_math_function(m_context.current_property_id(), function))
  1533. return maybe_function;
  1534. return nullptr;
  1535. }
  1536. Optional<Dimension> Parser::parse_dimension(ComponentValue const& component_value)
  1537. {
  1538. if (component_value.is(Token::Type::Dimension)) {
  1539. auto numeric_value = component_value.token().dimension_value();
  1540. auto unit_string = component_value.token().dimension_unit();
  1541. if (auto length_type = Length::unit_from_name(unit_string); length_type.has_value())
  1542. return Length { numeric_value, length_type.release_value() };
  1543. if (auto angle_type = Angle::unit_from_name(unit_string); angle_type.has_value())
  1544. return Angle { numeric_value, angle_type.release_value() };
  1545. if (auto flex_type = Flex::unit_from_name(unit_string); flex_type.has_value())
  1546. return Flex { numeric_value, flex_type.release_value() };
  1547. if (auto frequency_type = Frequency::unit_from_name(unit_string); frequency_type.has_value())
  1548. return Frequency { numeric_value, frequency_type.release_value() };
  1549. if (auto resolution_type = Resolution::unit_from_name(unit_string); resolution_type.has_value())
  1550. return Resolution { numeric_value, resolution_type.release_value() };
  1551. if (auto time_type = Time::unit_from_name(unit_string); time_type.has_value())
  1552. return Time { numeric_value, time_type.release_value() };
  1553. }
  1554. if (component_value.is(Token::Type::Percentage))
  1555. return Percentage { component_value.token().percentage() };
  1556. if (component_value.is(Token::Type::Number)) {
  1557. auto numeric_value = component_value.token().number_value();
  1558. if (numeric_value == 0)
  1559. return Length::make_px(0);
  1560. if (m_context.in_quirks_mode() && property_has_quirk(m_context.current_property_id(), Quirk::UnitlessLength)) {
  1561. // https://quirks.spec.whatwg.org/#quirky-length-value
  1562. // FIXME: Disallow quirk when inside a CSS sub-expression (like `calc()`)
  1563. // "The <quirky-length> value must not be supported in arguments to CSS expressions other than the rect()
  1564. // expression, and must not be supported in the supports() static method of the CSS interface."
  1565. return Length::make_px(CSSPixels::nearest_value_for(numeric_value));
  1566. }
  1567. }
  1568. return {};
  1569. }
  1570. Optional<AngleOrCalculated> Parser::parse_angle(TokenStream<ComponentValue>& tokens)
  1571. {
  1572. auto transaction = tokens.begin_transaction();
  1573. auto& token = tokens.consume_a_token();
  1574. if (auto dimension = parse_dimension(token); dimension.has_value()) {
  1575. if (dimension->is_angle()) {
  1576. transaction.commit();
  1577. return dimension->angle();
  1578. }
  1579. return {};
  1580. }
  1581. if (auto calc = parse_calculated_value(token); calc && calc->resolves_to_angle()) {
  1582. transaction.commit();
  1583. return calc.release_nonnull();
  1584. }
  1585. return {};
  1586. }
  1587. Optional<AnglePercentage> Parser::parse_angle_percentage(TokenStream<ComponentValue>& tokens)
  1588. {
  1589. auto transaction = tokens.begin_transaction();
  1590. auto& token = tokens.consume_a_token();
  1591. if (auto dimension = parse_dimension(token); dimension.has_value()) {
  1592. if (dimension->is_angle_percentage()) {
  1593. transaction.commit();
  1594. return dimension->angle_percentage();
  1595. }
  1596. return {};
  1597. }
  1598. if (auto calc = parse_calculated_value(token); calc && calc->resolves_to_angle_percentage()) {
  1599. transaction.commit();
  1600. return calc.release_nonnull();
  1601. }
  1602. return {};
  1603. }
  1604. Optional<FlexOrCalculated> Parser::parse_flex(TokenStream<ComponentValue>& tokens)
  1605. {
  1606. auto transaction = tokens.begin_transaction();
  1607. auto& token = tokens.consume_a_token();
  1608. if (auto dimension = parse_dimension(token); dimension.has_value()) {
  1609. if (dimension->is_flex()) {
  1610. transaction.commit();
  1611. return dimension->flex();
  1612. }
  1613. return {};
  1614. }
  1615. if (auto calc = parse_calculated_value(token); calc && calc->resolves_to_flex()) {
  1616. transaction.commit();
  1617. return calc.release_nonnull();
  1618. }
  1619. return {};
  1620. }
  1621. Optional<FrequencyOrCalculated> Parser::parse_frequency(TokenStream<ComponentValue>& tokens)
  1622. {
  1623. auto transaction = tokens.begin_transaction();
  1624. auto& token = tokens.consume_a_token();
  1625. if (auto dimension = parse_dimension(token); dimension.has_value()) {
  1626. if (dimension->is_frequency()) {
  1627. transaction.commit();
  1628. return dimension->frequency();
  1629. }
  1630. return {};
  1631. }
  1632. if (auto calc = parse_calculated_value(token); calc && calc->resolves_to_frequency()) {
  1633. transaction.commit();
  1634. return calc.release_nonnull();
  1635. }
  1636. return {};
  1637. }
  1638. Optional<FrequencyPercentage> Parser::parse_frequency_percentage(TokenStream<ComponentValue>& tokens)
  1639. {
  1640. auto transaction = tokens.begin_transaction();
  1641. auto& token = tokens.consume_a_token();
  1642. if (auto dimension = parse_dimension(token); dimension.has_value()) {
  1643. if (dimension->is_frequency_percentage()) {
  1644. transaction.commit();
  1645. return dimension->frequency_percentage();
  1646. }
  1647. return {};
  1648. }
  1649. if (auto calc = parse_calculated_value(token); calc && calc->resolves_to_frequency_percentage()) {
  1650. transaction.commit();
  1651. return calc.release_nonnull();
  1652. }
  1653. return {};
  1654. }
  1655. Optional<IntegerOrCalculated> Parser::parse_integer(TokenStream<ComponentValue>& tokens)
  1656. {
  1657. auto transaction = tokens.begin_transaction();
  1658. auto& token = tokens.consume_a_token();
  1659. if (token.is(Token::Type::Number) && token.token().number().is_integer()) {
  1660. transaction.commit();
  1661. return token.token().to_integer();
  1662. }
  1663. if (auto calc = parse_calculated_value(token); calc && calc->resolves_to_number()) {
  1664. transaction.commit();
  1665. return calc.release_nonnull();
  1666. }
  1667. return {};
  1668. }
  1669. Optional<LengthOrCalculated> Parser::parse_length(TokenStream<ComponentValue>& tokens)
  1670. {
  1671. auto transaction = tokens.begin_transaction();
  1672. auto& token = tokens.consume_a_token();
  1673. if (auto dimension = parse_dimension(token); dimension.has_value()) {
  1674. if (dimension->is_length()) {
  1675. transaction.commit();
  1676. return dimension->length();
  1677. }
  1678. return {};
  1679. }
  1680. if (auto calc = parse_calculated_value(token); calc && calc->resolves_to_length()) {
  1681. transaction.commit();
  1682. return calc.release_nonnull();
  1683. }
  1684. return {};
  1685. }
  1686. Optional<LengthPercentage> Parser::parse_length_percentage(TokenStream<ComponentValue>& tokens)
  1687. {
  1688. auto transaction = tokens.begin_transaction();
  1689. auto& token = tokens.consume_a_token();
  1690. if (auto dimension = parse_dimension(token); dimension.has_value()) {
  1691. if (dimension->is_length_percentage()) {
  1692. transaction.commit();
  1693. return dimension->length_percentage();
  1694. }
  1695. return {};
  1696. }
  1697. if (auto calc = parse_calculated_value(token); calc && calc->resolves_to_length_percentage()) {
  1698. transaction.commit();
  1699. return calc.release_nonnull();
  1700. }
  1701. return {};
  1702. }
  1703. Optional<NumberOrCalculated> Parser::parse_number(TokenStream<ComponentValue>& tokens)
  1704. {
  1705. auto transaction = tokens.begin_transaction();
  1706. auto& token = tokens.consume_a_token();
  1707. if (token.is(Token::Type::Number)) {
  1708. transaction.commit();
  1709. return token.token().number_value();
  1710. }
  1711. if (auto calc = parse_calculated_value(token); calc && calc->resolves_to_number()) {
  1712. transaction.commit();
  1713. return calc.release_nonnull();
  1714. }
  1715. return {};
  1716. }
  1717. Optional<NumberPercentage> Parser::parse_number_percentage(TokenStream<ComponentValue>& tokens)
  1718. {
  1719. auto transaction = tokens.begin_transaction();
  1720. auto& token = tokens.consume_a_token();
  1721. if (token.is(Token::Type::Number)) {
  1722. transaction.commit();
  1723. return token.token().number();
  1724. }
  1725. if (token.is(Token::Type::Percentage)) {
  1726. transaction.commit();
  1727. return Percentage(token.token().percentage());
  1728. }
  1729. if (auto calc = parse_calculated_value(token); calc && calc->resolves_to_number_percentage()) {
  1730. transaction.commit();
  1731. return calc.release_nonnull();
  1732. }
  1733. return {};
  1734. }
  1735. Optional<ResolutionOrCalculated> Parser::parse_resolution(TokenStream<ComponentValue>& tokens)
  1736. {
  1737. auto transaction = tokens.begin_transaction();
  1738. auto& token = tokens.consume_a_token();
  1739. if (auto dimension = parse_dimension(token); dimension.has_value()) {
  1740. if (dimension->is_resolution()) {
  1741. transaction.commit();
  1742. return dimension->resolution();
  1743. }
  1744. return {};
  1745. }
  1746. if (auto calc = parse_calculated_value(token); calc && calc->resolves_to_resolution()) {
  1747. transaction.commit();
  1748. return calc.release_nonnull();
  1749. }
  1750. return {};
  1751. }
  1752. Optional<TimeOrCalculated> Parser::parse_time(TokenStream<ComponentValue>& tokens)
  1753. {
  1754. auto transaction = tokens.begin_transaction();
  1755. auto& token = tokens.consume_a_token();
  1756. if (auto dimension = parse_dimension(token); dimension.has_value()) {
  1757. if (dimension->is_time()) {
  1758. transaction.commit();
  1759. return dimension->time();
  1760. }
  1761. return {};
  1762. }
  1763. if (auto calc = parse_calculated_value(token); calc && calc->resolves_to_time()) {
  1764. transaction.commit();
  1765. return calc.release_nonnull();
  1766. }
  1767. return {};
  1768. }
  1769. Optional<TimePercentage> Parser::parse_time_percentage(TokenStream<ComponentValue>& tokens)
  1770. {
  1771. auto transaction = tokens.begin_transaction();
  1772. auto& token = tokens.consume_a_token();
  1773. if (auto dimension = parse_dimension(token); dimension.has_value()) {
  1774. if (dimension->is_time_percentage()) {
  1775. transaction.commit();
  1776. return dimension->time_percentage();
  1777. }
  1778. return {};
  1779. }
  1780. if (auto calc = parse_calculated_value(token); calc && calc->resolves_to_time_percentage()) {
  1781. transaction.commit();
  1782. return calc.release_nonnull();
  1783. }
  1784. return {};
  1785. }
  1786. Optional<LengthOrCalculated> Parser::parse_source_size_value(TokenStream<ComponentValue>& tokens)
  1787. {
  1788. if (tokens.next_token().is_ident("auto"sv)) {
  1789. tokens.discard_a_token(); // auto
  1790. return LengthOrCalculated { Length::make_auto() };
  1791. }
  1792. return parse_length(tokens);
  1793. }
  1794. Optional<Ratio> Parser::parse_ratio(TokenStream<ComponentValue>& tokens)
  1795. {
  1796. auto transaction = tokens.begin_transaction();
  1797. tokens.discard_whitespace();
  1798. auto read_number_value = [this](ComponentValue const& component_value) -> Optional<double> {
  1799. if (component_value.is(Token::Type::Number)) {
  1800. return component_value.token().number_value();
  1801. } else if (component_value.is_function()) {
  1802. auto maybe_calc = parse_calculated_value(component_value);
  1803. if (!maybe_calc || !maybe_calc->resolves_to_number())
  1804. return {};
  1805. if (auto resolved_number = maybe_calc->resolve_number(); resolved_number.has_value() && resolved_number.value() >= 0) {
  1806. return resolved_number.value();
  1807. }
  1808. }
  1809. return {};
  1810. };
  1811. // `<ratio> = <number [0,∞]> [ / <number [0,∞]> ]?`
  1812. auto maybe_numerator = read_number_value(tokens.consume_a_token());
  1813. if (!maybe_numerator.has_value() || maybe_numerator.value() < 0)
  1814. return {};
  1815. auto numerator = maybe_numerator.value();
  1816. {
  1817. auto two_value_transaction = tokens.begin_transaction();
  1818. tokens.discard_whitespace();
  1819. auto const& solidus = tokens.consume_a_token();
  1820. tokens.discard_whitespace();
  1821. auto maybe_denominator = read_number_value(tokens.consume_a_token());
  1822. if (solidus.is_delim('/') && maybe_denominator.has_value() && maybe_denominator.value() >= 0) {
  1823. auto denominator = maybe_denominator.value();
  1824. // Two-value ratio
  1825. two_value_transaction.commit();
  1826. transaction.commit();
  1827. return Ratio { numerator, denominator };
  1828. }
  1829. }
  1830. // Single-value ratio
  1831. transaction.commit();
  1832. return Ratio { numerator };
  1833. }
  1834. // https://www.w3.org/TR/css-syntax-3/#urange-syntax
  1835. Optional<Gfx::UnicodeRange> Parser::parse_unicode_range(TokenStream<ComponentValue>& tokens)
  1836. {
  1837. auto transaction = tokens.begin_transaction();
  1838. tokens.discard_whitespace();
  1839. // <urange> =
  1840. // u '+' <ident-token> '?'* |
  1841. // u <dimension-token> '?'* |
  1842. // u <number-token> '?'* |
  1843. // u <number-token> <dimension-token> |
  1844. // u <number-token> <number-token> |
  1845. // u '+' '?'+
  1846. // (All with no whitespace in between tokens.)
  1847. // NOTE: Parsing this is different from usual. We take these steps:
  1848. // 1. Match the grammar above against the tokens, concatenating them into a string using their original representation.
  1849. // 2. Then, parse that string according to the spec algorithm.
  1850. // Step 2 is performed by calling the other parse_unicode_range() overload.
  1851. auto is_ending_token = [](ComponentValue const& component_value) {
  1852. return component_value.is(Token::Type::EndOfFile)
  1853. || component_value.is(Token::Type::Comma)
  1854. || component_value.is(Token::Type::Semicolon)
  1855. || component_value.is(Token::Type::Whitespace);
  1856. };
  1857. auto create_unicode_range = [&](StringView text, auto& local_transaction) -> Optional<Gfx::UnicodeRange> {
  1858. auto maybe_unicode_range = parse_unicode_range(text);
  1859. if (maybe_unicode_range.has_value()) {
  1860. local_transaction.commit();
  1861. transaction.commit();
  1862. }
  1863. return maybe_unicode_range;
  1864. };
  1865. // All options start with 'u'/'U'.
  1866. auto const& u = tokens.consume_a_token();
  1867. if (!u.is_ident("u"sv)) {
  1868. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: <urange> does not start with 'u'");
  1869. return {};
  1870. }
  1871. auto const& second_token = tokens.consume_a_token();
  1872. // u '+' <ident-token> '?'* |
  1873. // u '+' '?'+
  1874. if (second_token.is_delim('+')) {
  1875. auto local_transaction = tokens.begin_transaction();
  1876. StringBuilder string_builder;
  1877. string_builder.append(second_token.token().original_source_text());
  1878. auto const& third_token = tokens.consume_a_token();
  1879. if (third_token.is(Token::Type::Ident) || third_token.is_delim('?')) {
  1880. string_builder.append(third_token.token().original_source_text());
  1881. while (tokens.next_token().is_delim('?'))
  1882. string_builder.append(tokens.consume_a_token().token().original_source_text());
  1883. if (is_ending_token(tokens.next_token()))
  1884. return create_unicode_range(string_builder.string_view(), local_transaction);
  1885. }
  1886. }
  1887. // u <dimension-token> '?'*
  1888. if (second_token.is(Token::Type::Dimension)) {
  1889. auto local_transaction = tokens.begin_transaction();
  1890. StringBuilder string_builder;
  1891. string_builder.append(second_token.token().original_source_text());
  1892. while (tokens.next_token().is_delim('?'))
  1893. string_builder.append(tokens.consume_a_token().token().original_source_text());
  1894. if (is_ending_token(tokens.next_token()))
  1895. return create_unicode_range(string_builder.string_view(), local_transaction);
  1896. }
  1897. // u <number-token> '?'* |
  1898. // u <number-token> <dimension-token> |
  1899. // u <number-token> <number-token>
  1900. if (second_token.is(Token::Type::Number)) {
  1901. auto local_transaction = tokens.begin_transaction();
  1902. StringBuilder string_builder;
  1903. string_builder.append(second_token.token().original_source_text());
  1904. if (is_ending_token(tokens.next_token()))
  1905. return create_unicode_range(string_builder.string_view(), local_transaction);
  1906. auto const& third_token = tokens.consume_a_token();
  1907. if (third_token.is_delim('?')) {
  1908. string_builder.append(third_token.token().original_source_text());
  1909. while (tokens.next_token().is_delim('?'))
  1910. string_builder.append(tokens.consume_a_token().token().original_source_text());
  1911. if (is_ending_token(tokens.next_token()))
  1912. return create_unicode_range(string_builder.string_view(), local_transaction);
  1913. } else if (third_token.is(Token::Type::Dimension)) {
  1914. string_builder.append(third_token.token().original_source_text());
  1915. if (is_ending_token(tokens.next_token()))
  1916. return create_unicode_range(string_builder.string_view(), local_transaction);
  1917. } else if (third_token.is(Token::Type::Number)) {
  1918. string_builder.append(third_token.token().original_source_text());
  1919. if (is_ending_token(tokens.next_token()))
  1920. return create_unicode_range(string_builder.string_view(), local_transaction);
  1921. }
  1922. }
  1923. if constexpr (CSS_PARSER_DEBUG) {
  1924. dbgln("CSSParser: Tokens did not match <urange> grammar.");
  1925. tokens.dump_all_tokens();
  1926. }
  1927. return {};
  1928. }
  1929. Optional<Gfx::UnicodeRange> Parser::parse_unicode_range(StringView text)
  1930. {
  1931. auto make_valid_unicode_range = [&](u32 start_value, u32 end_value) -> Optional<Gfx::UnicodeRange> {
  1932. // https://www.w3.org/TR/css-syntax-3/#maximum-allowed-code-point
  1933. constexpr u32 maximum_allowed_code_point = 0x10FFFF;
  1934. // To determine what codepoints the <urange> represents:
  1935. // 1. If end value is greater than the maximum allowed code point,
  1936. // the <urange> is invalid and a syntax error.
  1937. if (end_value > maximum_allowed_code_point) {
  1938. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Invalid <urange>: end_value ({}) > maximum ({})", end_value, maximum_allowed_code_point);
  1939. return {};
  1940. }
  1941. // 2. If start value is greater than end value, the <urange> is invalid and a syntax error.
  1942. if (start_value > end_value) {
  1943. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Invalid <urange>: start_value ({}) > end_value ({})", start_value, end_value);
  1944. return {};
  1945. }
  1946. // 3. Otherwise, the <urange> represents a contiguous range of codepoints from start value to end value, inclusive.
  1947. return Gfx::UnicodeRange { start_value, end_value };
  1948. };
  1949. // 1. Skipping the first u token, concatenate the representations of all the tokens in the production together.
  1950. // Let this be text.
  1951. // NOTE: The concatenation is already done by the caller.
  1952. GenericLexer lexer { text };
  1953. // 2. If the first character of text is U+002B PLUS SIGN, consume it.
  1954. // Otherwise, this is an invalid <urange>, and this algorithm must exit.
  1955. if (lexer.next_is('+')) {
  1956. lexer.consume();
  1957. } else {
  1958. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Second character of <urange> was not '+'; got: '{}'", lexer.consume());
  1959. return {};
  1960. }
  1961. // 3. Consume as many hex digits from text as possible.
  1962. // then consume as many U+003F QUESTION MARK (?) code points as possible.
  1963. auto start_position = lexer.tell();
  1964. auto hex_digits = lexer.consume_while(is_ascii_hex_digit);
  1965. auto question_marks = lexer.consume_while([](auto it) { return it == '?'; });
  1966. // If zero code points were consumed, or more than six code points were consumed,
  1967. // this is an invalid <urange>, and this algorithm must exit.
  1968. size_t consumed_code_points = hex_digits.length() + question_marks.length();
  1969. if (consumed_code_points == 0 || consumed_code_points > 6) {
  1970. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: <urange> start value had {} digits/?s, expected between 1 and 6.", consumed_code_points);
  1971. return {};
  1972. }
  1973. StringView start_value_code_points = text.substring_view(start_position, consumed_code_points);
  1974. // If any U+003F QUESTION MARK (?) code points were consumed, then:
  1975. if (question_marks.length() > 0) {
  1976. // 1. If there are any code points left in text, this is an invalid <urange>,
  1977. // and this algorithm must exit.
  1978. if (lexer.tell_remaining() != 0) {
  1979. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: <urange> invalid; had {} code points left over.", lexer.tell_remaining());
  1980. return {};
  1981. }
  1982. // 2. Interpret the consumed code points as a hexadecimal number,
  1983. // with the U+003F QUESTION MARK (?) code points replaced by U+0030 DIGIT ZERO (0) code points.
  1984. // This is the start value.
  1985. auto start_value_string = start_value_code_points.replace("?"sv, "0"sv, ReplaceMode::All);
  1986. auto maybe_start_value = AK::StringUtils::convert_to_uint_from_hex<u32>(start_value_string);
  1987. if (!maybe_start_value.has_value()) {
  1988. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: <urange> ?-converted start value did not parse as hex number.");
  1989. return {};
  1990. }
  1991. u32 start_value = maybe_start_value.release_value();
  1992. // 3. Interpret the consumed code points as a hexadecimal number again,
  1993. // with the U+003F QUESTION MARK (?) code points replaced by U+0046 LATIN CAPITAL LETTER F (F) code points.
  1994. // This is the end value.
  1995. auto end_value_string = start_value_code_points.replace("?"sv, "F"sv, ReplaceMode::All);
  1996. auto maybe_end_value = AK::StringUtils::convert_to_uint_from_hex<u32>(end_value_string);
  1997. if (!maybe_end_value.has_value()) {
  1998. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: <urange> ?-converted end value did not parse as hex number.");
  1999. return {};
  2000. }
  2001. u32 end_value = maybe_end_value.release_value();
  2002. // 4. Exit this algorithm.
  2003. return make_valid_unicode_range(start_value, end_value);
  2004. }
  2005. // Otherwise, interpret the consumed code points as a hexadecimal number. This is the start value.
  2006. auto maybe_start_value = AK::StringUtils::convert_to_uint_from_hex<u32>(start_value_code_points);
  2007. if (!maybe_start_value.has_value()) {
  2008. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: <urange> start value did not parse as hex number.");
  2009. return {};
  2010. }
  2011. u32 start_value = maybe_start_value.release_value();
  2012. // 4. If there are no code points left in text, The end value is the same as the start value.
  2013. // Exit this algorithm.
  2014. if (lexer.tell_remaining() == 0)
  2015. return make_valid_unicode_range(start_value, start_value);
  2016. // 5. If the next code point in text is U+002D HYPHEN-MINUS (-), consume it.
  2017. if (lexer.next_is('-')) {
  2018. lexer.consume();
  2019. }
  2020. // Otherwise, this is an invalid <urange>, and this algorithm must exit.
  2021. else {
  2022. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: <urange> start and end values not separated by '-'.");
  2023. return {};
  2024. }
  2025. // 6. Consume as many hex digits as possible from text.
  2026. auto end_hex_digits = lexer.consume_while(is_ascii_hex_digit);
  2027. // If zero hex digits were consumed, or more than 6 hex digits were consumed,
  2028. // this is an invalid <urange>, and this algorithm must exit.
  2029. if (end_hex_digits.length() == 0 || end_hex_digits.length() > 6) {
  2030. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: <urange> end value had {} digits, expected between 1 and 6.", end_hex_digits.length());
  2031. return {};
  2032. }
  2033. // If there are any code points left in text, this is an invalid <urange>, and this algorithm must exit.
  2034. if (lexer.tell_remaining() != 0) {
  2035. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: <urange> invalid; had {} code points left over.", lexer.tell_remaining());
  2036. return {};
  2037. }
  2038. // 7. Interpret the consumed code points as a hexadecimal number. This is the end value.
  2039. auto maybe_end_value = AK::StringUtils::convert_to_uint_from_hex<u32>(end_hex_digits);
  2040. if (!maybe_end_value.has_value()) {
  2041. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: <urange> end value did not parse as hex number.");
  2042. return {};
  2043. }
  2044. u32 end_value = maybe_end_value.release_value();
  2045. return make_valid_unicode_range(start_value, end_value);
  2046. }
  2047. Vector<Gfx::UnicodeRange> Parser::parse_unicode_ranges(TokenStream<ComponentValue>& tokens)
  2048. {
  2049. Vector<Gfx::UnicodeRange> unicode_ranges;
  2050. auto range_token_lists = parse_a_comma_separated_list_of_component_values(tokens);
  2051. for (auto& range_tokens : range_token_lists) {
  2052. TokenStream range_token_stream { range_tokens };
  2053. auto maybe_unicode_range = parse_unicode_range(range_token_stream);
  2054. if (!maybe_unicode_range.has_value()) {
  2055. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: unicode-range format invalid; discarding.");
  2056. return {};
  2057. }
  2058. unicode_ranges.append(maybe_unicode_range.release_value());
  2059. }
  2060. return unicode_ranges;
  2061. }
  2062. RefPtr<CSSStyleValue> Parser::parse_dimension_value(TokenStream<ComponentValue>& tokens)
  2063. {
  2064. if (auto dimension = parse_dimension(tokens.next_token()); dimension.has_value()) {
  2065. tokens.discard_a_token(); // dimension
  2066. if (dimension->is_angle())
  2067. return AngleStyleValue::create(dimension->angle());
  2068. if (dimension->is_frequency())
  2069. return FrequencyStyleValue::create(dimension->frequency());
  2070. if (dimension->is_length())
  2071. return LengthStyleValue::create(dimension->length());
  2072. if (dimension->is_percentage())
  2073. return PercentageStyleValue::create(dimension->percentage());
  2074. if (dimension->is_resolution())
  2075. return ResolutionStyleValue::create(dimension->resolution());
  2076. if (dimension->is_time())
  2077. return TimeStyleValue::create(dimension->time());
  2078. VERIFY_NOT_REACHED();
  2079. }
  2080. if (auto calc = parse_calculated_value(tokens.next_token()); calc && calc->resolves_to_dimension()) {
  2081. tokens.discard_a_token(); // calc
  2082. return calc;
  2083. }
  2084. return nullptr;
  2085. }
  2086. RefPtr<CSSStyleValue> Parser::parse_integer_value(TokenStream<ComponentValue>& tokens)
  2087. {
  2088. auto const& peek_token = tokens.next_token();
  2089. if (peek_token.is(Token::Type::Number) && peek_token.token().number().is_integer()) {
  2090. tokens.discard_a_token(); // integer
  2091. return IntegerStyleValue::create(peek_token.token().number().integer_value());
  2092. }
  2093. if (auto calc = parse_calculated_value(peek_token); calc && calc->resolves_to_number()) {
  2094. tokens.discard_a_token(); // calc
  2095. return calc;
  2096. }
  2097. return nullptr;
  2098. }
  2099. RefPtr<CSSStyleValue> Parser::parse_number_value(TokenStream<ComponentValue>& tokens)
  2100. {
  2101. auto const& peek_token = tokens.next_token();
  2102. if (peek_token.is(Token::Type::Number)) {
  2103. tokens.discard_a_token(); // number
  2104. return NumberStyleValue::create(peek_token.token().number().value());
  2105. }
  2106. if (auto calc = parse_calculated_value(peek_token); calc && calc->resolves_to_number()) {
  2107. tokens.discard_a_token(); // calc
  2108. return calc;
  2109. }
  2110. return nullptr;
  2111. }
  2112. RefPtr<CSSStyleValue> Parser::parse_number_percentage_value(TokenStream<ComponentValue>& tokens)
  2113. {
  2114. // Parses [<percentage> | <number>] (which is equivalent to [<alpha-value>])
  2115. auto const& peek_token = tokens.next_token();
  2116. if (peek_token.is(Token::Type::Number)) {
  2117. tokens.discard_a_token(); // number
  2118. return NumberStyleValue::create(peek_token.token().number().value());
  2119. }
  2120. if (peek_token.is(Token::Type::Percentage)) {
  2121. tokens.discard_a_token(); // percentage
  2122. return PercentageStyleValue::create(Percentage(peek_token.token().percentage()));
  2123. }
  2124. if (auto calc = parse_calculated_value(peek_token); calc && calc->resolves_to_number_percentage()) {
  2125. tokens.discard_a_token(); // calc
  2126. return calc;
  2127. }
  2128. return nullptr;
  2129. }
  2130. RefPtr<CSSStyleValue> Parser::parse_number_percentage_none_value(TokenStream<ComponentValue>& tokens)
  2131. {
  2132. // Parses [<percentage> | <number> | none] (which is equivalent to [<alpha-value> | none])
  2133. auto peek_token = tokens.next_token();
  2134. if (peek_token.is(Token::Type::Number)) {
  2135. tokens.discard_a_token(); // number
  2136. return NumberStyleValue::create(peek_token.token().number().value());
  2137. }
  2138. if (peek_token.is(Token::Type::Percentage)) {
  2139. tokens.discard_a_token(); // percentage
  2140. return PercentageStyleValue::create(Percentage(peek_token.token().percentage()));
  2141. }
  2142. if (auto calc = parse_calculated_value(peek_token); calc && calc->resolves_to_number_percentage()) {
  2143. tokens.discard_a_token(); // calc
  2144. return calc;
  2145. }
  2146. if (peek_token.is(Token::Type::Ident)) {
  2147. auto keyword = keyword_from_string(peek_token.token().ident());
  2148. if (keyword.has_value() && keyword.value() == Keyword::None) {
  2149. tokens.discard_a_token(); // keyword none
  2150. return CSSKeywordValue::create(keyword.value());
  2151. }
  2152. }
  2153. return nullptr;
  2154. }
  2155. RefPtr<CSSStyleValue> Parser::parse_percentage_value(TokenStream<ComponentValue>& tokens)
  2156. {
  2157. auto const& peek_token = tokens.next_token();
  2158. if (peek_token.is(Token::Type::Percentage)) {
  2159. tokens.discard_a_token(); // percentage
  2160. return PercentageStyleValue::create(Percentage(peek_token.token().percentage()));
  2161. }
  2162. if (auto calc = parse_calculated_value(peek_token); calc && calc->resolves_to_percentage()) {
  2163. tokens.discard_a_token(); // calc
  2164. return calc;
  2165. }
  2166. return nullptr;
  2167. }
  2168. RefPtr<CSSStyleValue> Parser::parse_angle_value(TokenStream<ComponentValue>& tokens)
  2169. {
  2170. auto transaction = tokens.begin_transaction();
  2171. if (auto dimension_value = parse_dimension_value(tokens)) {
  2172. if (dimension_value->is_angle()
  2173. || (dimension_value->is_math() && dimension_value->as_math().resolves_to_angle())) {
  2174. transaction.commit();
  2175. return dimension_value;
  2176. }
  2177. }
  2178. return nullptr;
  2179. }
  2180. RefPtr<CSSStyleValue> Parser::parse_angle_percentage_value(TokenStream<ComponentValue>& tokens)
  2181. {
  2182. auto transaction = tokens.begin_transaction();
  2183. if (auto dimension_value = parse_dimension_value(tokens)) {
  2184. if (dimension_value->is_angle() || dimension_value->is_percentage()
  2185. || (dimension_value->is_math() && dimension_value->as_math().resolves_to_angle_percentage())) {
  2186. transaction.commit();
  2187. return dimension_value;
  2188. }
  2189. }
  2190. return nullptr;
  2191. }
  2192. RefPtr<CSSStyleValue> Parser::parse_flex_value(TokenStream<ComponentValue>& tokens)
  2193. {
  2194. auto transaction = tokens.begin_transaction();
  2195. if (auto dimension_value = parse_dimension_value(tokens)) {
  2196. if (dimension_value->is_flex()
  2197. || (dimension_value->is_math() && dimension_value->as_math().resolves_to_flex())) {
  2198. transaction.commit();
  2199. return dimension_value;
  2200. }
  2201. }
  2202. return nullptr;
  2203. }
  2204. RefPtr<CSSStyleValue> Parser::parse_frequency_value(TokenStream<ComponentValue>& tokens)
  2205. {
  2206. auto transaction = tokens.begin_transaction();
  2207. if (auto dimension_value = parse_dimension_value(tokens)) {
  2208. if (dimension_value->is_frequency()
  2209. || (dimension_value->is_math() && dimension_value->as_math().resolves_to_frequency())) {
  2210. transaction.commit();
  2211. return dimension_value;
  2212. }
  2213. }
  2214. return nullptr;
  2215. }
  2216. RefPtr<CSSStyleValue> Parser::parse_frequency_percentage_value(TokenStream<ComponentValue>& tokens)
  2217. {
  2218. auto transaction = tokens.begin_transaction();
  2219. if (auto dimension_value = parse_dimension_value(tokens)) {
  2220. if (dimension_value->is_frequency() || dimension_value->is_percentage()
  2221. || (dimension_value->is_math() && dimension_value->as_math().resolves_to_frequency_percentage())) {
  2222. transaction.commit();
  2223. return dimension_value;
  2224. }
  2225. }
  2226. return nullptr;
  2227. }
  2228. RefPtr<CSSStyleValue> Parser::parse_length_value(TokenStream<ComponentValue>& tokens)
  2229. {
  2230. auto transaction = tokens.begin_transaction();
  2231. if (auto dimension_value = parse_dimension_value(tokens)) {
  2232. if (dimension_value->is_length()
  2233. || (dimension_value->is_math() && dimension_value->as_math().resolves_to_length())) {
  2234. transaction.commit();
  2235. return dimension_value;
  2236. }
  2237. }
  2238. return nullptr;
  2239. }
  2240. RefPtr<CSSStyleValue> Parser::parse_length_percentage_value(TokenStream<ComponentValue>& tokens)
  2241. {
  2242. auto transaction = tokens.begin_transaction();
  2243. if (auto dimension_value = parse_dimension_value(tokens)) {
  2244. if (dimension_value->is_length() || dimension_value->is_percentage()
  2245. || (dimension_value->is_math() && dimension_value->as_math().resolves_to_length_percentage())) {
  2246. transaction.commit();
  2247. return dimension_value;
  2248. }
  2249. }
  2250. return nullptr;
  2251. }
  2252. RefPtr<CSSStyleValue> Parser::parse_resolution_value(TokenStream<ComponentValue>& tokens)
  2253. {
  2254. auto transaction = tokens.begin_transaction();
  2255. if (auto dimension_value = parse_dimension_value(tokens)) {
  2256. if (dimension_value->is_resolution()
  2257. || (dimension_value->is_math() && dimension_value->as_math().resolves_to_resolution())) {
  2258. transaction.commit();
  2259. return dimension_value;
  2260. }
  2261. }
  2262. return nullptr;
  2263. }
  2264. RefPtr<CSSStyleValue> Parser::parse_time_value(TokenStream<ComponentValue>& tokens)
  2265. {
  2266. auto transaction = tokens.begin_transaction();
  2267. if (auto dimension_value = parse_dimension_value(tokens)) {
  2268. if (dimension_value->is_time()
  2269. || (dimension_value->is_math() && dimension_value->as_math().resolves_to_time())) {
  2270. transaction.commit();
  2271. return dimension_value;
  2272. }
  2273. }
  2274. return nullptr;
  2275. }
  2276. RefPtr<CSSStyleValue> Parser::parse_time_percentage_value(TokenStream<ComponentValue>& tokens)
  2277. {
  2278. auto transaction = tokens.begin_transaction();
  2279. if (auto dimension_value = parse_dimension_value(tokens)) {
  2280. if (dimension_value->is_time() || dimension_value->is_percentage()
  2281. || (dimension_value->is_math() && dimension_value->as_math().resolves_to_time_percentage())) {
  2282. transaction.commit();
  2283. return dimension_value;
  2284. }
  2285. }
  2286. return nullptr;
  2287. }
  2288. RefPtr<CSSStyleValue> Parser::parse_keyword_value(TokenStream<ComponentValue>& tokens)
  2289. {
  2290. auto const& peek_token = tokens.next_token();
  2291. if (peek_token.is(Token::Type::Ident)) {
  2292. auto keyword = keyword_from_string(peek_token.token().ident());
  2293. if (keyword.has_value()) {
  2294. tokens.discard_a_token(); // ident
  2295. return CSSKeywordValue::create(keyword.value());
  2296. }
  2297. }
  2298. return nullptr;
  2299. }
  2300. // https://www.w3.org/TR/CSS2/visufx.html#value-def-shape
  2301. RefPtr<CSSStyleValue> Parser::parse_rect_value(TokenStream<ComponentValue>& tokens)
  2302. {
  2303. auto transaction = tokens.begin_transaction();
  2304. auto const& function_token = tokens.consume_a_token();
  2305. if (!function_token.is_function("rect"sv))
  2306. return nullptr;
  2307. Vector<Length, 4> params;
  2308. auto argument_tokens = TokenStream { function_token.function().value };
  2309. enum class CommaRequirement {
  2310. Unknown,
  2311. RequiresCommas,
  2312. RequiresNoCommas
  2313. };
  2314. enum class Side {
  2315. Top = 0,
  2316. Right = 1,
  2317. Bottom = 2,
  2318. Left = 3
  2319. };
  2320. auto comma_requirement = CommaRequirement::Unknown;
  2321. // In CSS 2.1, the only valid <shape> value is: rect(<top>, <right>, <bottom>, <left>) where
  2322. // <top> and <bottom> specify offsets from the top border edge of the box, and <right>, and
  2323. // <left> specify offsets from the left border edge of the box.
  2324. for (size_t side = 0; side < 4; side++) {
  2325. argument_tokens.discard_whitespace();
  2326. // <top>, <right>, <bottom>, and <left> may either have a <length> value or 'auto'.
  2327. // Negative lengths are permitted.
  2328. if (argument_tokens.next_token().is_ident("auto"sv)) {
  2329. (void)argument_tokens.consume_a_token(); // `auto`
  2330. params.append(Length::make_auto());
  2331. } else {
  2332. auto maybe_length = parse_length(argument_tokens);
  2333. if (!maybe_length.has_value())
  2334. return nullptr;
  2335. if (maybe_length.value().is_calculated()) {
  2336. dbgln("FIXME: Support calculated lengths in rect(): {}", maybe_length.value().calculated()->to_string());
  2337. return nullptr;
  2338. }
  2339. params.append(maybe_length.value().value());
  2340. }
  2341. argument_tokens.discard_whitespace();
  2342. // The last side, should be no more tokens following it.
  2343. if (static_cast<Side>(side) == Side::Left) {
  2344. if (argument_tokens.has_next_token())
  2345. return nullptr;
  2346. break;
  2347. }
  2348. bool next_is_comma = argument_tokens.next_token().is(Token::Type::Comma);
  2349. // Authors should separate offset values with commas. User agents must support separation
  2350. // with commas, but may also support separation without commas (but not a combination),
  2351. // because a previous revision of this specification was ambiguous in this respect.
  2352. if (comma_requirement == CommaRequirement::Unknown)
  2353. comma_requirement = next_is_comma ? CommaRequirement::RequiresCommas : CommaRequirement::RequiresNoCommas;
  2354. if (comma_requirement == CommaRequirement::RequiresCommas) {
  2355. if (next_is_comma)
  2356. argument_tokens.discard_a_token();
  2357. else
  2358. return nullptr;
  2359. } else if (comma_requirement == CommaRequirement::RequiresNoCommas) {
  2360. if (next_is_comma)
  2361. return nullptr;
  2362. } else {
  2363. VERIFY_NOT_REACHED();
  2364. }
  2365. }
  2366. transaction.commit();
  2367. return RectStyleValue::create(EdgeRect { params[0], params[1], params[2], params[3] });
  2368. }
  2369. // https://www.w3.org/TR/css-color-4/#typedef-hue
  2370. RefPtr<CSSStyleValue> Parser::parse_hue_none_value(TokenStream<ComponentValue>& tokens)
  2371. {
  2372. // Parses [<hue> | none]
  2373. // <hue> = <number> | <angle>
  2374. auto peek_token = tokens.next_token();
  2375. if (peek_token.is(Token::Type::Number)) {
  2376. tokens.discard_a_token(); // number
  2377. return NumberStyleValue::create(peek_token.token().number().value());
  2378. }
  2379. if (auto calc = parse_calculated_value(peek_token); calc && (calc->resolves_to_number() || calc->resolves_to_angle())) {
  2380. tokens.discard_a_token(); // calc number/angle
  2381. return calc;
  2382. }
  2383. if (auto dimension = parse_dimension(peek_token); dimension.has_value() && dimension->is_angle()) {
  2384. tokens.discard_a_token(); // angle
  2385. return AngleStyleValue::create(dimension->angle());
  2386. }
  2387. if (peek_token.is(Token::Type::Ident)) {
  2388. auto keyword = keyword_from_string(peek_token.token().ident());
  2389. if (keyword.has_value() && keyword.value() == Keyword::None) {
  2390. tokens.discard_a_token(); // keyword none
  2391. return CSSKeywordValue::create(keyword.value());
  2392. }
  2393. }
  2394. return nullptr;
  2395. }
  2396. // https://www.w3.org/TR/css-color-4/#typedef-color-alpha-value
  2397. RefPtr<CSSStyleValue> Parser::parse_solidus_and_alpha_value(TokenStream<ComponentValue>& tokens)
  2398. {
  2399. // [ / [<alpha-value> | none] ]?
  2400. // <alpha-value> = <number> | <percentage>
  2401. // Common to the modern-syntax color functions.
  2402. auto transaction = tokens.begin_transaction();
  2403. tokens.discard_whitespace();
  2404. if (!tokens.consume_a_token().is_delim('/'))
  2405. return {};
  2406. tokens.discard_whitespace();
  2407. auto alpha = parse_number_percentage_none_value(tokens);
  2408. if (!alpha)
  2409. return {};
  2410. tokens.discard_whitespace();
  2411. transaction.commit();
  2412. return alpha;
  2413. }
  2414. // https://www.w3.org/TR/css-color-4/#funcdef-rgb
  2415. RefPtr<CSSStyleValue> Parser::parse_rgb_color_value(TokenStream<ComponentValue>& outer_tokens)
  2416. {
  2417. // rgb() = [ <legacy-rgb-syntax> | <modern-rgb-syntax> ]
  2418. // rgba() = [ <legacy-rgba-syntax> | <modern-rgba-syntax> ]
  2419. // <legacy-rgb-syntax> = rgb( <percentage>#{3} , <alpha-value>? ) |
  2420. // rgb( <number>#{3} , <alpha-value>? )
  2421. // <legacy-rgba-syntax> = rgba( <percentage>#{3} , <alpha-value>? ) |
  2422. // rgba( <number>#{3} , <alpha-value>? )
  2423. // <modern-rgb-syntax> = rgb(
  2424. // [ <number> | <percentage> | none]{3}
  2425. // [ / [<alpha-value> | none] ]? )
  2426. // <modern-rgba-syntax> = rgba(
  2427. // [ <number> | <percentage> | none]{3}
  2428. // [ / [<alpha-value> | none] ]? )
  2429. auto transaction = outer_tokens.begin_transaction();
  2430. outer_tokens.discard_whitespace();
  2431. auto& function_token = outer_tokens.consume_a_token();
  2432. if (!function_token.is_function("rgb"sv) && !function_token.is_function("rgba"sv))
  2433. return {};
  2434. RefPtr<CSSStyleValue> red;
  2435. RefPtr<CSSStyleValue> green;
  2436. RefPtr<CSSStyleValue> blue;
  2437. RefPtr<CSSStyleValue> alpha;
  2438. auto inner_tokens = TokenStream { function_token.function().value };
  2439. inner_tokens.discard_whitespace();
  2440. red = parse_number_percentage_none_value(inner_tokens);
  2441. if (!red)
  2442. return {};
  2443. inner_tokens.discard_whitespace();
  2444. bool legacy_syntax = inner_tokens.next_token().is(Token::Type::Comma);
  2445. if (legacy_syntax) {
  2446. // Legacy syntax
  2447. // <percentage>#{3} , <alpha-value>?
  2448. // | <number>#{3} , <alpha-value>?
  2449. // So, r/g/b can be numbers or percentages, as long as they're all the same type.
  2450. // We accepted the 'none' keyword when parsing the red value, but it's not allowed in the legacy syntax.
  2451. if (red->is_keyword())
  2452. return {};
  2453. inner_tokens.discard_a_token(); // comma
  2454. inner_tokens.discard_whitespace();
  2455. green = parse_number_percentage_value(inner_tokens);
  2456. if (!green)
  2457. return {};
  2458. inner_tokens.discard_whitespace();
  2459. if (!inner_tokens.consume_a_token().is(Token::Type::Comma))
  2460. return {};
  2461. inner_tokens.discard_whitespace();
  2462. blue = parse_number_percentage_value(inner_tokens);
  2463. if (!blue)
  2464. return {};
  2465. inner_tokens.discard_whitespace();
  2466. if (inner_tokens.has_next_token()) {
  2467. // Try and read comma and alpha
  2468. if (!inner_tokens.consume_a_token().is(Token::Type::Comma))
  2469. return {};
  2470. inner_tokens.discard_whitespace();
  2471. alpha = parse_number_percentage_value(inner_tokens);
  2472. if (!alpha)
  2473. return {};
  2474. inner_tokens.discard_whitespace();
  2475. if (inner_tokens.has_next_token())
  2476. return {};
  2477. }
  2478. // Verify we're all percentages or all numbers
  2479. auto is_percentage = [](CSSStyleValue const& style_value) {
  2480. return style_value.is_percentage()
  2481. || (style_value.is_math() && style_value.as_math().resolves_to_percentage());
  2482. };
  2483. bool red_is_percentage = is_percentage(*red);
  2484. bool green_is_percentage = is_percentage(*green);
  2485. bool blue_is_percentage = is_percentage(*blue);
  2486. if (red_is_percentage != green_is_percentage || red_is_percentage != blue_is_percentage)
  2487. return {};
  2488. } else {
  2489. // Modern syntax
  2490. // [ <number> | <percentage> | none]{3} [ / [<alpha-value> | none] ]?
  2491. green = parse_number_percentage_none_value(inner_tokens);
  2492. if (!green)
  2493. return {};
  2494. inner_tokens.discard_whitespace();
  2495. blue = parse_number_percentage_none_value(inner_tokens);
  2496. if (!blue)
  2497. return {};
  2498. inner_tokens.discard_whitespace();
  2499. if (inner_tokens.has_next_token()) {
  2500. alpha = parse_solidus_and_alpha_value(inner_tokens);
  2501. if (!alpha || inner_tokens.has_next_token())
  2502. return {};
  2503. }
  2504. }
  2505. if (!alpha)
  2506. alpha = NumberStyleValue::create(1);
  2507. transaction.commit();
  2508. return CSSRGB::create(red.release_nonnull(), green.release_nonnull(), blue.release_nonnull(), alpha.release_nonnull());
  2509. }
  2510. // https://www.w3.org/TR/css-color-4/#funcdef-hsl
  2511. RefPtr<CSSStyleValue> Parser::parse_hsl_color_value(TokenStream<ComponentValue>& outer_tokens)
  2512. {
  2513. // hsl() = [ <legacy-hsl-syntax> | <modern-hsl-syntax> ]
  2514. // hsla() = [ <legacy-hsla-syntax> | <modern-hsla-syntax> ]
  2515. // <modern-hsl-syntax> = hsl(
  2516. // [<hue> | none]
  2517. // [<percentage> | <number> | none]
  2518. // [<percentage> | <number> | none]
  2519. // [ / [<alpha-value> | none] ]? )
  2520. // <modern-hsla-syntax> = hsla(
  2521. // [<hue> | none]
  2522. // [<percentage> | <number> | none]
  2523. // [<percentage> | <number> | none]
  2524. // [ / [<alpha-value> | none] ]? )
  2525. // <legacy-hsl-syntax> = hsl( <hue>, <percentage>, <percentage>, <alpha-value>? )
  2526. // <legacy-hsla-syntax> = hsla( <hue>, <percentage>, <percentage>, <alpha-value>? )
  2527. auto transaction = outer_tokens.begin_transaction();
  2528. outer_tokens.discard_whitespace();
  2529. auto& function_token = outer_tokens.consume_a_token();
  2530. if (!function_token.is_function("hsl"sv) && !function_token.is_function("hsla"sv))
  2531. return {};
  2532. RefPtr<CSSStyleValue> h;
  2533. RefPtr<CSSStyleValue> s;
  2534. RefPtr<CSSStyleValue> l;
  2535. RefPtr<CSSStyleValue> alpha;
  2536. auto inner_tokens = TokenStream { function_token.function().value };
  2537. inner_tokens.discard_whitespace();
  2538. h = parse_hue_none_value(inner_tokens);
  2539. if (!h)
  2540. return {};
  2541. inner_tokens.discard_whitespace();
  2542. bool legacy_syntax = inner_tokens.next_token().is(Token::Type::Comma);
  2543. if (legacy_syntax) {
  2544. // Legacy syntax
  2545. // <hue>, <percentage>, <percentage>, <alpha-value>?
  2546. // We accepted the 'none' keyword when parsing the h value, but it's not allowed in the legacy syntax.
  2547. if (h->is_keyword())
  2548. return {};
  2549. (void)inner_tokens.consume_a_token(); // comma
  2550. inner_tokens.discard_whitespace();
  2551. s = parse_percentage_value(inner_tokens);
  2552. if (!s)
  2553. return {};
  2554. inner_tokens.discard_whitespace();
  2555. if (!inner_tokens.consume_a_token().is(Token::Type::Comma))
  2556. return {};
  2557. inner_tokens.discard_whitespace();
  2558. l = parse_percentage_value(inner_tokens);
  2559. if (!l)
  2560. return {};
  2561. inner_tokens.discard_whitespace();
  2562. if (inner_tokens.has_next_token()) {
  2563. // Try and read comma and alpha
  2564. if (!inner_tokens.consume_a_token().is(Token::Type::Comma))
  2565. return {};
  2566. inner_tokens.discard_whitespace();
  2567. alpha = parse_number_percentage_value(inner_tokens);
  2568. // The parser has consumed a comma, so the alpha value is now required
  2569. if (!alpha)
  2570. return {};
  2571. inner_tokens.discard_whitespace();
  2572. if (inner_tokens.has_next_token())
  2573. return {};
  2574. }
  2575. } else {
  2576. // Modern syntax
  2577. // [<hue> | none]
  2578. // [<percentage> | <number> | none]
  2579. // [<percentage> | <number> | none]
  2580. // [ / [<alpha-value> | none] ]?
  2581. s = parse_number_percentage_none_value(inner_tokens);
  2582. if (!s)
  2583. return {};
  2584. inner_tokens.discard_whitespace();
  2585. l = parse_number_percentage_none_value(inner_tokens);
  2586. if (!l)
  2587. return {};
  2588. inner_tokens.discard_whitespace();
  2589. if (inner_tokens.has_next_token()) {
  2590. alpha = parse_solidus_and_alpha_value(inner_tokens);
  2591. if (!alpha || inner_tokens.has_next_token())
  2592. return {};
  2593. }
  2594. }
  2595. if (!alpha)
  2596. alpha = NumberStyleValue::create(1);
  2597. transaction.commit();
  2598. return CSSHSL::create(h.release_nonnull(), s.release_nonnull(), l.release_nonnull(), alpha.release_nonnull());
  2599. }
  2600. // https://www.w3.org/TR/css-color-4/#funcdef-hwb
  2601. RefPtr<CSSStyleValue> Parser::parse_hwb_color_value(TokenStream<ComponentValue>& outer_tokens)
  2602. {
  2603. // hwb() = hwb(
  2604. // [<hue> | none]
  2605. // [<percentage> | <number> | none]
  2606. // [<percentage> | <number> | none]
  2607. // [ / [<alpha-value> | none] ]? )
  2608. auto transaction = outer_tokens.begin_transaction();
  2609. outer_tokens.discard_whitespace();
  2610. auto& function_token = outer_tokens.consume_a_token();
  2611. if (!function_token.is_function("hwb"sv))
  2612. return {};
  2613. RefPtr<CSSStyleValue> h;
  2614. RefPtr<CSSStyleValue> w;
  2615. RefPtr<CSSStyleValue> b;
  2616. RefPtr<CSSStyleValue> alpha;
  2617. auto inner_tokens = TokenStream { function_token.function().value };
  2618. inner_tokens.discard_whitespace();
  2619. h = parse_hue_none_value(inner_tokens);
  2620. if (!h)
  2621. return {};
  2622. inner_tokens.discard_whitespace();
  2623. w = parse_number_percentage_none_value(inner_tokens);
  2624. if (!w)
  2625. return {};
  2626. inner_tokens.discard_whitespace();
  2627. b = parse_number_percentage_none_value(inner_tokens);
  2628. if (!b)
  2629. return {};
  2630. inner_tokens.discard_whitespace();
  2631. if (inner_tokens.has_next_token()) {
  2632. alpha = parse_solidus_and_alpha_value(inner_tokens);
  2633. if (!alpha || inner_tokens.has_next_token())
  2634. return {};
  2635. }
  2636. if (!alpha)
  2637. alpha = NumberStyleValue::create(1);
  2638. transaction.commit();
  2639. return CSSHWB::create(h.release_nonnull(), w.release_nonnull(), b.release_nonnull(), alpha.release_nonnull());
  2640. }
  2641. Optional<Array<RefPtr<CSSStyleValue>, 4>> Parser::parse_lab_like_color_value(TokenStream<ComponentValue>& outer_tokens, StringView function_name)
  2642. {
  2643. // This helper is designed to be compatible with lab and oklab and parses a function with a form like:
  2644. // f() = f( [ <percentage> | <number> | none]
  2645. // [ <percentage> | <number> | none]
  2646. // [ <percentage> | <number> | none]
  2647. // [ / [<alpha-value> | none] ]? )
  2648. auto transaction = outer_tokens.begin_transaction();
  2649. outer_tokens.discard_whitespace();
  2650. auto& function_token = outer_tokens.consume_a_token();
  2651. if (!function_token.is_function(function_name))
  2652. return OptionalNone {};
  2653. RefPtr<CSSStyleValue> l;
  2654. RefPtr<CSSStyleValue> a;
  2655. RefPtr<CSSStyleValue> b;
  2656. RefPtr<CSSStyleValue> alpha;
  2657. auto inner_tokens = TokenStream { function_token.function().value };
  2658. inner_tokens.discard_whitespace();
  2659. l = parse_number_percentage_none_value(inner_tokens);
  2660. if (!l)
  2661. return OptionalNone {};
  2662. inner_tokens.discard_whitespace();
  2663. a = parse_number_percentage_none_value(inner_tokens);
  2664. if (!a)
  2665. return OptionalNone {};
  2666. inner_tokens.discard_whitespace();
  2667. b = parse_number_percentage_none_value(inner_tokens);
  2668. if (!b)
  2669. return OptionalNone {};
  2670. inner_tokens.discard_whitespace();
  2671. if (inner_tokens.has_next_token()) {
  2672. alpha = parse_solidus_and_alpha_value(inner_tokens);
  2673. if (!alpha || inner_tokens.has_next_token())
  2674. return OptionalNone {};
  2675. }
  2676. if (!alpha)
  2677. alpha = NumberStyleValue::create(1);
  2678. transaction.commit();
  2679. return Array { move(l), move(a), move(b), move(alpha) };
  2680. }
  2681. // https://www.w3.org/TR/css-color-4/#funcdef-lab
  2682. RefPtr<CSSStyleValue> Parser::parse_lab_color_value(TokenStream<ComponentValue>& outer_tokens)
  2683. {
  2684. // lab() = lab( [<percentage> | <number> | none]
  2685. // [ <percentage> | <number> | none]
  2686. // [ <percentage> | <number> | none]
  2687. // [ / [<alpha-value> | none] ]? )
  2688. auto maybe_color_values = parse_lab_like_color_value(outer_tokens, "lab"sv);
  2689. if (!maybe_color_values.has_value())
  2690. return {};
  2691. auto& color_values = *maybe_color_values;
  2692. return CSSLabLike::create<CSSLab>(color_values[0].release_nonnull(),
  2693. color_values[1].release_nonnull(),
  2694. color_values[2].release_nonnull(),
  2695. color_values[3].release_nonnull());
  2696. }
  2697. // https://www.w3.org/TR/css-color-4/#funcdef-oklab
  2698. RefPtr<CSSStyleValue> Parser::parse_oklab_color_value(TokenStream<ComponentValue>& outer_tokens)
  2699. {
  2700. // oklab() = oklab( [ <percentage> | <number> | none]
  2701. // [ <percentage> | <number> | none]
  2702. // [ <percentage> | <number> | none]
  2703. // [ / [<alpha-value> | none] ]? )
  2704. auto maybe_color_values = parse_lab_like_color_value(outer_tokens, "oklab"sv);
  2705. if (!maybe_color_values.has_value())
  2706. return {};
  2707. auto& color_values = *maybe_color_values;
  2708. return CSSLabLike::create<CSSOKLab>(color_values[0].release_nonnull(),
  2709. color_values[1].release_nonnull(),
  2710. color_values[2].release_nonnull(),
  2711. color_values[3].release_nonnull());
  2712. }
  2713. Optional<Array<RefPtr<CSSStyleValue>, 4>> Parser::parse_lch_like_color_value(TokenStream<ComponentValue>& outer_tokens, StringView function_name)
  2714. {
  2715. // This helper is designed to be compatible with lch and oklch and parses a function with a form like:
  2716. // f() = f( [<percentage> | <number> | none]
  2717. // [ <percentage> | <number> | none]
  2718. // [ <hue> | none]
  2719. // [ / [<alpha-value> | none] ]? )
  2720. auto transaction = outer_tokens.begin_transaction();
  2721. outer_tokens.discard_whitespace();
  2722. auto const& function_token = outer_tokens.consume_a_token();
  2723. if (!function_token.is_function(function_name))
  2724. return OptionalNone {};
  2725. auto inner_tokens = TokenStream { function_token.function().value };
  2726. inner_tokens.discard_whitespace();
  2727. auto l = parse_number_percentage_none_value(inner_tokens);
  2728. if (!l)
  2729. return OptionalNone {};
  2730. inner_tokens.discard_whitespace();
  2731. auto c = parse_number_percentage_none_value(inner_tokens);
  2732. if (!c)
  2733. return OptionalNone {};
  2734. inner_tokens.discard_whitespace();
  2735. auto h = parse_hue_none_value(inner_tokens);
  2736. if (!h)
  2737. return OptionalNone {};
  2738. inner_tokens.discard_whitespace();
  2739. RefPtr<CSSStyleValue> alpha;
  2740. if (inner_tokens.has_next_token()) {
  2741. alpha = parse_solidus_and_alpha_value(inner_tokens);
  2742. if (!alpha || inner_tokens.has_next_token())
  2743. return OptionalNone {};
  2744. }
  2745. if (!alpha)
  2746. alpha = NumberStyleValue::create(1);
  2747. transaction.commit();
  2748. return Array { move(l), move(c), move(h), move(alpha) };
  2749. }
  2750. // https://www.w3.org/TR/css-color-4/#funcdef-lch
  2751. RefPtr<CSSStyleValue> Parser::parse_lch_color_value(TokenStream<ComponentValue>& outer_tokens)
  2752. {
  2753. // lch() = lch( [<percentage> | <number> | none]
  2754. // [ <percentage> | <number> | none]
  2755. // [ <hue> | none]
  2756. // [ / [<alpha-value> | none] ]? )
  2757. auto maybe_color_values = parse_lch_like_color_value(outer_tokens, "lch"sv);
  2758. if (!maybe_color_values.has_value())
  2759. return {};
  2760. auto& color_values = *maybe_color_values;
  2761. return CSSLCHLike::create<CSSLCH>(color_values[0].release_nonnull(),
  2762. color_values[1].release_nonnull(),
  2763. color_values[2].release_nonnull(),
  2764. color_values[3].release_nonnull());
  2765. }
  2766. // https://www.w3.org/TR/css-color-4/#funcdef-oklch
  2767. RefPtr<CSSStyleValue> Parser::parse_oklch_color_value(TokenStream<ComponentValue>& outer_tokens)
  2768. {
  2769. // oklch() = oklch( [ <percentage> | <number> | none]
  2770. // [ <percentage> | <number> | none]
  2771. // [ <hue> | none]
  2772. // [ / [<alpha-value> | none] ]? )
  2773. auto maybe_color_values = parse_lch_like_color_value(outer_tokens, "oklch"sv);
  2774. if (!maybe_color_values.has_value())
  2775. return {};
  2776. auto& color_values = *maybe_color_values;
  2777. return CSSLCHLike::create<CSSOKLCH>(color_values[0].release_nonnull(),
  2778. color_values[1].release_nonnull(),
  2779. color_values[2].release_nonnull(),
  2780. color_values[3].release_nonnull());
  2781. }
  2782. // https://www.w3.org/TR/css-color-4/#funcdef-color
  2783. RefPtr<CSSStyleValue> Parser::parse_color_function(TokenStream<ComponentValue>& outer_tokens)
  2784. {
  2785. // color() = color( <colorspace-params> [ / [ <alpha-value> | none ] ]? )
  2786. // <colorspace-params> = [ <predefined-rgb-params> | <xyz-params>]
  2787. // <predefined-rgb-params> = <predefined-rgb> [ <number> | <percentage> | none ]{3}
  2788. // <predefined-rgb> = srgb | srgb-linear | display-p3 | a98-rgb | prophoto-rgb | rec2020
  2789. // <xyz-params> = <xyz-space> [ <number> | <percentage> | none ]{3}
  2790. // <xyz-space> = xyz | xyz-d50 | xyz-d65
  2791. auto transaction = outer_tokens.begin_transaction();
  2792. outer_tokens.discard_whitespace();
  2793. auto const& function_token = outer_tokens.consume_a_token();
  2794. if (!function_token.is_function("color"sv))
  2795. return {};
  2796. auto inner_tokens = TokenStream { function_token.function().value };
  2797. inner_tokens.discard_whitespace();
  2798. auto const& maybe_color_space = inner_tokens.consume_a_token();
  2799. inner_tokens.discard_whitespace();
  2800. if (!any_of(CSSColor::s_supported_color_space, [&](auto supported) { return maybe_color_space.is_ident(supported); }))
  2801. return {};
  2802. auto const& color_space = maybe_color_space.token().ident();
  2803. auto c1 = parse_number_percentage_value(inner_tokens);
  2804. if (!c1)
  2805. return {};
  2806. inner_tokens.discard_whitespace();
  2807. auto c2 = parse_number_percentage_value(inner_tokens);
  2808. if (!c2)
  2809. return {};
  2810. inner_tokens.discard_whitespace();
  2811. auto c3 = parse_number_percentage_value(inner_tokens);
  2812. if (!c3)
  2813. return {};
  2814. inner_tokens.discard_whitespace();
  2815. RefPtr<CSSStyleValue> alpha;
  2816. if (inner_tokens.has_next_token()) {
  2817. alpha = parse_solidus_and_alpha_value(inner_tokens);
  2818. if (!alpha || inner_tokens.has_next_token())
  2819. return {};
  2820. }
  2821. if (!alpha)
  2822. alpha = NumberStyleValue::create(1);
  2823. transaction.commit();
  2824. return CSSColor::create(color_space.to_ascii_lowercase(),
  2825. c1.release_nonnull(),
  2826. c2.release_nonnull(),
  2827. c3.release_nonnull(),
  2828. alpha.release_nonnull());
  2829. }
  2830. // https://www.w3.org/TR/css-color-4/#color-syntax
  2831. RefPtr<CSSStyleValue> Parser::parse_color_value(TokenStream<ComponentValue>& tokens)
  2832. {
  2833. // Keywords: <system-color> | <deprecated-color> | currentColor
  2834. {
  2835. auto transaction = tokens.begin_transaction();
  2836. if (auto keyword = parse_keyword_value(tokens); keyword && keyword->has_color()) {
  2837. transaction.commit();
  2838. return keyword;
  2839. }
  2840. }
  2841. // Functions
  2842. if (auto color = parse_color_function(tokens))
  2843. return color;
  2844. if (auto rgb = parse_rgb_color_value(tokens))
  2845. return rgb;
  2846. if (auto hsl = parse_hsl_color_value(tokens))
  2847. return hsl;
  2848. if (auto hwb = parse_hwb_color_value(tokens))
  2849. return hwb;
  2850. if (auto lab = parse_lab_color_value(tokens))
  2851. return lab;
  2852. if (auto lch = parse_lch_color_value(tokens))
  2853. return lch;
  2854. if (auto oklab = parse_oklab_color_value(tokens))
  2855. return oklab;
  2856. if (auto oklch = parse_oklch_color_value(tokens))
  2857. return oklch;
  2858. auto transaction = tokens.begin_transaction();
  2859. tokens.discard_whitespace();
  2860. auto const& component_value = tokens.consume_a_token();
  2861. if (component_value.is(Token::Type::Ident)) {
  2862. auto ident = component_value.token().ident();
  2863. auto color = Color::from_string(ident);
  2864. if (color.has_value()) {
  2865. transaction.commit();
  2866. return CSSColorValue::create_from_color(color.release_value());
  2867. }
  2868. // Otherwise, fall through to the hashless-hex-color case
  2869. }
  2870. if (component_value.is(Token::Type::Hash)) {
  2871. auto color = Color::from_string(MUST(String::formatted("#{}", component_value.token().hash_value())));
  2872. if (color.has_value()) {
  2873. transaction.commit();
  2874. return CSSColorValue::create_from_color(color.release_value());
  2875. }
  2876. return {};
  2877. }
  2878. // https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk
  2879. if (m_context.in_quirks_mode() && property_has_quirk(m_context.current_property_id(), Quirk::HashlessHexColor)) {
  2880. // The value of a quirky color is obtained from the possible component values using the following algorithm,
  2881. // aborting on the first step that returns a value:
  2882. // 1. Let cv be the component value.
  2883. auto const& cv = component_value;
  2884. String serialization;
  2885. // 2. If cv is a <number-token> or a <dimension-token>, follow these substeps:
  2886. if (cv.is(Token::Type::Number) || cv.is(Token::Type::Dimension)) {
  2887. // 1. If cv’s type flag is not "integer", return an error.
  2888. // This means that values that happen to use scientific notation, e.g., 5e5e5e, will fail to parse.
  2889. if (!cv.token().number().is_integer())
  2890. return {};
  2891. // 2. If cv’s value is less than zero, return an error.
  2892. auto value = cv.is(Token::Type::Number) ? cv.token().to_integer() : cv.token().dimension_value_int();
  2893. if (value < 0)
  2894. return {};
  2895. // 3. Let serialization be the serialization of cv’s value, as a base-ten integer using digits 0-9 (U+0030 to U+0039) in the shortest form possible.
  2896. StringBuilder serialization_builder;
  2897. serialization_builder.appendff("{}", value);
  2898. // 4. If cv is a <dimension-token>, append the unit to serialization.
  2899. if (cv.is(Token::Type::Dimension))
  2900. serialization_builder.append(cv.token().dimension_unit());
  2901. // 5. If serialization consists of fewer than six characters, prepend zeros (U+0030) so that it becomes six characters.
  2902. serialization = MUST(serialization_builder.to_string());
  2903. if (serialization_builder.length() < 6) {
  2904. StringBuilder builder;
  2905. for (size_t i = 0; i < (6 - serialization_builder.length()); i++)
  2906. builder.append('0');
  2907. builder.append(serialization_builder.string_view());
  2908. serialization = MUST(builder.to_string());
  2909. }
  2910. }
  2911. // 3. Otherwise, cv is an <ident-token>; let serialization be cv’s value.
  2912. else {
  2913. if (!cv.is(Token::Type::Ident))
  2914. return {};
  2915. serialization = cv.token().ident().to_string();
  2916. }
  2917. // 4. If serialization does not consist of three or six characters, return an error.
  2918. if (serialization.bytes().size() != 3 && serialization.bytes().size() != 6)
  2919. return {};
  2920. // 5. If serialization contains any characters not in the range [0-9A-Fa-f] (U+0030 to U+0039, U+0041 to U+0046, U+0061 to U+0066), return an error.
  2921. for (auto c : serialization.bytes_as_string_view()) {
  2922. if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')))
  2923. return {};
  2924. }
  2925. // 6. Return the concatenation of "#" (U+0023) and serialization.
  2926. auto color = Color::from_string(MUST(String::formatted("#{}", serialization)));
  2927. if (color.has_value()) {
  2928. transaction.commit();
  2929. return CSSColorValue::create_from_color(color.release_value());
  2930. }
  2931. }
  2932. return {};
  2933. }
  2934. // https://drafts.csswg.org/css-lists-3/#counter-functions
  2935. RefPtr<CSSStyleValue> Parser::parse_counter_value(TokenStream<ComponentValue>& tokens)
  2936. {
  2937. auto parse_counter_name = [this](TokenStream<ComponentValue>& tokens) -> Optional<FlyString> {
  2938. // https://drafts.csswg.org/css-lists-3/#typedef-counter-name
  2939. // Counters are referred to in CSS syntax using the <counter-name> type, which represents
  2940. // their name as a <custom-ident>. A <counter-name> name cannot match the keyword none;
  2941. // such an identifier is invalid as a <counter-name>.
  2942. auto transaction = tokens.begin_transaction();
  2943. tokens.discard_whitespace();
  2944. auto counter_name = parse_custom_ident_value(tokens, { "none"sv });
  2945. if (!counter_name)
  2946. return {};
  2947. tokens.discard_whitespace();
  2948. if (tokens.has_next_token())
  2949. return {};
  2950. transaction.commit();
  2951. return counter_name->custom_ident();
  2952. };
  2953. auto parse_counter_style = [this](TokenStream<ComponentValue>& tokens) -> RefPtr<CSSStyleValue> {
  2954. // https://drafts.csswg.org/css-counter-styles-3/#typedef-counter-style
  2955. // <counter-style> = <counter-style-name> | <symbols()>
  2956. // For now we just support <counter-style-name>, found here:
  2957. // https://drafts.csswg.org/css-counter-styles-3/#typedef-counter-style-name
  2958. // <counter-style-name> is a <custom-ident> that is not an ASCII case-insensitive match for none.
  2959. auto transaction = tokens.begin_transaction();
  2960. tokens.discard_whitespace();
  2961. auto counter_style_name = parse_custom_ident_value(tokens, { "none"sv });
  2962. if (!counter_style_name)
  2963. return {};
  2964. tokens.discard_whitespace();
  2965. if (tokens.has_next_token())
  2966. return {};
  2967. transaction.commit();
  2968. return counter_style_name.release_nonnull();
  2969. };
  2970. auto transaction = tokens.begin_transaction();
  2971. auto const& token = tokens.consume_a_token();
  2972. if (token.is_function("counter"sv)) {
  2973. // counter() = counter( <counter-name>, <counter-style>? )
  2974. auto& function = token.function();
  2975. TokenStream function_tokens { function.value };
  2976. auto function_values = parse_a_comma_separated_list_of_component_values(function_tokens);
  2977. if (function_values.is_empty() || function_values.size() > 2)
  2978. return nullptr;
  2979. TokenStream name_tokens { function_values[0] };
  2980. auto counter_name = parse_counter_name(name_tokens);
  2981. if (!counter_name.has_value())
  2982. return nullptr;
  2983. RefPtr<CSSStyleValue> counter_style;
  2984. if (function_values.size() > 1) {
  2985. TokenStream counter_style_tokens { function_values[1] };
  2986. counter_style = parse_counter_style(counter_style_tokens);
  2987. if (!counter_style)
  2988. return nullptr;
  2989. } else {
  2990. // In both cases, if the <counter-style> argument is omitted it defaults to `decimal`.
  2991. counter_style = CustomIdentStyleValue::create("decimal"_fly_string);
  2992. }
  2993. transaction.commit();
  2994. return CounterStyleValue::create_counter(counter_name.release_value(), counter_style.release_nonnull());
  2995. }
  2996. if (token.is_function("counters"sv)) {
  2997. // counters() = counters( <counter-name>, <string>, <counter-style>? )
  2998. auto& function = token.function();
  2999. TokenStream function_tokens { function.value };
  3000. auto function_values = parse_a_comma_separated_list_of_component_values(function_tokens);
  3001. if (function_values.size() < 2 || function_values.size() > 3)
  3002. return nullptr;
  3003. TokenStream name_tokens { function_values[0] };
  3004. auto counter_name = parse_counter_name(name_tokens);
  3005. if (!counter_name.has_value())
  3006. return nullptr;
  3007. TokenStream string_tokens { function_values[1] };
  3008. string_tokens.discard_whitespace();
  3009. auto join_string = parse_string_value(string_tokens);
  3010. string_tokens.discard_whitespace();
  3011. if (!join_string || string_tokens.has_next_token())
  3012. return nullptr;
  3013. RefPtr<CSSStyleValue> counter_style;
  3014. if (function_values.size() > 2) {
  3015. TokenStream counter_style_tokens { function_values[2] };
  3016. counter_style = parse_counter_style(counter_style_tokens);
  3017. if (!counter_style)
  3018. return nullptr;
  3019. } else {
  3020. // In both cases, if the <counter-style> argument is omitted it defaults to `decimal`.
  3021. counter_style = CustomIdentStyleValue::create("decimal"_fly_string);
  3022. }
  3023. transaction.commit();
  3024. return CounterStyleValue::create_counters(counter_name.release_value(), join_string->string_value(), counter_style.release_nonnull());
  3025. }
  3026. return nullptr;
  3027. }
  3028. RefPtr<CSSStyleValue> Parser::parse_counter_definitions_value(TokenStream<ComponentValue>& tokens, AllowReversed allow_reversed, i32 default_value_if_not_reversed)
  3029. {
  3030. // If AllowReversed is Yes, parses:
  3031. // [ <counter-name> <integer>? | <reversed-counter-name> <integer>? ]+
  3032. // Otherwise parses:
  3033. // [ <counter-name> <integer>? ]+
  3034. // FIXME: This disabled parsing of `reversed()` counters. Remove this line once they're supported.
  3035. allow_reversed = AllowReversed::No;
  3036. auto transaction = tokens.begin_transaction();
  3037. tokens.discard_whitespace();
  3038. Vector<CounterDefinition> counter_definitions;
  3039. while (tokens.has_next_token()) {
  3040. auto per_item_transaction = tokens.begin_transaction();
  3041. CounterDefinition definition {};
  3042. // <counter-name> | <reversed-counter-name>
  3043. auto& token = tokens.consume_a_token();
  3044. if (token.is(Token::Type::Ident)) {
  3045. definition.name = token.token().ident();
  3046. definition.is_reversed = false;
  3047. } else if (allow_reversed == AllowReversed::Yes && token.is_function("reversed"sv)) {
  3048. TokenStream function_tokens { token.function().value };
  3049. function_tokens.discard_whitespace();
  3050. auto& name_token = function_tokens.consume_a_token();
  3051. if (!name_token.is(Token::Type::Ident))
  3052. break;
  3053. function_tokens.discard_whitespace();
  3054. if (function_tokens.has_next_token())
  3055. break;
  3056. definition.name = name_token.token().ident();
  3057. definition.is_reversed = true;
  3058. } else {
  3059. break;
  3060. }
  3061. tokens.discard_whitespace();
  3062. // <integer>?
  3063. definition.value = parse_integer_value(tokens);
  3064. if (!definition.value && !definition.is_reversed)
  3065. definition.value = IntegerStyleValue::create(default_value_if_not_reversed);
  3066. counter_definitions.append(move(definition));
  3067. tokens.discard_whitespace();
  3068. per_item_transaction.commit();
  3069. }
  3070. if (counter_definitions.is_empty())
  3071. return {};
  3072. transaction.commit();
  3073. return CounterDefinitionsStyleValue::create(move(counter_definitions));
  3074. }
  3075. RefPtr<CSSStyleValue> Parser::parse_ratio_value(TokenStream<ComponentValue>& tokens)
  3076. {
  3077. if (auto ratio = parse_ratio(tokens); ratio.has_value())
  3078. return RatioStyleValue::create(ratio.release_value());
  3079. return nullptr;
  3080. }
  3081. RefPtr<StringStyleValue> Parser::parse_string_value(TokenStream<ComponentValue>& tokens)
  3082. {
  3083. auto const& peek = tokens.next_token();
  3084. if (peek.is(Token::Type::String)) {
  3085. tokens.discard_a_token();
  3086. return StringStyleValue::create(peek.token().string());
  3087. }
  3088. return nullptr;
  3089. }
  3090. RefPtr<CSSStyleValue> Parser::parse_image_value(TokenStream<ComponentValue>& tokens)
  3091. {
  3092. if (auto url = parse_url_function(tokens); url.has_value())
  3093. return ImageStyleValue::create(url.value());
  3094. if (auto linear_gradient = parse_linear_gradient_function(tokens))
  3095. return linear_gradient;
  3096. if (auto conic_gradient = parse_conic_gradient_function(tokens))
  3097. return conic_gradient;
  3098. if (auto radial_gradient = parse_radial_gradient_function(tokens))
  3099. return radial_gradient;
  3100. return nullptr;
  3101. }
  3102. // https://svgwg.org/svg2-draft/painting.html#SpecifyingPaint
  3103. RefPtr<CSSStyleValue> Parser::parse_paint_value(TokenStream<ComponentValue>& tokens)
  3104. {
  3105. // `<paint> = none | <color> | <url> [none | <color>]? | context-fill | context-stroke`
  3106. auto parse_color_or_none = [&]() -> Optional<RefPtr<CSSStyleValue>> {
  3107. if (auto color = parse_color_value(tokens))
  3108. return color;
  3109. // NOTE: <color> also accepts identifiers, so we do this identifier check last.
  3110. if (tokens.next_token().is(Token::Type::Ident)) {
  3111. auto maybe_keyword = keyword_from_string(tokens.next_token().token().ident());
  3112. if (maybe_keyword.has_value()) {
  3113. // FIXME: Accept `context-fill` and `context-stroke`
  3114. switch (*maybe_keyword) {
  3115. case Keyword::None:
  3116. tokens.discard_a_token();
  3117. return CSSKeywordValue::create(*maybe_keyword);
  3118. default:
  3119. return nullptr;
  3120. }
  3121. }
  3122. }
  3123. return OptionalNone {};
  3124. };
  3125. // FIMXE: Allow context-fill/context-stroke here
  3126. if (auto color_or_none = parse_color_or_none(); color_or_none.has_value())
  3127. return *color_or_none;
  3128. if (auto url = parse_url_value(tokens)) {
  3129. tokens.discard_whitespace();
  3130. if (auto color_or_none = parse_color_or_none(); color_or_none == nullptr) {
  3131. // Fail to parse if the fallback is invalid, but otherwise ignore it.
  3132. // FIXME: Use fallback color
  3133. return nullptr;
  3134. }
  3135. return url;
  3136. }
  3137. return nullptr;
  3138. }
  3139. // https://www.w3.org/TR/css-values-4/#position
  3140. RefPtr<PositionStyleValue> Parser::parse_position_value(TokenStream<ComponentValue>& tokens, PositionParsingMode position_parsing_mode)
  3141. {
  3142. auto parse_position_edge = [](ComponentValue const& token) -> Optional<PositionEdge> {
  3143. if (!token.is(Token::Type::Ident))
  3144. return {};
  3145. auto keyword = keyword_from_string(token.token().ident());
  3146. if (!keyword.has_value())
  3147. return {};
  3148. return keyword_to_position_edge(*keyword);
  3149. };
  3150. auto parse_length_percentage = [&](ComponentValue const& token) -> Optional<LengthPercentage> {
  3151. if (token.is(Token::Type::EndOfFile))
  3152. return {};
  3153. if (auto dimension = parse_dimension(token); dimension.has_value()) {
  3154. if (dimension->is_length_percentage())
  3155. return dimension->length_percentage();
  3156. return {};
  3157. }
  3158. if (auto calc = parse_calculated_value(token); calc && calc->resolves_to_length_percentage())
  3159. return LengthPercentage { calc.release_nonnull() };
  3160. return {};
  3161. };
  3162. auto is_horizontal = [](PositionEdge edge, bool accept_center) -> bool {
  3163. switch (edge) {
  3164. case PositionEdge::Left:
  3165. case PositionEdge::Right:
  3166. return true;
  3167. case PositionEdge::Center:
  3168. return accept_center;
  3169. default:
  3170. return false;
  3171. }
  3172. };
  3173. auto is_vertical = [](PositionEdge edge, bool accept_center) -> bool {
  3174. switch (edge) {
  3175. case PositionEdge::Top:
  3176. case PositionEdge::Bottom:
  3177. return true;
  3178. case PositionEdge::Center:
  3179. return accept_center;
  3180. default:
  3181. return false;
  3182. }
  3183. };
  3184. auto make_edge_style_value = [](PositionEdge position_edge, bool is_horizontal) -> NonnullRefPtr<EdgeStyleValue> {
  3185. if (position_edge == PositionEdge::Center)
  3186. return EdgeStyleValue::create(is_horizontal ? PositionEdge::Left : PositionEdge::Top, Percentage { 50 });
  3187. return EdgeStyleValue::create(position_edge, Length::make_px(0));
  3188. };
  3189. // <position> = [
  3190. // [ left | center | right | top | bottom | <length-percentage> ]
  3191. // |
  3192. // [ left | center | right ] && [ top | center | bottom ]
  3193. // |
  3194. // [ left | center | right | <length-percentage> ]
  3195. // [ top | center | bottom | <length-percentage> ]
  3196. // |
  3197. // [ [ left | right ] <length-percentage> ] &&
  3198. // [ [ top | bottom ] <length-percentage> ]
  3199. // ]
  3200. // [ left | center | right | top | bottom | <length-percentage> ]
  3201. auto alternative_1 = [&]() -> RefPtr<PositionStyleValue> {
  3202. auto transaction = tokens.begin_transaction();
  3203. tokens.discard_whitespace();
  3204. auto const& token = tokens.consume_a_token();
  3205. // [ left | center | right | top | bottom ]
  3206. if (auto maybe_edge = parse_position_edge(token); maybe_edge.has_value()) {
  3207. auto edge = maybe_edge.release_value();
  3208. transaction.commit();
  3209. // [ left | right ]
  3210. if (is_horizontal(edge, false))
  3211. return PositionStyleValue::create(make_edge_style_value(edge, true), make_edge_style_value(PositionEdge::Center, false));
  3212. // [ top | bottom ]
  3213. if (is_vertical(edge, false))
  3214. return PositionStyleValue::create(make_edge_style_value(PositionEdge::Center, true), make_edge_style_value(edge, false));
  3215. // [ center ]
  3216. VERIFY(edge == PositionEdge::Center);
  3217. return PositionStyleValue::create(make_edge_style_value(PositionEdge::Center, true), make_edge_style_value(PositionEdge::Center, false));
  3218. }
  3219. // [ <length-percentage> ]
  3220. if (auto maybe_percentage = parse_length_percentage(token); maybe_percentage.has_value()) {
  3221. transaction.commit();
  3222. return PositionStyleValue::create(EdgeStyleValue::create(PositionEdge::Left, *maybe_percentage), make_edge_style_value(PositionEdge::Center, false));
  3223. }
  3224. return nullptr;
  3225. };
  3226. // [ left | center | right ] && [ top | center | bottom ]
  3227. auto alternative_2 = [&]() -> RefPtr<PositionStyleValue> {
  3228. auto transaction = tokens.begin_transaction();
  3229. tokens.discard_whitespace();
  3230. // Parse out two position edges
  3231. auto maybe_first_edge = parse_position_edge(tokens.consume_a_token());
  3232. if (!maybe_first_edge.has_value())
  3233. return nullptr;
  3234. auto first_edge = maybe_first_edge.release_value();
  3235. tokens.discard_whitespace();
  3236. auto maybe_second_edge = parse_position_edge(tokens.consume_a_token());
  3237. if (!maybe_second_edge.has_value())
  3238. return nullptr;
  3239. auto second_edge = maybe_second_edge.release_value();
  3240. // If 'left' or 'right' is given, that position is X and the other is Y.
  3241. // Conversely -
  3242. // If 'top' or 'bottom' is given, that position is Y and the other is X.
  3243. if (is_vertical(first_edge, false) || is_horizontal(second_edge, false))
  3244. swap(first_edge, second_edge);
  3245. // [ left | center | right ] [ top | bottom | center ]
  3246. if (is_horizontal(first_edge, true) && is_vertical(second_edge, true)) {
  3247. transaction.commit();
  3248. return PositionStyleValue::create(make_edge_style_value(first_edge, true), make_edge_style_value(second_edge, false));
  3249. }
  3250. return nullptr;
  3251. };
  3252. // [ left | center | right | <length-percentage> ]
  3253. // [ top | center | bottom | <length-percentage> ]
  3254. auto alternative_3 = [&]() -> RefPtr<PositionStyleValue> {
  3255. auto transaction = tokens.begin_transaction();
  3256. auto parse_position_or_length = [&](bool as_horizontal) -> RefPtr<EdgeStyleValue> {
  3257. tokens.discard_whitespace();
  3258. auto const& token = tokens.consume_a_token();
  3259. if (auto maybe_position = parse_position_edge(token); maybe_position.has_value()) {
  3260. auto position = maybe_position.release_value();
  3261. bool valid = as_horizontal ? is_horizontal(position, true) : is_vertical(position, true);
  3262. if (!valid)
  3263. return nullptr;
  3264. return make_edge_style_value(position, as_horizontal);
  3265. }
  3266. auto maybe_length = parse_length_percentage(token);
  3267. if (!maybe_length.has_value())
  3268. return nullptr;
  3269. return EdgeStyleValue::create(as_horizontal ? PositionEdge::Left : PositionEdge::Top, maybe_length.release_value());
  3270. };
  3271. // [ left | center | right | <length-percentage> ]
  3272. auto horizontal_edge = parse_position_or_length(true);
  3273. if (!horizontal_edge)
  3274. return nullptr;
  3275. // [ top | center | bottom | <length-percentage> ]
  3276. auto vertical_edge = parse_position_or_length(false);
  3277. if (!vertical_edge)
  3278. return nullptr;
  3279. transaction.commit();
  3280. return PositionStyleValue::create(horizontal_edge.release_nonnull(), vertical_edge.release_nonnull());
  3281. };
  3282. // [ [ left | right ] <length-percentage> ] &&
  3283. // [ [ top | bottom ] <length-percentage> ]
  3284. auto alternative_4 = [&]() -> RefPtr<PositionStyleValue> {
  3285. struct PositionAndLength {
  3286. PositionEdge position;
  3287. LengthPercentage length;
  3288. };
  3289. auto parse_position_and_length = [&]() -> Optional<PositionAndLength> {
  3290. tokens.discard_whitespace();
  3291. auto maybe_position = parse_position_edge(tokens.consume_a_token());
  3292. if (!maybe_position.has_value())
  3293. return {};
  3294. tokens.discard_whitespace();
  3295. auto maybe_length = parse_length_percentage(tokens.consume_a_token());
  3296. if (!maybe_length.has_value())
  3297. return {};
  3298. return PositionAndLength {
  3299. .position = maybe_position.release_value(),
  3300. .length = maybe_length.release_value(),
  3301. };
  3302. };
  3303. auto transaction = tokens.begin_transaction();
  3304. auto maybe_group1 = parse_position_and_length();
  3305. if (!maybe_group1.has_value())
  3306. return nullptr;
  3307. auto maybe_group2 = parse_position_and_length();
  3308. if (!maybe_group2.has_value())
  3309. return nullptr;
  3310. auto group1 = maybe_group1.release_value();
  3311. auto group2 = maybe_group2.release_value();
  3312. // [ [ left | right ] <length-percentage> ] [ [ top | bottom ] <length-percentage> ]
  3313. if (is_horizontal(group1.position, false) && is_vertical(group2.position, false)) {
  3314. transaction.commit();
  3315. return PositionStyleValue::create(EdgeStyleValue::create(group1.position, group1.length), EdgeStyleValue::create(group2.position, group2.length));
  3316. }
  3317. // [ [ top | bottom ] <length-percentage> ] [ [ left | right ] <length-percentage> ]
  3318. if (is_vertical(group1.position, false) && is_horizontal(group2.position, false)) {
  3319. transaction.commit();
  3320. return PositionStyleValue::create(EdgeStyleValue::create(group2.position, group2.length), EdgeStyleValue::create(group1.position, group1.length));
  3321. }
  3322. return nullptr;
  3323. };
  3324. // The extra 3-value syntax that's allowed for background-position:
  3325. // [ center | [ left | right ] <length-percentage>? ] &&
  3326. // [ center | [ top | bottom ] <length-percentage>? ]
  3327. auto alternative_5_for_background_position = [&]() -> RefPtr<PositionStyleValue> {
  3328. auto transaction = tokens.begin_transaction();
  3329. struct PositionAndMaybeLength {
  3330. PositionEdge position;
  3331. Optional<LengthPercentage> length;
  3332. };
  3333. // [ <position> <length-percentage>? ]
  3334. auto parse_position_and_maybe_length = [&]() -> Optional<PositionAndMaybeLength> {
  3335. tokens.discard_whitespace();
  3336. auto maybe_position = parse_position_edge(tokens.consume_a_token());
  3337. if (!maybe_position.has_value())
  3338. return {};
  3339. tokens.discard_whitespace();
  3340. auto maybe_length = parse_length_percentage(tokens.next_token());
  3341. if (maybe_length.has_value()) {
  3342. // 'center' cannot be followed by a <length-percentage>
  3343. if (maybe_position.value() == PositionEdge::Center && maybe_length.has_value())
  3344. return {};
  3345. tokens.discard_a_token();
  3346. }
  3347. return PositionAndMaybeLength {
  3348. .position = maybe_position.release_value(),
  3349. .length = move(maybe_length),
  3350. };
  3351. };
  3352. auto maybe_group1 = parse_position_and_maybe_length();
  3353. if (!maybe_group1.has_value())
  3354. return nullptr;
  3355. auto maybe_group2 = parse_position_and_maybe_length();
  3356. if (!maybe_group2.has_value())
  3357. return nullptr;
  3358. auto group1 = maybe_group1.release_value();
  3359. auto group2 = maybe_group2.release_value();
  3360. // 2-value or 4-value if both <length-percentage>s are present or missing.
  3361. if (group1.length.has_value() == group2.length.has_value())
  3362. return nullptr;
  3363. // If 'left' or 'right' is given, that position is X and the other is Y.
  3364. // Conversely -
  3365. // If 'top' or 'bottom' is given, that position is Y and the other is X.
  3366. if (is_vertical(group1.position, false) || is_horizontal(group2.position, false))
  3367. swap(group1, group2);
  3368. // [ center | [ left | right ] ]
  3369. if (!is_horizontal(group1.position, true))
  3370. return nullptr;
  3371. // [ center | [ top | bottom ] ]
  3372. if (!is_vertical(group2.position, true))
  3373. return nullptr;
  3374. auto to_style_value = [&](PositionAndMaybeLength const& group, bool is_horizontal) -> NonnullRefPtr<EdgeStyleValue> {
  3375. if (group.position == PositionEdge::Center)
  3376. return EdgeStyleValue::create(is_horizontal ? PositionEdge::Left : PositionEdge::Top, Percentage { 50 });
  3377. return EdgeStyleValue::create(group.position, group.length.value_or(Length::make_px(0)));
  3378. };
  3379. transaction.commit();
  3380. return PositionStyleValue::create(to_style_value(group1, true), to_style_value(group2, false));
  3381. };
  3382. // Note: The alternatives must be attempted in this order since shorter alternatives can match a prefix of longer ones.
  3383. if (auto position = alternative_4())
  3384. return position;
  3385. if (position_parsing_mode == PositionParsingMode::BackgroundPosition) {
  3386. if (auto position = alternative_5_for_background_position())
  3387. return position;
  3388. }
  3389. if (auto position = alternative_3())
  3390. return position;
  3391. if (auto position = alternative_2())
  3392. return position;
  3393. if (auto position = alternative_1())
  3394. return position;
  3395. return nullptr;
  3396. }
  3397. template<typename ParseFunction>
  3398. RefPtr<CSSStyleValue> Parser::parse_comma_separated_value_list(TokenStream<ComponentValue>& tokens, ParseFunction parse_one_value)
  3399. {
  3400. auto first = parse_one_value(tokens);
  3401. if (!first || !tokens.has_next_token())
  3402. return first;
  3403. StyleValueVector values;
  3404. values.append(first.release_nonnull());
  3405. while (tokens.has_next_token()) {
  3406. if (!tokens.consume_a_token().is(Token::Type::Comma))
  3407. return nullptr;
  3408. if (auto maybe_value = parse_one_value(tokens)) {
  3409. values.append(maybe_value.release_nonnull());
  3410. continue;
  3411. }
  3412. return nullptr;
  3413. }
  3414. return StyleValueList::create(move(values), StyleValueList::Separator::Comma);
  3415. }
  3416. RefPtr<CSSStyleValue> Parser::parse_simple_comma_separated_value_list(PropertyID property_id, TokenStream<ComponentValue>& tokens)
  3417. {
  3418. return parse_comma_separated_value_list(tokens, [this, property_id](auto& tokens) -> RefPtr<CSSStyleValue> {
  3419. if (auto value = parse_css_value_for_property(property_id, tokens))
  3420. return value;
  3421. tokens.reconsume_current_input_token();
  3422. return nullptr;
  3423. });
  3424. }
  3425. RefPtr<CSSStyleValue> Parser::parse_all_as_single_keyword_value(TokenStream<ComponentValue>& tokens, Keyword keyword)
  3426. {
  3427. auto transaction = tokens.begin_transaction();
  3428. tokens.discard_whitespace();
  3429. auto keyword_value = parse_keyword_value(tokens);
  3430. tokens.discard_whitespace();
  3431. if (tokens.has_next_token() || !keyword_value || keyword_value->to_keyword() != keyword)
  3432. return {};
  3433. transaction.commit();
  3434. return keyword_value;
  3435. }
  3436. static void remove_property(Vector<PropertyID>& properties, PropertyID property_to_remove)
  3437. {
  3438. properties.remove_first_matching([&](auto it) { return it == property_to_remove; });
  3439. }
  3440. // https://www.w3.org/TR/css-sizing-4/#aspect-ratio
  3441. RefPtr<CSSStyleValue> Parser::parse_aspect_ratio_value(TokenStream<ComponentValue>& tokens)
  3442. {
  3443. // `auto || <ratio>`
  3444. RefPtr<CSSStyleValue> auto_value;
  3445. RefPtr<CSSStyleValue> ratio_value;
  3446. auto transaction = tokens.begin_transaction();
  3447. while (tokens.has_next_token()) {
  3448. auto maybe_value = parse_css_value_for_property(PropertyID::AspectRatio, tokens);
  3449. if (!maybe_value)
  3450. return nullptr;
  3451. if (maybe_value->is_ratio()) {
  3452. if (ratio_value)
  3453. return nullptr;
  3454. ratio_value = maybe_value.release_nonnull();
  3455. continue;
  3456. }
  3457. if (maybe_value->is_keyword() && maybe_value->as_keyword().keyword() == Keyword::Auto) {
  3458. if (auto_value)
  3459. return nullptr;
  3460. auto_value = maybe_value.release_nonnull();
  3461. continue;
  3462. }
  3463. return nullptr;
  3464. }
  3465. if (auto_value && ratio_value) {
  3466. transaction.commit();
  3467. return StyleValueList::create(
  3468. StyleValueVector { auto_value.release_nonnull(), ratio_value.release_nonnull() },
  3469. StyleValueList::Separator::Space);
  3470. }
  3471. if (ratio_value) {
  3472. transaction.commit();
  3473. return ratio_value.release_nonnull();
  3474. }
  3475. if (auto_value) {
  3476. transaction.commit();
  3477. return auto_value.release_nonnull();
  3478. }
  3479. return nullptr;
  3480. }
  3481. RefPtr<CSSStyleValue> Parser::parse_background_value(TokenStream<ComponentValue>& tokens)
  3482. {
  3483. auto transaction = tokens.begin_transaction();
  3484. auto make_background_shorthand = [&](auto background_color, auto background_image, auto background_position, auto background_size, auto background_repeat, auto background_attachment, auto background_origin, auto background_clip) {
  3485. return ShorthandStyleValue::create(PropertyID::Background,
  3486. { PropertyID::BackgroundColor, PropertyID::BackgroundImage, PropertyID::BackgroundPosition, PropertyID::BackgroundSize, PropertyID::BackgroundRepeat, PropertyID::BackgroundAttachment, PropertyID::BackgroundOrigin, PropertyID::BackgroundClip },
  3487. { move(background_color), move(background_image), move(background_position), move(background_size), move(background_repeat), move(background_attachment), move(background_origin), move(background_clip) });
  3488. };
  3489. StyleValueVector background_images;
  3490. StyleValueVector background_positions;
  3491. StyleValueVector background_sizes;
  3492. StyleValueVector background_repeats;
  3493. StyleValueVector background_attachments;
  3494. StyleValueVector background_clips;
  3495. StyleValueVector background_origins;
  3496. RefPtr<CSSStyleValue> background_color;
  3497. auto initial_background_image = property_initial_value(m_context.realm(), PropertyID::BackgroundImage);
  3498. auto initial_background_position = property_initial_value(m_context.realm(), PropertyID::BackgroundPosition);
  3499. auto initial_background_size = property_initial_value(m_context.realm(), PropertyID::BackgroundSize);
  3500. auto initial_background_repeat = property_initial_value(m_context.realm(), PropertyID::BackgroundRepeat);
  3501. auto initial_background_attachment = property_initial_value(m_context.realm(), PropertyID::BackgroundAttachment);
  3502. auto initial_background_clip = property_initial_value(m_context.realm(), PropertyID::BackgroundClip);
  3503. auto initial_background_origin = property_initial_value(m_context.realm(), PropertyID::BackgroundOrigin);
  3504. auto initial_background_color = property_initial_value(m_context.realm(), PropertyID::BackgroundColor);
  3505. // Per-layer values
  3506. RefPtr<CSSStyleValue> background_image;
  3507. RefPtr<CSSStyleValue> background_position;
  3508. RefPtr<CSSStyleValue> background_size;
  3509. RefPtr<CSSStyleValue> background_repeat;
  3510. RefPtr<CSSStyleValue> background_attachment;
  3511. RefPtr<CSSStyleValue> background_clip;
  3512. RefPtr<CSSStyleValue> background_origin;
  3513. bool has_multiple_layers = false;
  3514. // BackgroundSize is always parsed as part of BackgroundPosition, so we don't include it here.
  3515. Vector<PropertyID> remaining_layer_properties {
  3516. PropertyID::BackgroundAttachment,
  3517. PropertyID::BackgroundClip,
  3518. PropertyID::BackgroundColor,
  3519. PropertyID::BackgroundImage,
  3520. PropertyID::BackgroundOrigin,
  3521. PropertyID::BackgroundPosition,
  3522. PropertyID::BackgroundRepeat,
  3523. };
  3524. auto background_layer_is_valid = [&](bool allow_background_color) -> bool {
  3525. if (allow_background_color) {
  3526. if (background_color)
  3527. return true;
  3528. } else {
  3529. if (background_color)
  3530. return false;
  3531. }
  3532. return background_image || background_position || background_size || background_repeat || background_attachment || background_clip || background_origin;
  3533. };
  3534. auto complete_background_layer = [&]() {
  3535. background_images.append(background_image ? background_image.release_nonnull() : initial_background_image);
  3536. background_positions.append(background_position ? background_position.release_nonnull() : initial_background_position);
  3537. background_sizes.append(background_size ? background_size.release_nonnull() : initial_background_size);
  3538. background_repeats.append(background_repeat ? background_repeat.release_nonnull() : initial_background_repeat);
  3539. background_attachments.append(background_attachment ? background_attachment.release_nonnull() : initial_background_attachment);
  3540. if (!background_origin && !background_clip) {
  3541. background_origin = initial_background_origin;
  3542. background_clip = initial_background_clip;
  3543. } else if (!background_clip) {
  3544. background_clip = background_origin;
  3545. }
  3546. background_origins.append(background_origin.release_nonnull());
  3547. background_clips.append(background_clip.release_nonnull());
  3548. background_image = nullptr;
  3549. background_position = nullptr;
  3550. background_size = nullptr;
  3551. background_repeat = nullptr;
  3552. background_attachment = nullptr;
  3553. background_clip = nullptr;
  3554. background_origin = nullptr;
  3555. remaining_layer_properties.clear_with_capacity();
  3556. remaining_layer_properties.unchecked_append(PropertyID::BackgroundAttachment);
  3557. remaining_layer_properties.unchecked_append(PropertyID::BackgroundClip);
  3558. remaining_layer_properties.unchecked_append(PropertyID::BackgroundColor);
  3559. remaining_layer_properties.unchecked_append(PropertyID::BackgroundImage);
  3560. remaining_layer_properties.unchecked_append(PropertyID::BackgroundOrigin);
  3561. remaining_layer_properties.unchecked_append(PropertyID::BackgroundPosition);
  3562. remaining_layer_properties.unchecked_append(PropertyID::BackgroundRepeat);
  3563. };
  3564. while (tokens.has_next_token()) {
  3565. if (tokens.next_token().is(Token::Type::Comma)) {
  3566. has_multiple_layers = true;
  3567. if (!background_layer_is_valid(false))
  3568. return nullptr;
  3569. complete_background_layer();
  3570. tokens.discard_a_token();
  3571. continue;
  3572. }
  3573. auto value_and_property = parse_css_value_for_properties(remaining_layer_properties, tokens);
  3574. if (!value_and_property.has_value())
  3575. return nullptr;
  3576. auto& value = value_and_property->style_value;
  3577. remove_property(remaining_layer_properties, value_and_property->property);
  3578. switch (value_and_property->property) {
  3579. case PropertyID::BackgroundAttachment:
  3580. VERIFY(!background_attachment);
  3581. background_attachment = value.release_nonnull();
  3582. continue;
  3583. case PropertyID::BackgroundColor:
  3584. VERIFY(!background_color);
  3585. background_color = value.release_nonnull();
  3586. continue;
  3587. case PropertyID::BackgroundImage:
  3588. VERIFY(!background_image);
  3589. background_image = value.release_nonnull();
  3590. continue;
  3591. case PropertyID::BackgroundClip:
  3592. case PropertyID::BackgroundOrigin: {
  3593. // background-origin and background-clip accept the same values. From the spec:
  3594. // "If one <box> value is present then it sets both background-origin and background-clip to that value.
  3595. // If two values are present, then the first sets background-origin and the second background-clip."
  3596. // - https://www.w3.org/TR/css-backgrounds-3/#background
  3597. // So, we put the first one in background-origin, then if we get a second, we put it in background-clip.
  3598. // If we only get one, we copy the value before creating the ShorthandStyleValue.
  3599. if (!background_origin) {
  3600. background_origin = value.release_nonnull();
  3601. } else if (!background_clip) {
  3602. background_clip = value.release_nonnull();
  3603. } else {
  3604. VERIFY_NOT_REACHED();
  3605. }
  3606. continue;
  3607. }
  3608. case PropertyID::BackgroundPosition: {
  3609. VERIFY(!background_position);
  3610. background_position = value.release_nonnull();
  3611. // Attempt to parse `/ <background-size>`
  3612. auto background_size_transaction = tokens.begin_transaction();
  3613. auto& maybe_slash = tokens.consume_a_token();
  3614. if (maybe_slash.is_delim('/')) {
  3615. if (auto maybe_background_size = parse_single_background_size_value(tokens)) {
  3616. background_size_transaction.commit();
  3617. background_size = maybe_background_size.release_nonnull();
  3618. continue;
  3619. }
  3620. return nullptr;
  3621. }
  3622. continue;
  3623. }
  3624. case PropertyID::BackgroundRepeat: {
  3625. VERIFY(!background_repeat);
  3626. tokens.reconsume_current_input_token();
  3627. if (auto maybe_repeat = parse_single_background_repeat_value(tokens)) {
  3628. background_repeat = maybe_repeat.release_nonnull();
  3629. continue;
  3630. }
  3631. return nullptr;
  3632. }
  3633. default:
  3634. VERIFY_NOT_REACHED();
  3635. }
  3636. return nullptr;
  3637. }
  3638. if (!background_layer_is_valid(true))
  3639. return nullptr;
  3640. // We only need to create StyleValueLists if there are multiple layers.
  3641. // Otherwise, we can pass the single StyleValues directly.
  3642. if (has_multiple_layers) {
  3643. complete_background_layer();
  3644. if (!background_color)
  3645. background_color = initial_background_color;
  3646. transaction.commit();
  3647. return make_background_shorthand(
  3648. background_color.release_nonnull(),
  3649. StyleValueList::create(move(background_images), StyleValueList::Separator::Comma),
  3650. StyleValueList::create(move(background_positions), StyleValueList::Separator::Comma),
  3651. StyleValueList::create(move(background_sizes), StyleValueList::Separator::Comma),
  3652. StyleValueList::create(move(background_repeats), StyleValueList::Separator::Comma),
  3653. StyleValueList::create(move(background_attachments), StyleValueList::Separator::Comma),
  3654. StyleValueList::create(move(background_origins), StyleValueList::Separator::Comma),
  3655. StyleValueList::create(move(background_clips), StyleValueList::Separator::Comma));
  3656. }
  3657. if (!background_color)
  3658. background_color = initial_background_color;
  3659. if (!background_image)
  3660. background_image = initial_background_image;
  3661. if (!background_position)
  3662. background_position = initial_background_position;
  3663. if (!background_size)
  3664. background_size = initial_background_size;
  3665. if (!background_repeat)
  3666. background_repeat = initial_background_repeat;
  3667. if (!background_attachment)
  3668. background_attachment = initial_background_attachment;
  3669. if (!background_origin && !background_clip) {
  3670. background_origin = initial_background_origin;
  3671. background_clip = initial_background_clip;
  3672. } else if (!background_clip) {
  3673. background_clip = background_origin;
  3674. }
  3675. transaction.commit();
  3676. return make_background_shorthand(
  3677. background_color.release_nonnull(),
  3678. background_image.release_nonnull(),
  3679. background_position.release_nonnull(),
  3680. background_size.release_nonnull(),
  3681. background_repeat.release_nonnull(),
  3682. background_attachment.release_nonnull(),
  3683. background_origin.release_nonnull(),
  3684. background_clip.release_nonnull());
  3685. }
  3686. static Optional<LengthPercentage> style_value_to_length_percentage(auto value)
  3687. {
  3688. if (value->is_percentage())
  3689. return LengthPercentage { value->as_percentage().percentage() };
  3690. if (value->is_length())
  3691. return LengthPercentage { value->as_length().length() };
  3692. if (value->is_math())
  3693. return LengthPercentage { value->as_math() };
  3694. return {};
  3695. }
  3696. RefPtr<CSSStyleValue> Parser::parse_single_background_position_x_or_y_value(TokenStream<ComponentValue>& tokens, PropertyID property)
  3697. {
  3698. PositionEdge relative_edge {};
  3699. if (property == PropertyID::BackgroundPositionX) {
  3700. // [ center | [ [ left | right | x-start | x-end ]? <length-percentage>? ]! ]#
  3701. relative_edge = PositionEdge::Left;
  3702. } else if (property == PropertyID::BackgroundPositionY) {
  3703. // [ center | [ [ top | bottom | y-start | y-end ]? <length-percentage>? ]! ]#
  3704. relative_edge = PositionEdge::Top;
  3705. } else {
  3706. VERIFY_NOT_REACHED();
  3707. }
  3708. auto transaction = tokens.begin_transaction();
  3709. if (!tokens.has_next_token())
  3710. return nullptr;
  3711. auto value = parse_css_value_for_property(property, tokens);
  3712. if (!value)
  3713. return nullptr;
  3714. if (value->is_keyword()) {
  3715. auto keyword = value->to_keyword();
  3716. if (keyword == Keyword::Center) {
  3717. transaction.commit();
  3718. return EdgeStyleValue::create(relative_edge, Percentage { 50 });
  3719. }
  3720. if (auto edge = keyword_to_position_edge(keyword); edge.has_value()) {
  3721. relative_edge = *edge;
  3722. } else {
  3723. return nullptr;
  3724. }
  3725. if (tokens.has_next_token()) {
  3726. value = parse_css_value_for_property(property, tokens);
  3727. if (!value) {
  3728. transaction.commit();
  3729. return EdgeStyleValue::create(relative_edge, Length::make_px(0));
  3730. }
  3731. }
  3732. }
  3733. auto offset = style_value_to_length_percentage(value);
  3734. if (offset.has_value()) {
  3735. transaction.commit();
  3736. return EdgeStyleValue::create(relative_edge, *offset);
  3737. }
  3738. // If no offset is provided create this element but with an offset of default value of zero
  3739. transaction.commit();
  3740. return EdgeStyleValue::create(relative_edge, Length::make_px(0));
  3741. }
  3742. RefPtr<CSSStyleValue> Parser::parse_single_background_repeat_value(TokenStream<ComponentValue>& tokens)
  3743. {
  3744. auto transaction = tokens.begin_transaction();
  3745. auto is_directional_repeat = [](CSSStyleValue const& value) -> bool {
  3746. auto keyword = value.to_keyword();
  3747. return keyword == Keyword::RepeatX || keyword == Keyword::RepeatY;
  3748. };
  3749. auto as_repeat = [](Keyword keyword) -> Optional<Repeat> {
  3750. switch (keyword) {
  3751. case Keyword::NoRepeat:
  3752. return Repeat::NoRepeat;
  3753. case Keyword::Repeat:
  3754. return Repeat::Repeat;
  3755. case Keyword::Round:
  3756. return Repeat::Round;
  3757. case Keyword::Space:
  3758. return Repeat::Space;
  3759. default:
  3760. return {};
  3761. }
  3762. };
  3763. auto maybe_x_value = parse_css_value_for_property(PropertyID::BackgroundRepeat, tokens);
  3764. if (!maybe_x_value)
  3765. return nullptr;
  3766. auto x_value = maybe_x_value.release_nonnull();
  3767. if (is_directional_repeat(*x_value)) {
  3768. auto keyword = x_value->to_keyword();
  3769. transaction.commit();
  3770. return BackgroundRepeatStyleValue::create(
  3771. keyword == Keyword::RepeatX ? Repeat::Repeat : Repeat::NoRepeat,
  3772. keyword == Keyword::RepeatX ? Repeat::NoRepeat : Repeat::Repeat);
  3773. }
  3774. auto x_repeat = as_repeat(x_value->to_keyword());
  3775. if (!x_repeat.has_value())
  3776. return nullptr;
  3777. // See if we have a second value for Y
  3778. auto maybe_y_value = parse_css_value_for_property(PropertyID::BackgroundRepeat, tokens);
  3779. if (!maybe_y_value) {
  3780. // We don't have a second value, so use x for both
  3781. transaction.commit();
  3782. return BackgroundRepeatStyleValue::create(x_repeat.value(), x_repeat.value());
  3783. }
  3784. auto y_value = maybe_y_value.release_nonnull();
  3785. if (is_directional_repeat(*y_value))
  3786. return nullptr;
  3787. auto y_repeat = as_repeat(y_value->to_keyword());
  3788. if (!y_repeat.has_value())
  3789. return nullptr;
  3790. transaction.commit();
  3791. return BackgroundRepeatStyleValue::create(x_repeat.value(), y_repeat.value());
  3792. }
  3793. RefPtr<CSSStyleValue> Parser::parse_single_background_size_value(TokenStream<ComponentValue>& tokens)
  3794. {
  3795. auto transaction = tokens.begin_transaction();
  3796. auto get_length_percentage = [](CSSStyleValue& style_value) -> Optional<LengthPercentage> {
  3797. if (style_value.has_auto())
  3798. return LengthPercentage { Length::make_auto() };
  3799. if (style_value.is_percentage())
  3800. return LengthPercentage { style_value.as_percentage().percentage() };
  3801. if (style_value.is_length())
  3802. return LengthPercentage { style_value.as_length().length() };
  3803. if (style_value.is_math())
  3804. return LengthPercentage { style_value.as_math() };
  3805. return {};
  3806. };
  3807. auto maybe_x_value = parse_css_value_for_property(PropertyID::BackgroundSize, tokens);
  3808. if (!maybe_x_value)
  3809. return nullptr;
  3810. auto x_value = maybe_x_value.release_nonnull();
  3811. if (x_value->to_keyword() == Keyword::Cover || x_value->to_keyword() == Keyword::Contain) {
  3812. transaction.commit();
  3813. return x_value;
  3814. }
  3815. auto maybe_y_value = parse_css_value_for_property(PropertyID::BackgroundSize, tokens);
  3816. if (!maybe_y_value) {
  3817. auto y_value = LengthPercentage { Length::make_auto() };
  3818. auto x_size = get_length_percentage(*x_value);
  3819. if (!x_size.has_value())
  3820. return nullptr;
  3821. transaction.commit();
  3822. return BackgroundSizeStyleValue::create(x_size.value(), y_value);
  3823. }
  3824. auto y_value = maybe_y_value.release_nonnull();
  3825. auto x_size = get_length_percentage(*x_value);
  3826. auto y_size = get_length_percentage(*y_value);
  3827. if (!x_size.has_value() || !y_size.has_value())
  3828. return nullptr;
  3829. transaction.commit();
  3830. return BackgroundSizeStyleValue::create(x_size.release_value(), y_size.release_value());
  3831. }
  3832. RefPtr<CSSStyleValue> Parser::parse_border_value(PropertyID property_id, TokenStream<ComponentValue>& tokens)
  3833. {
  3834. RefPtr<CSSStyleValue> border_width;
  3835. RefPtr<CSSStyleValue> border_color;
  3836. RefPtr<CSSStyleValue> border_style;
  3837. auto color_property = PropertyID::Invalid;
  3838. auto style_property = PropertyID::Invalid;
  3839. auto width_property = PropertyID::Invalid;
  3840. switch (property_id) {
  3841. case PropertyID::Border:
  3842. color_property = PropertyID::BorderColor;
  3843. style_property = PropertyID::BorderStyle;
  3844. width_property = PropertyID::BorderWidth;
  3845. break;
  3846. case PropertyID::BorderBottom:
  3847. color_property = PropertyID::BorderBottomColor;
  3848. style_property = PropertyID::BorderBottomStyle;
  3849. width_property = PropertyID::BorderBottomWidth;
  3850. break;
  3851. case PropertyID::BorderLeft:
  3852. color_property = PropertyID::BorderLeftColor;
  3853. style_property = PropertyID::BorderLeftStyle;
  3854. width_property = PropertyID::BorderLeftWidth;
  3855. break;
  3856. case PropertyID::BorderRight:
  3857. color_property = PropertyID::BorderRightColor;
  3858. style_property = PropertyID::BorderRightStyle;
  3859. width_property = PropertyID::BorderRightWidth;
  3860. break;
  3861. case PropertyID::BorderTop:
  3862. color_property = PropertyID::BorderTopColor;
  3863. style_property = PropertyID::BorderTopStyle;
  3864. width_property = PropertyID::BorderTopWidth;
  3865. break;
  3866. default:
  3867. VERIFY_NOT_REACHED();
  3868. }
  3869. auto remaining_longhands = Vector { width_property, color_property, style_property };
  3870. auto transaction = tokens.begin_transaction();
  3871. while (tokens.has_next_token()) {
  3872. auto property_and_value = parse_css_value_for_properties(remaining_longhands, tokens);
  3873. if (!property_and_value.has_value())
  3874. return nullptr;
  3875. auto& value = property_and_value->style_value;
  3876. remove_property(remaining_longhands, property_and_value->property);
  3877. if (property_and_value->property == width_property) {
  3878. VERIFY(!border_width);
  3879. border_width = value.release_nonnull();
  3880. } else if (property_and_value->property == color_property) {
  3881. VERIFY(!border_color);
  3882. border_color = value.release_nonnull();
  3883. } else if (property_and_value->property == style_property) {
  3884. VERIFY(!border_style);
  3885. border_style = value.release_nonnull();
  3886. } else {
  3887. VERIFY_NOT_REACHED();
  3888. }
  3889. }
  3890. if (!border_width)
  3891. border_width = property_initial_value(m_context.realm(), width_property);
  3892. if (!border_style)
  3893. border_style = property_initial_value(m_context.realm(), style_property);
  3894. if (!border_color)
  3895. border_color = property_initial_value(m_context.realm(), color_property);
  3896. transaction.commit();
  3897. return ShorthandStyleValue::create(property_id,
  3898. { width_property, style_property, color_property },
  3899. { border_width.release_nonnull(), border_style.release_nonnull(), border_color.release_nonnull() });
  3900. }
  3901. RefPtr<CSSStyleValue> Parser::parse_border_radius_value(TokenStream<ComponentValue>& tokens)
  3902. {
  3903. if (tokens.remaining_token_count() == 2) {
  3904. auto transaction = tokens.begin_transaction();
  3905. auto horizontal = parse_length_percentage(tokens);
  3906. auto vertical = parse_length_percentage(tokens);
  3907. if (horizontal.has_value() && vertical.has_value()) {
  3908. transaction.commit();
  3909. return BorderRadiusStyleValue::create(horizontal.release_value(), vertical.release_value());
  3910. }
  3911. }
  3912. if (tokens.remaining_token_count() == 1) {
  3913. auto transaction = tokens.begin_transaction();
  3914. auto radius = parse_length_percentage(tokens);
  3915. if (radius.has_value()) {
  3916. transaction.commit();
  3917. return BorderRadiusStyleValue::create(radius.value(), radius.value());
  3918. }
  3919. }
  3920. return nullptr;
  3921. }
  3922. RefPtr<CSSStyleValue> Parser::parse_border_radius_shorthand_value(TokenStream<ComponentValue>& tokens)
  3923. {
  3924. auto top_left = [&](Vector<LengthPercentage>& radii) { return radii[0]; };
  3925. auto top_right = [&](Vector<LengthPercentage>& radii) {
  3926. switch (radii.size()) {
  3927. case 4:
  3928. case 3:
  3929. case 2:
  3930. return radii[1];
  3931. case 1:
  3932. return radii[0];
  3933. default:
  3934. VERIFY_NOT_REACHED();
  3935. }
  3936. };
  3937. auto bottom_right = [&](Vector<LengthPercentage>& radii) {
  3938. switch (radii.size()) {
  3939. case 4:
  3940. case 3:
  3941. return radii[2];
  3942. case 2:
  3943. case 1:
  3944. return radii[0];
  3945. default:
  3946. VERIFY_NOT_REACHED();
  3947. }
  3948. };
  3949. auto bottom_left = [&](Vector<LengthPercentage>& radii) {
  3950. switch (radii.size()) {
  3951. case 4:
  3952. return radii[3];
  3953. case 3:
  3954. case 2:
  3955. return radii[1];
  3956. case 1:
  3957. return radii[0];
  3958. default:
  3959. VERIFY_NOT_REACHED();
  3960. }
  3961. };
  3962. Vector<LengthPercentage> horizontal_radii;
  3963. Vector<LengthPercentage> vertical_radii;
  3964. bool reading_vertical = false;
  3965. auto transaction = tokens.begin_transaction();
  3966. while (tokens.has_next_token()) {
  3967. if (tokens.next_token().is_delim('/')) {
  3968. if (reading_vertical || horizontal_radii.is_empty())
  3969. return nullptr;
  3970. reading_vertical = true;
  3971. tokens.discard_a_token(); // `/`
  3972. continue;
  3973. }
  3974. auto maybe_dimension = parse_length_percentage(tokens);
  3975. if (!maybe_dimension.has_value())
  3976. return nullptr;
  3977. if (reading_vertical) {
  3978. vertical_radii.append(maybe_dimension.release_value());
  3979. } else {
  3980. horizontal_radii.append(maybe_dimension.release_value());
  3981. }
  3982. }
  3983. if (horizontal_radii.size() > 4 || vertical_radii.size() > 4
  3984. || horizontal_radii.is_empty()
  3985. || (reading_vertical && vertical_radii.is_empty()))
  3986. return nullptr;
  3987. auto top_left_radius = BorderRadiusStyleValue::create(top_left(horizontal_radii),
  3988. vertical_radii.is_empty() ? top_left(horizontal_radii) : top_left(vertical_radii));
  3989. auto top_right_radius = BorderRadiusStyleValue::create(top_right(horizontal_radii),
  3990. vertical_radii.is_empty() ? top_right(horizontal_radii) : top_right(vertical_radii));
  3991. auto bottom_right_radius = BorderRadiusStyleValue::create(bottom_right(horizontal_radii),
  3992. vertical_radii.is_empty() ? bottom_right(horizontal_radii) : bottom_right(vertical_radii));
  3993. auto bottom_left_radius = BorderRadiusStyleValue::create(bottom_left(horizontal_radii),
  3994. vertical_radii.is_empty() ? bottom_left(horizontal_radii) : bottom_left(vertical_radii));
  3995. transaction.commit();
  3996. return ShorthandStyleValue::create(PropertyID::BorderRadius,
  3997. { PropertyID::BorderTopLeftRadius, PropertyID::BorderTopRightRadius, PropertyID::BorderBottomRightRadius, PropertyID::BorderBottomLeftRadius },
  3998. { move(top_left_radius), move(top_right_radius), move(bottom_right_radius), move(bottom_left_radius) });
  3999. }
  4000. RefPtr<CSSStyleValue> Parser::parse_columns_value(TokenStream<ComponentValue>& tokens)
  4001. {
  4002. if (tokens.remaining_token_count() > 2)
  4003. return nullptr;
  4004. RefPtr<CSSStyleValue> column_count;
  4005. RefPtr<CSSStyleValue> column_width;
  4006. Vector<PropertyID> remaining_longhands { PropertyID::ColumnCount, PropertyID::ColumnWidth };
  4007. int found_autos = 0;
  4008. auto transaction = tokens.begin_transaction();
  4009. while (tokens.has_next_token()) {
  4010. auto property_and_value = parse_css_value_for_properties(remaining_longhands, tokens);
  4011. if (!property_and_value.has_value())
  4012. return nullptr;
  4013. auto& value = property_and_value->style_value;
  4014. // since the values can be in either order, we want to skip over autos
  4015. if (value->has_auto()) {
  4016. found_autos++;
  4017. continue;
  4018. }
  4019. remove_property(remaining_longhands, property_and_value->property);
  4020. switch (property_and_value->property) {
  4021. case PropertyID::ColumnCount: {
  4022. VERIFY(!column_count);
  4023. column_count = value.release_nonnull();
  4024. continue;
  4025. }
  4026. case PropertyID::ColumnWidth: {
  4027. VERIFY(!column_width);
  4028. column_width = value.release_nonnull();
  4029. continue;
  4030. }
  4031. default:
  4032. VERIFY_NOT_REACHED();
  4033. }
  4034. }
  4035. if (found_autos > 2)
  4036. return nullptr;
  4037. if (found_autos == 2) {
  4038. column_count = CSSKeywordValue::create(Keyword::Auto);
  4039. column_width = CSSKeywordValue::create(Keyword::Auto);
  4040. }
  4041. if (found_autos == 1) {
  4042. if (!column_count)
  4043. column_count = CSSKeywordValue::create(Keyword::Auto);
  4044. if (!column_width)
  4045. column_width = CSSKeywordValue::create(Keyword::Auto);
  4046. }
  4047. if (!column_count)
  4048. column_count = property_initial_value(m_context.realm(), PropertyID::ColumnCount);
  4049. if (!column_width)
  4050. column_width = property_initial_value(m_context.realm(), PropertyID::ColumnWidth);
  4051. transaction.commit();
  4052. return ShorthandStyleValue::create(PropertyID::Columns,
  4053. { PropertyID::ColumnCount, PropertyID::ColumnWidth },
  4054. { column_count.release_nonnull(), column_width.release_nonnull() });
  4055. }
  4056. RefPtr<CSSStyleValue> Parser::parse_shadow_value(TokenStream<ComponentValue>& tokens, AllowInsetKeyword allow_inset_keyword)
  4057. {
  4058. // "none"
  4059. if (auto none = parse_all_as_single_keyword_value(tokens, Keyword::None))
  4060. return none;
  4061. return parse_comma_separated_value_list(tokens, [this, allow_inset_keyword](auto& tokens) {
  4062. return parse_single_shadow_value(tokens, allow_inset_keyword);
  4063. });
  4064. }
  4065. RefPtr<CSSStyleValue> Parser::parse_single_shadow_value(TokenStream<ComponentValue>& tokens, AllowInsetKeyword allow_inset_keyword)
  4066. {
  4067. auto transaction = tokens.begin_transaction();
  4068. RefPtr<CSSStyleValue> color;
  4069. RefPtr<CSSStyleValue> offset_x;
  4070. RefPtr<CSSStyleValue> offset_y;
  4071. RefPtr<CSSStyleValue> blur_radius;
  4072. RefPtr<CSSStyleValue> spread_distance;
  4073. Optional<ShadowPlacement> placement;
  4074. auto possibly_dynamic_length = [&](ComponentValue const& token) -> RefPtr<CSSStyleValue> {
  4075. auto tokens = TokenStream<ComponentValue>::of_single_token(token);
  4076. auto maybe_length = parse_length(tokens);
  4077. if (!maybe_length.has_value())
  4078. return nullptr;
  4079. return maybe_length->as_style_value();
  4080. };
  4081. while (tokens.has_next_token()) {
  4082. if (auto maybe_color = parse_color_value(tokens); maybe_color) {
  4083. if (color)
  4084. return nullptr;
  4085. color = maybe_color.release_nonnull();
  4086. continue;
  4087. }
  4088. auto const& token = tokens.next_token();
  4089. if (auto maybe_offset_x = possibly_dynamic_length(token); maybe_offset_x) {
  4090. // horizontal offset
  4091. if (offset_x)
  4092. return nullptr;
  4093. offset_x = maybe_offset_x;
  4094. tokens.discard_a_token();
  4095. // vertical offset
  4096. if (!tokens.has_next_token())
  4097. return nullptr;
  4098. auto maybe_offset_y = possibly_dynamic_length(tokens.next_token());
  4099. if (!maybe_offset_y)
  4100. return nullptr;
  4101. offset_y = maybe_offset_y;
  4102. tokens.discard_a_token();
  4103. // blur radius (optional)
  4104. if (!tokens.has_next_token())
  4105. break;
  4106. auto maybe_blur_radius = possibly_dynamic_length(tokens.next_token());
  4107. if (!maybe_blur_radius)
  4108. continue;
  4109. blur_radius = maybe_blur_radius;
  4110. tokens.discard_a_token();
  4111. // spread distance (optional)
  4112. if (!tokens.has_next_token())
  4113. break;
  4114. auto maybe_spread_distance = possibly_dynamic_length(tokens.next_token());
  4115. if (!maybe_spread_distance)
  4116. continue;
  4117. spread_distance = maybe_spread_distance;
  4118. tokens.discard_a_token();
  4119. continue;
  4120. }
  4121. if (allow_inset_keyword == AllowInsetKeyword::Yes && token.is_ident("inset"sv)) {
  4122. if (placement.has_value())
  4123. return nullptr;
  4124. placement = ShadowPlacement::Inner;
  4125. tokens.discard_a_token();
  4126. continue;
  4127. }
  4128. if (token.is(Token::Type::Comma))
  4129. break;
  4130. return nullptr;
  4131. }
  4132. // If color is absent, default to `currentColor`
  4133. if (!color)
  4134. color = CSSKeywordValue::create(Keyword::Currentcolor);
  4135. // x/y offsets are required
  4136. if (!offset_x || !offset_y)
  4137. return nullptr;
  4138. // Other lengths default to 0
  4139. if (!blur_radius)
  4140. blur_radius = LengthStyleValue::create(Length::make_px(0));
  4141. if (!spread_distance)
  4142. spread_distance = LengthStyleValue::create(Length::make_px(0));
  4143. // Placement is outer by default
  4144. if (!placement.has_value())
  4145. placement = ShadowPlacement::Outer;
  4146. transaction.commit();
  4147. return ShadowStyleValue::create(color.release_nonnull(), offset_x.release_nonnull(), offset_y.release_nonnull(), blur_radius.release_nonnull(), spread_distance.release_nonnull(), placement.release_value());
  4148. }
  4149. RefPtr<CSSStyleValue> Parser::parse_rotate_value(TokenStream<ComponentValue>& tokens)
  4150. {
  4151. // Value: none | <angle> | [ x | y | z | <number>{3} ] && <angle>
  4152. if (tokens.remaining_token_count() == 1) {
  4153. // "none"
  4154. if (auto none = parse_all_as_single_keyword_value(tokens, Keyword::None))
  4155. return none;
  4156. // <angle>
  4157. if (auto angle = parse_angle_value(tokens))
  4158. return RotationStyleValue::create(angle.release_nonnull(), NumberStyleValue::create(0), NumberStyleValue::create(0), NumberStyleValue::create(1));
  4159. }
  4160. auto parse_one_of_xyz = [&]() -> Optional<ComponentValue const&> {
  4161. auto transaction = tokens.begin_transaction();
  4162. auto const& axis = tokens.consume_a_token();
  4163. if (axis.is_ident("x"sv) || axis.is_ident("y"sv) || axis.is_ident("z"sv)) {
  4164. transaction.commit();
  4165. return axis;
  4166. }
  4167. return {};
  4168. };
  4169. // [ x | y | z ] && <angle>
  4170. if (tokens.remaining_token_count() == 2) {
  4171. // Try parsing `x <angle>`
  4172. if (auto axis = parse_one_of_xyz(); axis.has_value()) {
  4173. if (auto angle = parse_angle_value(tokens); angle) {
  4174. if (axis->is_ident("x"sv))
  4175. return RotationStyleValue::create(angle.release_nonnull(), NumberStyleValue::create(1), NumberStyleValue::create(0), NumberStyleValue::create(0));
  4176. if (axis->is_ident("y"sv))
  4177. return RotationStyleValue::create(angle.release_nonnull(), NumberStyleValue::create(0), NumberStyleValue::create(1), NumberStyleValue::create(0));
  4178. if (axis->is_ident("z"sv))
  4179. return RotationStyleValue::create(angle.release_nonnull(), NumberStyleValue::create(0), NumberStyleValue::create(0), NumberStyleValue::create(1));
  4180. }
  4181. }
  4182. // Try parsing `<angle> x`
  4183. if (auto angle = parse_angle_value(tokens); angle) {
  4184. if (auto axis = parse_one_of_xyz(); axis.has_value()) {
  4185. if (axis->is_ident("x"sv))
  4186. return RotationStyleValue::create(angle.release_nonnull(), NumberStyleValue::create(1), NumberStyleValue::create(0), NumberStyleValue::create(0));
  4187. if (axis->is_ident("y"sv))
  4188. return RotationStyleValue::create(angle.release_nonnull(), NumberStyleValue::create(0), NumberStyleValue::create(1), NumberStyleValue::create(0));
  4189. if (axis->is_ident("z"sv))
  4190. return RotationStyleValue::create(angle.release_nonnull(), NumberStyleValue::create(0), NumberStyleValue::create(0), NumberStyleValue::create(1));
  4191. }
  4192. }
  4193. }
  4194. auto parse_three_numbers = [&]() -> Optional<StyleValueVector> {
  4195. auto transaction = tokens.begin_transaction();
  4196. StyleValueVector numbers;
  4197. for (size_t i = 0; i < 3; ++i) {
  4198. if (auto number = parse_number_value(tokens); number) {
  4199. numbers.append(number.release_nonnull());
  4200. } else {
  4201. return {};
  4202. }
  4203. }
  4204. transaction.commit();
  4205. return numbers;
  4206. };
  4207. // <number>{3} && <angle>
  4208. if (tokens.remaining_token_count() == 4) {
  4209. // Try parsing <number>{3} <angle>
  4210. if (auto maybe_numbers = parse_three_numbers(); maybe_numbers.has_value()) {
  4211. if (auto angle = parse_angle_value(tokens); angle) {
  4212. auto numbers = maybe_numbers.release_value();
  4213. return RotationStyleValue::create(angle.release_nonnull(), numbers[0], numbers[1], numbers[2]);
  4214. }
  4215. }
  4216. // Try parsing <angle> <number>{3}
  4217. if (auto angle = parse_angle_value(tokens); angle) {
  4218. if (auto maybe_numbers = parse_three_numbers(); maybe_numbers.has_value()) {
  4219. auto numbers = maybe_numbers.release_value();
  4220. return RotationStyleValue::create(angle.release_nonnull(), numbers[0], numbers[1], numbers[2]);
  4221. }
  4222. }
  4223. }
  4224. return nullptr;
  4225. }
  4226. RefPtr<CSSStyleValue> Parser::parse_stroke_dasharray_value(TokenStream<ComponentValue>& tokens)
  4227. {
  4228. // https://svgwg.org/svg2-draft/painting.html#StrokeDashing
  4229. // Value: none | <dasharray>
  4230. if (auto none = parse_all_as_single_keyword_value(tokens, Keyword::None))
  4231. return none;
  4232. // https://svgwg.org/svg2-draft/painting.html#DataTypeDasharray
  4233. // <dasharray> = [ [ <length-percentage> | <number> ]+ ]#
  4234. Vector<ValueComparingNonnullRefPtr<CSSStyleValue const>> dashes;
  4235. while (tokens.has_next_token()) {
  4236. tokens.discard_whitespace();
  4237. // A <dasharray> is a list of comma and/or white space separated <number> or <length-percentage> values. A <number> value represents a value in user units.
  4238. auto value = parse_number_value(tokens);
  4239. if (value) {
  4240. dashes.append(value.release_nonnull());
  4241. } else {
  4242. auto value = parse_length_percentage_value(tokens);
  4243. if (!value)
  4244. return {};
  4245. dashes.append(value.release_nonnull());
  4246. }
  4247. tokens.discard_whitespace();
  4248. if (tokens.has_next_token() && tokens.next_token().is(Token::Type::Comma))
  4249. tokens.discard_a_token();
  4250. }
  4251. return StyleValueList::create(move(dashes), StyleValueList::Separator::Comma);
  4252. }
  4253. RefPtr<CSSStyleValue> Parser::parse_content_value(TokenStream<ComponentValue>& tokens)
  4254. {
  4255. // FIXME: `content` accepts several kinds of function() type, which we don't handle in property_accepts_value() yet.
  4256. auto is_single_value_keyword = [](Keyword keyword) -> bool {
  4257. switch (keyword) {
  4258. case Keyword::None:
  4259. case Keyword::Normal:
  4260. return true;
  4261. default:
  4262. return false;
  4263. }
  4264. };
  4265. if (tokens.remaining_token_count() == 1) {
  4266. auto transaction = tokens.begin_transaction();
  4267. if (auto keyword = parse_keyword_value(tokens)) {
  4268. if (is_single_value_keyword(keyword->to_keyword())) {
  4269. transaction.commit();
  4270. return keyword;
  4271. }
  4272. }
  4273. }
  4274. auto transaction = tokens.begin_transaction();
  4275. StyleValueVector content_values;
  4276. StyleValueVector alt_text_values;
  4277. bool in_alt_text = false;
  4278. while (tokens.has_next_token()) {
  4279. auto& next = tokens.next_token();
  4280. if (next.is_delim('/')) {
  4281. if (in_alt_text || content_values.is_empty())
  4282. return nullptr;
  4283. in_alt_text = true;
  4284. tokens.discard_a_token();
  4285. continue;
  4286. }
  4287. if (auto style_value = parse_css_value_for_property(PropertyID::Content, tokens)) {
  4288. if (is_single_value_keyword(style_value->to_keyword()))
  4289. return nullptr;
  4290. if (in_alt_text) {
  4291. alt_text_values.append(style_value.release_nonnull());
  4292. } else {
  4293. content_values.append(style_value.release_nonnull());
  4294. }
  4295. continue;
  4296. }
  4297. return nullptr;
  4298. }
  4299. if (content_values.is_empty())
  4300. return nullptr;
  4301. if (in_alt_text && alt_text_values.is_empty())
  4302. return nullptr;
  4303. RefPtr<StyleValueList> alt_text;
  4304. if (!alt_text_values.is_empty())
  4305. alt_text = StyleValueList::create(move(alt_text_values), StyleValueList::Separator::Space);
  4306. transaction.commit();
  4307. return ContentStyleValue::create(StyleValueList::create(move(content_values), StyleValueList::Separator::Space), move(alt_text));
  4308. }
  4309. // https://drafts.csswg.org/css-lists-3/#propdef-counter-increment
  4310. RefPtr<CSSStyleValue> Parser::parse_counter_increment_value(TokenStream<ComponentValue>& tokens)
  4311. {
  4312. // [ <counter-name> <integer>? ]+ | none
  4313. if (auto none = parse_all_as_single_keyword_value(tokens, Keyword::None))
  4314. return none;
  4315. return parse_counter_definitions_value(tokens, AllowReversed::No, 1);
  4316. }
  4317. // https://drafts.csswg.org/css-lists-3/#propdef-counter-reset
  4318. RefPtr<CSSStyleValue> Parser::parse_counter_reset_value(TokenStream<ComponentValue>& tokens)
  4319. {
  4320. // [ <counter-name> <integer>? | <reversed-counter-name> <integer>? ]+ | none
  4321. if (auto none = parse_all_as_single_keyword_value(tokens, Keyword::None))
  4322. return none;
  4323. return parse_counter_definitions_value(tokens, AllowReversed::Yes, 0);
  4324. }
  4325. // https://drafts.csswg.org/css-lists-3/#propdef-counter-set
  4326. RefPtr<CSSStyleValue> Parser::parse_counter_set_value(TokenStream<ComponentValue>& tokens)
  4327. {
  4328. // [ <counter-name> <integer>? ]+ | none
  4329. if (auto none = parse_all_as_single_keyword_value(tokens, Keyword::None))
  4330. return none;
  4331. return parse_counter_definitions_value(tokens, AllowReversed::No, 0);
  4332. }
  4333. // https://www.w3.org/TR/css-display-3/#the-display-properties
  4334. RefPtr<CSSStyleValue> Parser::parse_display_value(TokenStream<ComponentValue>& tokens)
  4335. {
  4336. auto parse_single_component_display = [this](TokenStream<ComponentValue>& tokens) -> Optional<Display> {
  4337. auto transaction = tokens.begin_transaction();
  4338. if (auto keyword_value = parse_keyword_value(tokens)) {
  4339. auto keyword = keyword_value->to_keyword();
  4340. if (keyword == Keyword::ListItem) {
  4341. transaction.commit();
  4342. return Display::from_short(Display::Short::ListItem);
  4343. }
  4344. if (auto display_outside = keyword_to_display_outside(keyword); display_outside.has_value()) {
  4345. transaction.commit();
  4346. switch (display_outside.value()) {
  4347. case DisplayOutside::Block:
  4348. return Display::from_short(Display::Short::Block);
  4349. case DisplayOutside::Inline:
  4350. return Display::from_short(Display::Short::Inline);
  4351. case DisplayOutside::RunIn:
  4352. return Display::from_short(Display::Short::RunIn);
  4353. }
  4354. }
  4355. if (auto display_inside = keyword_to_display_inside(keyword); display_inside.has_value()) {
  4356. transaction.commit();
  4357. switch (display_inside.value()) {
  4358. case DisplayInside::Flow:
  4359. return Display::from_short(Display::Short::Flow);
  4360. case DisplayInside::FlowRoot:
  4361. return Display::from_short(Display::Short::FlowRoot);
  4362. case DisplayInside::Table:
  4363. return Display::from_short(Display::Short::Table);
  4364. case DisplayInside::Flex:
  4365. return Display::from_short(Display::Short::Flex);
  4366. case DisplayInside::Grid:
  4367. return Display::from_short(Display::Short::Grid);
  4368. case DisplayInside::Ruby:
  4369. return Display::from_short(Display::Short::Ruby);
  4370. case DisplayInside::Math:
  4371. return Display::from_short(Display::Short::Math);
  4372. }
  4373. }
  4374. if (auto display_internal = keyword_to_display_internal(keyword); display_internal.has_value()) {
  4375. transaction.commit();
  4376. return Display { display_internal.value() };
  4377. }
  4378. if (auto display_box = keyword_to_display_box(keyword); display_box.has_value()) {
  4379. transaction.commit();
  4380. switch (display_box.value()) {
  4381. case DisplayBox::Contents:
  4382. return Display::from_short(Display::Short::Contents);
  4383. case DisplayBox::None:
  4384. return Display::from_short(Display::Short::None);
  4385. }
  4386. }
  4387. if (auto display_legacy = keyword_to_display_legacy(keyword); display_legacy.has_value()) {
  4388. transaction.commit();
  4389. switch (display_legacy.value()) {
  4390. case DisplayLegacy::InlineBlock:
  4391. return Display::from_short(Display::Short::InlineBlock);
  4392. case DisplayLegacy::InlineTable:
  4393. return Display::from_short(Display::Short::InlineTable);
  4394. case DisplayLegacy::InlineFlex:
  4395. return Display::from_short(Display::Short::InlineFlex);
  4396. case DisplayLegacy::InlineGrid:
  4397. return Display::from_short(Display::Short::InlineGrid);
  4398. }
  4399. }
  4400. }
  4401. return OptionalNone {};
  4402. };
  4403. auto parse_multi_component_display = [this](TokenStream<ComponentValue>& tokens) -> Optional<Display> {
  4404. auto list_item = Display::ListItem::No;
  4405. Optional<DisplayInside> inside;
  4406. Optional<DisplayOutside> outside;
  4407. auto transaction = tokens.begin_transaction();
  4408. while (tokens.has_next_token()) {
  4409. if (auto value = parse_keyword_value(tokens)) {
  4410. auto keyword = value->to_keyword();
  4411. if (keyword == Keyword::ListItem) {
  4412. if (list_item == Display::ListItem::Yes)
  4413. return {};
  4414. list_item = Display::ListItem::Yes;
  4415. continue;
  4416. }
  4417. if (auto inside_value = keyword_to_display_inside(keyword); inside_value.has_value()) {
  4418. if (inside.has_value())
  4419. return {};
  4420. inside = inside_value.value();
  4421. continue;
  4422. }
  4423. if (auto outside_value = keyword_to_display_outside(keyword); outside_value.has_value()) {
  4424. if (outside.has_value())
  4425. return {};
  4426. outside = outside_value.value();
  4427. continue;
  4428. }
  4429. }
  4430. // Not a display value, abort.
  4431. dbgln_if(CSS_PARSER_DEBUG, "Unrecognized display value: `{}`", tokens.next_token().to_string());
  4432. return {};
  4433. }
  4434. // The spec does not allow any other inside values to be combined with list-item
  4435. // <display-outside>? && [ flow | flow-root ]? && list-item
  4436. if (list_item == Display::ListItem::Yes && inside.has_value() && inside != DisplayInside::Flow && inside != DisplayInside::FlowRoot)
  4437. return {};
  4438. transaction.commit();
  4439. return Display { outside.value_or(DisplayOutside::Block), inside.value_or(DisplayInside::Flow), list_item };
  4440. };
  4441. Optional<Display> display;
  4442. if (tokens.remaining_token_count() == 1)
  4443. display = parse_single_component_display(tokens);
  4444. else
  4445. display = parse_multi_component_display(tokens);
  4446. if (display.has_value())
  4447. return DisplayStyleValue::create(display.value());
  4448. return nullptr;
  4449. }
  4450. RefPtr<CSSStyleValue> Parser::parse_filter_value_list_value(TokenStream<ComponentValue>& tokens)
  4451. {
  4452. if (auto none = parse_all_as_single_keyword_value(tokens, Keyword::None))
  4453. return none;
  4454. auto transaction = tokens.begin_transaction();
  4455. // FIXME: <url>s are ignored for now
  4456. // <filter-value-list> = [ <filter-function> | <url> ]+
  4457. enum class FilterToken {
  4458. // Color filters:
  4459. Brightness,
  4460. Contrast,
  4461. Grayscale,
  4462. Invert,
  4463. Opacity,
  4464. Saturate,
  4465. Sepia,
  4466. // Special filters:
  4467. Blur,
  4468. DropShadow,
  4469. HueRotate
  4470. };
  4471. auto filter_token_to_operation = [&](auto filter) {
  4472. VERIFY(to_underlying(filter) < to_underlying(FilterToken::Blur));
  4473. return static_cast<FilterOperation::Color::Type>(filter);
  4474. };
  4475. auto parse_filter_function_name = [&](auto name) -> Optional<FilterToken> {
  4476. if (name.equals_ignoring_ascii_case("blur"sv))
  4477. return FilterToken::Blur;
  4478. if (name.equals_ignoring_ascii_case("brightness"sv))
  4479. return FilterToken::Brightness;
  4480. if (name.equals_ignoring_ascii_case("contrast"sv))
  4481. return FilterToken::Contrast;
  4482. if (name.equals_ignoring_ascii_case("drop-shadow"sv))
  4483. return FilterToken::DropShadow;
  4484. if (name.equals_ignoring_ascii_case("grayscale"sv))
  4485. return FilterToken::Grayscale;
  4486. if (name.equals_ignoring_ascii_case("hue-rotate"sv))
  4487. return FilterToken::HueRotate;
  4488. if (name.equals_ignoring_ascii_case("invert"sv))
  4489. return FilterToken::Invert;
  4490. if (name.equals_ignoring_ascii_case("opacity"sv))
  4491. return FilterToken::Opacity;
  4492. if (name.equals_ignoring_ascii_case("saturate"sv))
  4493. return FilterToken::Saturate;
  4494. if (name.equals_ignoring_ascii_case("sepia"sv))
  4495. return FilterToken::Sepia;
  4496. return {};
  4497. };
  4498. auto parse_filter_function = [&](auto filter_token, auto const& function_values) -> Optional<FilterFunction> {
  4499. TokenStream tokens { function_values };
  4500. tokens.discard_whitespace();
  4501. auto if_no_more_tokens_return = [&](auto filter) -> Optional<FilterFunction> {
  4502. tokens.discard_whitespace();
  4503. if (tokens.has_next_token())
  4504. return {};
  4505. return filter;
  4506. };
  4507. if (filter_token == FilterToken::Blur) {
  4508. // blur( <length>? )
  4509. if (!tokens.has_next_token())
  4510. return FilterOperation::Blur {};
  4511. auto blur_radius = parse_length(tokens);
  4512. tokens.discard_whitespace();
  4513. if (!blur_radius.has_value())
  4514. return {};
  4515. return if_no_more_tokens_return(FilterOperation::Blur { blur_radius.value() });
  4516. } else if (filter_token == FilterToken::DropShadow) {
  4517. if (!tokens.has_next_token())
  4518. return {};
  4519. // drop-shadow( [ <color>? && <length>{2,3} ] )
  4520. // Note: The following code is a little awkward to allow the color to be before or after the lengths.
  4521. Optional<LengthOrCalculated> maybe_radius = {};
  4522. auto maybe_color = parse_color_value(tokens);
  4523. tokens.discard_whitespace();
  4524. auto x_offset = parse_length(tokens);
  4525. tokens.discard_whitespace();
  4526. if (!x_offset.has_value() || !tokens.has_next_token())
  4527. return {};
  4528. auto y_offset = parse_length(tokens);
  4529. tokens.discard_whitespace();
  4530. if (!y_offset.has_value())
  4531. return {};
  4532. if (tokens.has_next_token()) {
  4533. maybe_radius = parse_length(tokens);
  4534. tokens.discard_whitespace();
  4535. if (!maybe_color && (!maybe_radius.has_value() || tokens.has_next_token())) {
  4536. maybe_color = parse_color_value(tokens);
  4537. if (!maybe_color)
  4538. return {};
  4539. } else if (!maybe_radius.has_value()) {
  4540. return {};
  4541. }
  4542. }
  4543. Optional<Color> color = {};
  4544. if (maybe_color)
  4545. color = maybe_color->to_color({});
  4546. return if_no_more_tokens_return(FilterOperation::DropShadow { x_offset.value(), y_offset.value(), maybe_radius, color });
  4547. } else if (filter_token == FilterToken::HueRotate) {
  4548. // hue-rotate( [ <angle> | <zero> ]? )
  4549. if (!tokens.has_next_token())
  4550. return FilterOperation::HueRotate {};
  4551. if (tokens.next_token().is(Token::Type::Number)) {
  4552. // hue-rotate(0)
  4553. auto number = tokens.consume_a_token().token().number();
  4554. if (number.is_integer() && number.integer_value() == 0)
  4555. return if_no_more_tokens_return(FilterOperation::HueRotate { FilterOperation::HueRotate::Zero {} });
  4556. return {};
  4557. }
  4558. if (auto angle = parse_angle(tokens); angle.has_value())
  4559. return if_no_more_tokens_return(FilterOperation::HueRotate { angle.value() });
  4560. return {};
  4561. } else {
  4562. // Simple filters:
  4563. // brightness( <number-percentage>? )
  4564. // contrast( <number-percentage>? )
  4565. // grayscale( <number-percentage>? )
  4566. // invert( <number-percentage>? )
  4567. // opacity( <number-percentage>? )
  4568. // sepia( <number-percentage>? )
  4569. // saturate( <number-percentage>? )
  4570. if (!tokens.has_next_token())
  4571. return FilterOperation::Color { filter_token_to_operation(filter_token) };
  4572. auto amount = parse_number_percentage(tokens);
  4573. return if_no_more_tokens_return(FilterOperation::Color { filter_token_to_operation(filter_token), amount });
  4574. }
  4575. };
  4576. Vector<FilterFunction> filter_value_list {};
  4577. while (tokens.has_next_token()) {
  4578. tokens.discard_whitespace();
  4579. if (!tokens.has_next_token())
  4580. break;
  4581. auto& token = tokens.consume_a_token();
  4582. if (!token.is_function())
  4583. return nullptr;
  4584. auto filter_token = parse_filter_function_name(token.function().name);
  4585. if (!filter_token.has_value())
  4586. return nullptr;
  4587. auto filter_function = parse_filter_function(*filter_token, token.function().value);
  4588. if (!filter_function.has_value())
  4589. return nullptr;
  4590. filter_value_list.append(*filter_function);
  4591. }
  4592. if (filter_value_list.is_empty())
  4593. return nullptr;
  4594. transaction.commit();
  4595. return FilterValueListStyleValue::create(move(filter_value_list));
  4596. }
  4597. RefPtr<CSSStyleValue> Parser::parse_flex_shorthand_value(TokenStream<ComponentValue>& tokens)
  4598. {
  4599. auto transaction = tokens.begin_transaction();
  4600. auto make_flex_shorthand = [&](NonnullRefPtr<CSSStyleValue> flex_grow, NonnullRefPtr<CSSStyleValue> flex_shrink, NonnullRefPtr<CSSStyleValue> flex_basis) {
  4601. transaction.commit();
  4602. return ShorthandStyleValue::create(PropertyID::Flex,
  4603. { PropertyID::FlexGrow, PropertyID::FlexShrink, PropertyID::FlexBasis },
  4604. { move(flex_grow), move(flex_shrink), move(flex_basis) });
  4605. };
  4606. if (tokens.remaining_token_count() == 1) {
  4607. // One-value syntax: <flex-grow> | <flex-basis> | none
  4608. auto properties = Array { PropertyID::FlexGrow, PropertyID::FlexBasis, PropertyID::Flex };
  4609. auto property_and_value = parse_css_value_for_properties(properties, tokens);
  4610. if (!property_and_value.has_value())
  4611. return nullptr;
  4612. auto& value = property_and_value->style_value;
  4613. switch (property_and_value->property) {
  4614. case PropertyID::FlexGrow: {
  4615. // NOTE: The spec says that flex-basis should be 0 here, but other engines currently use 0%.
  4616. // https://github.com/w3c/csswg-drafts/issues/5742
  4617. auto flex_basis = PercentageStyleValue::create(Percentage(0));
  4618. auto one = NumberStyleValue::create(1);
  4619. return make_flex_shorthand(*value, one, flex_basis);
  4620. }
  4621. case PropertyID::FlexBasis: {
  4622. auto one = NumberStyleValue::create(1);
  4623. return make_flex_shorthand(one, one, *value);
  4624. }
  4625. case PropertyID::Flex: {
  4626. if (value->is_keyword() && value->to_keyword() == Keyword::None) {
  4627. auto zero = NumberStyleValue::create(0);
  4628. return make_flex_shorthand(zero, zero, CSSKeywordValue::create(Keyword::Auto));
  4629. }
  4630. break;
  4631. }
  4632. default:
  4633. VERIFY_NOT_REACHED();
  4634. }
  4635. return nullptr;
  4636. }
  4637. RefPtr<CSSStyleValue> flex_grow;
  4638. RefPtr<CSSStyleValue> flex_shrink;
  4639. RefPtr<CSSStyleValue> flex_basis;
  4640. // NOTE: FlexGrow has to be before FlexBasis. `0` is a valid FlexBasis, but only
  4641. // if FlexGrow (along with optional FlexShrink) have already been specified.
  4642. auto remaining_longhands = Vector { PropertyID::FlexGrow, PropertyID::FlexBasis };
  4643. while (tokens.has_next_token()) {
  4644. auto property_and_value = parse_css_value_for_properties(remaining_longhands, tokens);
  4645. if (!property_and_value.has_value())
  4646. return nullptr;
  4647. auto& value = property_and_value->style_value;
  4648. remove_property(remaining_longhands, property_and_value->property);
  4649. switch (property_and_value->property) {
  4650. case PropertyID::FlexGrow: {
  4651. VERIFY(!flex_grow);
  4652. flex_grow = value.release_nonnull();
  4653. // Flex-shrink may optionally follow directly after.
  4654. auto maybe_flex_shrink = parse_css_value_for_property(PropertyID::FlexShrink, tokens);
  4655. if (maybe_flex_shrink)
  4656. flex_shrink = maybe_flex_shrink.release_nonnull();
  4657. continue;
  4658. }
  4659. case PropertyID::FlexBasis: {
  4660. VERIFY(!flex_basis);
  4661. flex_basis = value.release_nonnull();
  4662. continue;
  4663. }
  4664. default:
  4665. VERIFY_NOT_REACHED();
  4666. }
  4667. }
  4668. if (!flex_grow)
  4669. flex_grow = property_initial_value(m_context.realm(), PropertyID::FlexGrow);
  4670. if (!flex_shrink)
  4671. flex_shrink = property_initial_value(m_context.realm(), PropertyID::FlexShrink);
  4672. if (!flex_basis) {
  4673. // NOTE: The spec says that flex-basis should be 0 here, but other engines currently use 0%.
  4674. // https://github.com/w3c/csswg-drafts/issues/5742
  4675. flex_basis = PercentageStyleValue::create(Percentage(0));
  4676. }
  4677. return make_flex_shorthand(flex_grow.release_nonnull(), flex_shrink.release_nonnull(), flex_basis.release_nonnull());
  4678. }
  4679. RefPtr<CSSStyleValue> Parser::parse_flex_flow_value(TokenStream<ComponentValue>& tokens)
  4680. {
  4681. RefPtr<CSSStyleValue> flex_direction;
  4682. RefPtr<CSSStyleValue> flex_wrap;
  4683. auto remaining_longhands = Vector { PropertyID::FlexDirection, PropertyID::FlexWrap };
  4684. auto transaction = tokens.begin_transaction();
  4685. while (tokens.has_next_token()) {
  4686. auto property_and_value = parse_css_value_for_properties(remaining_longhands, tokens);
  4687. if (!property_and_value.has_value())
  4688. return nullptr;
  4689. auto& value = property_and_value->style_value;
  4690. remove_property(remaining_longhands, property_and_value->property);
  4691. switch (property_and_value->property) {
  4692. case PropertyID::FlexDirection:
  4693. VERIFY(!flex_direction);
  4694. flex_direction = value.release_nonnull();
  4695. continue;
  4696. case PropertyID::FlexWrap:
  4697. VERIFY(!flex_wrap);
  4698. flex_wrap = value.release_nonnull();
  4699. continue;
  4700. default:
  4701. VERIFY_NOT_REACHED();
  4702. }
  4703. }
  4704. if (!flex_direction)
  4705. flex_direction = property_initial_value(m_context.realm(), PropertyID::FlexDirection);
  4706. if (!flex_wrap)
  4707. flex_wrap = property_initial_value(m_context.realm(), PropertyID::FlexWrap);
  4708. transaction.commit();
  4709. return ShorthandStyleValue::create(PropertyID::FlexFlow,
  4710. { PropertyID::FlexDirection, PropertyID::FlexWrap },
  4711. { flex_direction.release_nonnull(), flex_wrap.release_nonnull() });
  4712. }
  4713. bool Parser::is_generic_font_family(Keyword keyword)
  4714. {
  4715. switch (keyword) {
  4716. case Keyword::Cursive:
  4717. case Keyword::Fantasy:
  4718. case Keyword::Monospace:
  4719. case Keyword::Serif:
  4720. case Keyword::SansSerif:
  4721. case Keyword::UiMonospace:
  4722. case Keyword::UiRounded:
  4723. case Keyword::UiSerif:
  4724. case Keyword::UiSansSerif:
  4725. return true;
  4726. default:
  4727. return false;
  4728. }
  4729. }
  4730. RefPtr<CSSStyleValue> Parser::parse_font_value(TokenStream<ComponentValue>& tokens)
  4731. {
  4732. RefPtr<CSSStyleValue> font_width;
  4733. RefPtr<CSSStyleValue> font_style;
  4734. RefPtr<CSSStyleValue> font_weight;
  4735. RefPtr<CSSStyleValue> font_size;
  4736. RefPtr<CSSStyleValue> line_height;
  4737. RefPtr<CSSStyleValue> font_families;
  4738. RefPtr<CSSStyleValue> font_variant;
  4739. // FIXME: Handle system fonts. (caption, icon, menu, message-box, small-caption, status-bar)
  4740. // Several sub-properties can be "normal", and appear in any order: style, variant, weight, stretch
  4741. // So, we have to handle that separately.
  4742. int normal_count = 0;
  4743. // FIXME: `font-variant` allows a lot of different values which aren't allowed in the `font` shorthand.
  4744. // FIXME: `font-width` allows <percentage> values, which aren't allowed in the `font` shorthand.
  4745. auto remaining_longhands = Vector { PropertyID::FontSize, PropertyID::FontStyle, PropertyID::FontVariant, PropertyID::FontWeight, PropertyID::FontWidth };
  4746. auto transaction = tokens.begin_transaction();
  4747. while (tokens.has_next_token()) {
  4748. auto& peek_token = tokens.next_token();
  4749. if (peek_token.is_ident("normal"sv)) {
  4750. normal_count++;
  4751. tokens.discard_a_token();
  4752. continue;
  4753. }
  4754. auto property_and_value = parse_css_value_for_properties(remaining_longhands, tokens);
  4755. if (!property_and_value.has_value())
  4756. return nullptr;
  4757. auto& value = property_and_value->style_value;
  4758. remove_property(remaining_longhands, property_and_value->property);
  4759. switch (property_and_value->property) {
  4760. case PropertyID::FontSize: {
  4761. VERIFY(!font_size);
  4762. font_size = value.release_nonnull();
  4763. // Consume `/ line-height` if present
  4764. if (tokens.next_token().is_delim('/')) {
  4765. tokens.discard_a_token();
  4766. auto maybe_line_height = parse_css_value_for_property(PropertyID::LineHeight, tokens);
  4767. if (!maybe_line_height)
  4768. return nullptr;
  4769. line_height = maybe_line_height.release_nonnull();
  4770. }
  4771. // Consume font-families
  4772. auto maybe_font_families = parse_font_family_value(tokens);
  4773. // font-family comes last, so we must not have any tokens left over.
  4774. if (!maybe_font_families || tokens.has_next_token())
  4775. return nullptr;
  4776. font_families = maybe_font_families.release_nonnull();
  4777. continue;
  4778. }
  4779. case PropertyID::FontWidth: {
  4780. VERIFY(!font_width);
  4781. font_width = value.release_nonnull();
  4782. continue;
  4783. }
  4784. case PropertyID::FontStyle: {
  4785. VERIFY(!font_style);
  4786. font_style = value.release_nonnull();
  4787. continue;
  4788. }
  4789. case PropertyID::FontVariant: {
  4790. VERIFY(!font_variant);
  4791. font_variant = value.release_nonnull();
  4792. continue;
  4793. }
  4794. case PropertyID::FontWeight: {
  4795. VERIFY(!font_weight);
  4796. font_weight = value.release_nonnull();
  4797. continue;
  4798. }
  4799. default:
  4800. VERIFY_NOT_REACHED();
  4801. }
  4802. return nullptr;
  4803. }
  4804. // Since normal is the default value for all the properties that can have it, we don't have to actually
  4805. // set anything to normal here. It'll be set when we create the ShorthandStyleValue below.
  4806. // We just need to make sure we were not given more normals than will fit.
  4807. int unset_value_count = (font_style ? 0 : 1) + (font_weight ? 0 : 1) + (font_variant ? 0 : 1) + (font_width ? 0 : 1);
  4808. if (unset_value_count < normal_count)
  4809. return nullptr;
  4810. if (!font_size || !font_families)
  4811. return nullptr;
  4812. if (!font_style)
  4813. font_style = property_initial_value(m_context.realm(), PropertyID::FontStyle);
  4814. if (!font_variant)
  4815. font_variant = property_initial_value(m_context.realm(), PropertyID::FontVariant);
  4816. if (!font_weight)
  4817. font_weight = property_initial_value(m_context.realm(), PropertyID::FontWeight);
  4818. if (!font_width)
  4819. font_width = property_initial_value(m_context.realm(), PropertyID::FontWidth);
  4820. if (!line_height)
  4821. line_height = property_initial_value(m_context.realm(), PropertyID::LineHeight);
  4822. transaction.commit();
  4823. return ShorthandStyleValue::create(PropertyID::Font,
  4824. { PropertyID::FontStyle, PropertyID::FontVariant, PropertyID::FontWeight, PropertyID::FontWidth, PropertyID::FontSize, PropertyID::LineHeight, PropertyID::FontFamily },
  4825. { font_style.release_nonnull(), font_variant.release_nonnull(), font_weight.release_nonnull(), font_width.release_nonnull(), font_size.release_nonnull(), line_height.release_nonnull(), font_families.release_nonnull() });
  4826. }
  4827. RefPtr<CSSStyleValue> Parser::parse_font_family_value(TokenStream<ComponentValue>& tokens)
  4828. {
  4829. auto next_is_comma_or_eof = [&]() -> bool {
  4830. return !tokens.has_next_token() || tokens.next_token().is(Token::Type::Comma);
  4831. };
  4832. // Note: Font-family names can either be a quoted string, or a keyword, or a series of custom-idents.
  4833. // eg, these are equivalent:
  4834. // font-family: my cool font\!, serif;
  4835. // font-family: "my cool font!", serif;
  4836. StyleValueVector font_families;
  4837. Vector<String> current_name_parts;
  4838. while (tokens.has_next_token()) {
  4839. auto const& peek = tokens.next_token();
  4840. if (peek.is(Token::Type::String)) {
  4841. // `font-family: my cool "font";` is invalid.
  4842. if (!current_name_parts.is_empty())
  4843. return nullptr;
  4844. tokens.discard_a_token(); // String
  4845. if (!next_is_comma_or_eof())
  4846. return nullptr;
  4847. font_families.append(StringStyleValue::create(peek.token().string()));
  4848. tokens.discard_a_token(); // Comma
  4849. continue;
  4850. }
  4851. if (peek.is(Token::Type::Ident)) {
  4852. // If this is a valid identifier, it's NOT a custom-ident and can't be part of a larger name.
  4853. // CSS-wide keywords are not allowed
  4854. if (auto builtin = parse_builtin_value(tokens))
  4855. return nullptr;
  4856. auto maybe_keyword = keyword_from_string(peek.token().ident());
  4857. // Can't have a generic-font-name as a token in an unquoted font name.
  4858. if (maybe_keyword.has_value() && is_generic_font_family(maybe_keyword.value())) {
  4859. if (!current_name_parts.is_empty())
  4860. return nullptr;
  4861. tokens.discard_a_token(); // Ident
  4862. if (!next_is_comma_or_eof())
  4863. return nullptr;
  4864. font_families.append(CSSKeywordValue::create(maybe_keyword.value()));
  4865. tokens.discard_a_token(); // Comma
  4866. continue;
  4867. }
  4868. current_name_parts.append(tokens.consume_a_token().token().ident().to_string());
  4869. continue;
  4870. }
  4871. if (peek.is(Token::Type::Comma)) {
  4872. if (current_name_parts.is_empty())
  4873. return nullptr;
  4874. tokens.discard_a_token(); // Comma
  4875. // This is really a series of custom-idents, not just one. But for the sake of simplicity we'll make it one.
  4876. font_families.append(CustomIdentStyleValue::create(MUST(String::join(' ', current_name_parts))));
  4877. current_name_parts.clear();
  4878. // Can't have a trailing comma
  4879. if (!tokens.has_next_token())
  4880. return nullptr;
  4881. continue;
  4882. }
  4883. return nullptr;
  4884. }
  4885. if (!current_name_parts.is_empty()) {
  4886. // This is really a series of custom-idents, not just one. But for the sake of simplicity we'll make it one.
  4887. font_families.append(CustomIdentStyleValue::create(MUST(String::join(' ', current_name_parts))));
  4888. current_name_parts.clear();
  4889. }
  4890. if (font_families.is_empty())
  4891. return nullptr;
  4892. return StyleValueList::create(move(font_families), StyleValueList::Separator::Comma);
  4893. }
  4894. RefPtr<CSSStyleValue> Parser::parse_font_language_override_value(TokenStream<ComponentValue>& tokens)
  4895. {
  4896. // https://drafts.csswg.org/css-fonts/#propdef-font-language-override
  4897. // This is `normal | <string>` but with the constraint that the string has to be 4 characters long:
  4898. // Shorter strings are right-padded with spaces, and longer strings are invalid.
  4899. if (auto normal = parse_all_as_single_keyword_value(tokens, Keyword::Normal))
  4900. return normal;
  4901. auto transaction = tokens.begin_transaction();
  4902. tokens.discard_whitespace();
  4903. if (auto string = parse_string_value(tokens)) {
  4904. auto string_value = string->string_value();
  4905. tokens.discard_whitespace();
  4906. if (tokens.has_next_token()) {
  4907. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Failed to parse font-language-override: unexpected trailing tokens");
  4908. return nullptr;
  4909. }
  4910. auto length = string_value.code_points().length();
  4911. if (length > 4) {
  4912. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Failed to parse font-language-override: <string> value \"{}\" is too long", string_value);
  4913. return nullptr;
  4914. }
  4915. transaction.commit();
  4916. if (length < 4)
  4917. return StringStyleValue::create(MUST(String::formatted("{:<4}", string_value)));
  4918. return string;
  4919. }
  4920. return nullptr;
  4921. }
  4922. RefPtr<CSSStyleValue> Parser::parse_font_feature_settings_value(TokenStream<ComponentValue>& tokens)
  4923. {
  4924. // https://drafts.csswg.org/css-fonts/#propdef-font-feature-settings
  4925. // normal | <feature-tag-value>#
  4926. // normal
  4927. if (auto normal = parse_all_as_single_keyword_value(tokens, Keyword::Normal))
  4928. return normal;
  4929. // <feature-tag-value>#
  4930. auto transaction = tokens.begin_transaction();
  4931. auto tag_values = parse_a_comma_separated_list_of_component_values(tokens);
  4932. // "The computed value of font-feature-settings is a map, so any duplicates in the specified value must not be preserved.
  4933. // If the same feature tag appears more than once, the value associated with the last appearance supersedes any previous
  4934. // value for that axis."
  4935. // So, we deduplicate them here using a HashSet.
  4936. OrderedHashMap<FlyString, NonnullRefPtr<OpenTypeTaggedStyleValue>> feature_tags_map;
  4937. for (auto const& values : tag_values) {
  4938. // <feature-tag-value> = <opentype-tag> [ <integer [0,∞]> | on | off ]?
  4939. TokenStream tag_tokens { values };
  4940. tag_tokens.discard_whitespace();
  4941. auto opentype_tag = parse_opentype_tag_value(tag_tokens);
  4942. tag_tokens.discard_whitespace();
  4943. RefPtr<CSSStyleValue> value;
  4944. if (tag_tokens.has_next_token()) {
  4945. if (auto integer = parse_integer_value(tag_tokens)) {
  4946. if (integer->is_integer() && integer->as_integer().value() < 0)
  4947. return nullptr;
  4948. value = integer;
  4949. } else {
  4950. // A value of on is synonymous with 1 and off is synonymous with 0.
  4951. auto keyword = parse_keyword_value(tag_tokens);
  4952. if (!keyword)
  4953. return nullptr;
  4954. switch (keyword->to_keyword()) {
  4955. case Keyword::On:
  4956. value = IntegerStyleValue::create(1);
  4957. break;
  4958. case Keyword::Off:
  4959. value = IntegerStyleValue::create(0);
  4960. break;
  4961. default:
  4962. return nullptr;
  4963. }
  4964. }
  4965. tag_tokens.discard_whitespace();
  4966. } else {
  4967. // "If the value is omitted, a value of 1 is assumed."
  4968. value = IntegerStyleValue::create(1);
  4969. }
  4970. if (!opentype_tag || !value || tag_tokens.has_next_token())
  4971. return nullptr;
  4972. feature_tags_map.set(opentype_tag->string_value(), OpenTypeTaggedStyleValue::create(opentype_tag->string_value(), value.release_nonnull()));
  4973. }
  4974. // "The computed value contains the de-duplicated feature tags, sorted in ascending order by code unit."
  4975. StyleValueVector feature_tags;
  4976. feature_tags.ensure_capacity(feature_tags_map.size());
  4977. for (auto const& [key, feature_tag] : feature_tags_map)
  4978. feature_tags.append(feature_tag);
  4979. quick_sort(feature_tags, [](auto& a, auto& b) {
  4980. return a->as_open_type_tagged().tag() < b->as_open_type_tagged().tag();
  4981. });
  4982. transaction.commit();
  4983. return StyleValueList::create(move(feature_tags), StyleValueList::Separator::Comma);
  4984. }
  4985. RefPtr<CSSStyleValue> Parser::parse_font_variation_settings_value(TokenStream<ComponentValue>& tokens)
  4986. {
  4987. // https://drafts.csswg.org/css-fonts/#propdef-font-variation-settings
  4988. // normal | [ <opentype-tag> <number>]#
  4989. // normal
  4990. if (auto normal = parse_all_as_single_keyword_value(tokens, Keyword::Normal))
  4991. return normal;
  4992. // [ <opentype-tag> <number>]#
  4993. auto transaction = tokens.begin_transaction();
  4994. auto tag_values = parse_a_comma_separated_list_of_component_values(tokens);
  4995. // "If the same axis name appears more than once, the value associated with the last appearance supersedes any
  4996. // previous value for that axis. This deduplication is observable by accessing the computed value of this property."
  4997. // So, we deduplicate them here using a HashSet.
  4998. OrderedHashMap<FlyString, NonnullRefPtr<OpenTypeTaggedStyleValue>> axis_tags_map;
  4999. for (auto const& values : tag_values) {
  5000. TokenStream tag_tokens { values };
  5001. tag_tokens.discard_whitespace();
  5002. auto opentype_tag = parse_opentype_tag_value(tag_tokens);
  5003. tag_tokens.discard_whitespace();
  5004. auto number = parse_number_value(tag_tokens);
  5005. tag_tokens.discard_whitespace();
  5006. if (!opentype_tag || !number || tag_tokens.has_next_token())
  5007. return nullptr;
  5008. axis_tags_map.set(opentype_tag->string_value(), OpenTypeTaggedStyleValue::create(opentype_tag->string_value(), number.release_nonnull()));
  5009. }
  5010. // "The computed value contains the de-duplicated axis names, sorted in ascending order by code unit."
  5011. StyleValueVector axis_tags;
  5012. axis_tags.ensure_capacity(axis_tags_map.size());
  5013. for (auto const& [key, axis_tag] : axis_tags_map)
  5014. axis_tags.append(axis_tag);
  5015. quick_sort(axis_tags, [](auto& a, auto& b) {
  5016. return a->as_open_type_tagged().tag() < b->as_open_type_tagged().tag();
  5017. });
  5018. transaction.commit();
  5019. return StyleValueList::create(move(axis_tags), StyleValueList::Separator::Comma);
  5020. }
  5021. Vector<ParsedFontFace::Source> Parser::parse_as_font_face_src()
  5022. {
  5023. return parse_font_face_src(m_token_stream);
  5024. }
  5025. template<typename T>
  5026. Vector<ParsedFontFace::Source> Parser::parse_font_face_src(TokenStream<T>& component_values)
  5027. {
  5028. // FIXME: Get this information from the system somehow?
  5029. // Format-name table: https://www.w3.org/TR/css-fonts-4/#font-format-definitions
  5030. auto font_format_is_supported = [](StringView name) {
  5031. // The spec requires us to treat opentype and truetype as synonymous.
  5032. if (name.is_one_of_ignoring_ascii_case("opentype"sv, "truetype"sv, "woff"sv, "woff2"sv))
  5033. return true;
  5034. return false;
  5035. };
  5036. Vector<ParsedFontFace::Source> supported_sources;
  5037. auto list_of_source_token_lists = parse_a_comma_separated_list_of_component_values(component_values);
  5038. for (auto const& source_token_list : list_of_source_token_lists) {
  5039. TokenStream source_tokens { source_token_list };
  5040. source_tokens.discard_whitespace();
  5041. // <url> [ format(<font-format>)]?
  5042. // FIXME: Implement optional tech() function from CSS-Fonts-4.
  5043. if (auto maybe_url = parse_url_function(source_tokens); maybe_url.has_value()) {
  5044. auto url = maybe_url.release_value();
  5045. if (!url.is_valid()) {
  5046. continue;
  5047. }
  5048. Optional<FlyString> format;
  5049. source_tokens.discard_whitespace();
  5050. if (!source_tokens.has_next_token()) {
  5051. supported_sources.empend(move(url), format);
  5052. continue;
  5053. }
  5054. auto const& maybe_function = source_tokens.consume_a_token();
  5055. if (!maybe_function.is_function()) {
  5056. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @font-face src invalid (token after `url()` that isn't a function: {}); discarding.", maybe_function.to_debug_string());
  5057. return {};
  5058. }
  5059. auto const& function = maybe_function.function();
  5060. if (function.name.equals_ignoring_ascii_case("format"sv)) {
  5061. TokenStream format_tokens { function.value };
  5062. format_tokens.discard_whitespace();
  5063. auto const& format_name_token = format_tokens.consume_a_token();
  5064. StringView format_name;
  5065. if (format_name_token.is(Token::Type::Ident)) {
  5066. format_name = format_name_token.token().ident();
  5067. } else if (format_name_token.is(Token::Type::String)) {
  5068. format_name = format_name_token.token().string();
  5069. } else {
  5070. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @font-face src invalid (`format()` parameter not an ident or string; is: {}); discarding.", format_name_token.to_debug_string());
  5071. return {};
  5072. }
  5073. if (!font_format_is_supported(format_name)) {
  5074. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @font-face src format({}) not supported; skipping.", format_name);
  5075. continue;
  5076. }
  5077. format = FlyString::from_utf8(format_name).release_value_but_fixme_should_propagate_errors();
  5078. } else {
  5079. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @font-face src invalid (unrecognized function token `{}`); discarding.", function.name);
  5080. return {};
  5081. }
  5082. source_tokens.discard_whitespace();
  5083. if (source_tokens.has_next_token()) {
  5084. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @font-face src invalid (extra token `{}`); discarding.", source_tokens.next_token().to_debug_string());
  5085. return {};
  5086. }
  5087. supported_sources.empend(move(url), format);
  5088. continue;
  5089. }
  5090. auto const& first = source_tokens.consume_a_token();
  5091. if (first.is_function("local"sv)) {
  5092. if (first.function().value.is_empty()) {
  5093. continue;
  5094. }
  5095. supported_sources.empend(first.function().value.first().to_string(), Optional<FlyString> {});
  5096. continue;
  5097. }
  5098. dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @font-face src invalid (failed to parse url from: {}); discarding.", first.to_debug_string());
  5099. return {};
  5100. }
  5101. return supported_sources;
  5102. }
  5103. template Vector<ParsedFontFace::Source> Parser::parse_font_face_src(TokenStream<Token>& component_values);
  5104. template Vector<ParsedFontFace::Source> Parser::parse_font_face_src(TokenStream<ComponentValue>& component_values);
  5105. RefPtr<CSSStyleValue> Parser::parse_list_style_value(TokenStream<ComponentValue>& tokens)
  5106. {
  5107. RefPtr<CSSStyleValue> list_position;
  5108. RefPtr<CSSStyleValue> list_image;
  5109. RefPtr<CSSStyleValue> list_type;
  5110. int found_nones = 0;
  5111. Vector<PropertyID> remaining_longhands { PropertyID::ListStyleImage, PropertyID::ListStylePosition, PropertyID::ListStyleType };
  5112. auto transaction = tokens.begin_transaction();
  5113. while (tokens.has_next_token()) {
  5114. if (auto const& peek = tokens.next_token(); peek.is_ident("none"sv)) {
  5115. tokens.discard_a_token();
  5116. found_nones++;
  5117. continue;
  5118. }
  5119. auto property_and_value = parse_css_value_for_properties(remaining_longhands, tokens);
  5120. if (!property_and_value.has_value())
  5121. return nullptr;
  5122. auto& value = property_and_value->style_value;
  5123. remove_property(remaining_longhands, property_and_value->property);
  5124. switch (property_and_value->property) {
  5125. case PropertyID::ListStylePosition: {
  5126. VERIFY(!list_position);
  5127. list_position = value.release_nonnull();
  5128. continue;
  5129. }
  5130. case PropertyID::ListStyleImage: {
  5131. VERIFY(!list_image);
  5132. list_image = value.release_nonnull();
  5133. continue;
  5134. }
  5135. case PropertyID::ListStyleType: {
  5136. VERIFY(!list_type);
  5137. list_type = value.release_nonnull();
  5138. continue;
  5139. }
  5140. default:
  5141. VERIFY_NOT_REACHED();
  5142. }
  5143. }
  5144. if (found_nones > 2)
  5145. return nullptr;
  5146. if (found_nones == 2) {
  5147. if (list_image || list_type)
  5148. return nullptr;
  5149. auto none = CSSKeywordValue::create(Keyword::None);
  5150. list_image = none;
  5151. list_type = none;
  5152. } else if (found_nones == 1) {
  5153. if (list_image && list_type)
  5154. return nullptr;
  5155. auto none = CSSKeywordValue::create(Keyword::None);
  5156. if (!list_image)
  5157. list_image = none;
  5158. if (!list_type)
  5159. list_type = none;
  5160. }
  5161. if (!list_position)
  5162. list_position = property_initial_value(m_context.realm(), PropertyID::ListStylePosition);
  5163. if (!list_image)
  5164. list_image = property_initial_value(m_context.realm(), PropertyID::ListStyleImage);
  5165. if (!list_type)
  5166. list_type = property_initial_value(m_context.realm(), PropertyID::ListStyleType);
  5167. transaction.commit();
  5168. return ShorthandStyleValue::create(PropertyID::ListStyle,
  5169. { PropertyID::ListStylePosition, PropertyID::ListStyleImage, PropertyID::ListStyleType },
  5170. { list_position.release_nonnull(), list_image.release_nonnull(), list_type.release_nonnull() });
  5171. }
  5172. RefPtr<CSSStyleValue> Parser::parse_math_depth_value(TokenStream<ComponentValue>& tokens)
  5173. {
  5174. // https://w3c.github.io/mathml-core/#propdef-math-depth
  5175. // auto-add | add(<integer>) | <integer>
  5176. auto transaction = tokens.begin_transaction();
  5177. auto const& token = tokens.consume_a_token();
  5178. if (tokens.has_next_token())
  5179. return nullptr;
  5180. // auto-add
  5181. if (token.is_ident("auto-add"sv)) {
  5182. transaction.commit();
  5183. return MathDepthStyleValue::create_auto_add();
  5184. }
  5185. // FIXME: Make it easier to parse "thing that might be <bar> or literally anything that resolves to it" and get rid of this
  5186. auto parse_something_that_resolves_to_integer = [this](ComponentValue const& token) -> RefPtr<CSSStyleValue> {
  5187. if (token.is(Token::Type::Number) && token.token().number().is_integer())
  5188. return IntegerStyleValue::create(token.token().to_integer());
  5189. if (auto value = parse_calculated_value(token); value && value->resolves_to_number())
  5190. return value;
  5191. return nullptr;
  5192. };
  5193. // add(<integer>)
  5194. if (token.is_function("add"sv)) {
  5195. auto add_tokens = TokenStream { token.function().value };
  5196. add_tokens.discard_whitespace();
  5197. auto const& integer_token = add_tokens.consume_a_token();
  5198. add_tokens.discard_whitespace();
  5199. if (add_tokens.has_next_token())
  5200. return nullptr;
  5201. if (auto integer_value = parse_something_that_resolves_to_integer(integer_token)) {
  5202. transaction.commit();
  5203. return MathDepthStyleValue::create_add(integer_value.release_nonnull());
  5204. }
  5205. return nullptr;
  5206. }
  5207. // <integer>
  5208. if (auto integer_value = parse_something_that_resolves_to_integer(token)) {
  5209. transaction.commit();
  5210. return MathDepthStyleValue::create_integer(integer_value.release_nonnull());
  5211. }
  5212. return nullptr;
  5213. }
  5214. RefPtr<CSSStyleValue> Parser::parse_overflow_value(TokenStream<ComponentValue>& tokens)
  5215. {
  5216. auto transaction = tokens.begin_transaction();
  5217. auto maybe_x_value = parse_css_value_for_property(PropertyID::OverflowX, tokens);
  5218. if (!maybe_x_value)
  5219. return nullptr;
  5220. auto maybe_y_value = parse_css_value_for_property(PropertyID::OverflowY, tokens);
  5221. transaction.commit();
  5222. if (maybe_y_value) {
  5223. return ShorthandStyleValue::create(PropertyID::Overflow,
  5224. { PropertyID::OverflowX, PropertyID::OverflowY },
  5225. { maybe_x_value.release_nonnull(), maybe_y_value.release_nonnull() });
  5226. }
  5227. return ShorthandStyleValue::create(PropertyID::Overflow,
  5228. { PropertyID::OverflowX, PropertyID::OverflowY },
  5229. { *maybe_x_value, *maybe_x_value });
  5230. }
  5231. RefPtr<CSSStyleValue> Parser::parse_place_content_value(TokenStream<ComponentValue>& tokens)
  5232. {
  5233. auto transaction = tokens.begin_transaction();
  5234. auto maybe_align_content_value = parse_css_value_for_property(PropertyID::AlignContent, tokens);
  5235. if (!maybe_align_content_value)
  5236. return nullptr;
  5237. if (!tokens.has_next_token()) {
  5238. if (!property_accepts_keyword(PropertyID::JustifyContent, maybe_align_content_value->to_keyword()))
  5239. return nullptr;
  5240. transaction.commit();
  5241. return ShorthandStyleValue::create(PropertyID::PlaceContent,
  5242. { PropertyID::AlignContent, PropertyID::JustifyContent },
  5243. { *maybe_align_content_value, *maybe_align_content_value });
  5244. }
  5245. auto maybe_justify_content_value = parse_css_value_for_property(PropertyID::JustifyContent, tokens);
  5246. if (!maybe_justify_content_value)
  5247. return nullptr;
  5248. transaction.commit();
  5249. return ShorthandStyleValue::create(PropertyID::PlaceContent,
  5250. { PropertyID::AlignContent, PropertyID::JustifyContent },
  5251. { maybe_align_content_value.release_nonnull(), maybe_justify_content_value.release_nonnull() });
  5252. }
  5253. RefPtr<CSSStyleValue> Parser::parse_place_items_value(TokenStream<ComponentValue>& tokens)
  5254. {
  5255. auto transaction = tokens.begin_transaction();
  5256. auto maybe_align_items_value = parse_css_value_for_property(PropertyID::AlignItems, tokens);
  5257. if (!maybe_align_items_value)
  5258. return nullptr;
  5259. if (!tokens.has_next_token()) {
  5260. if (!property_accepts_keyword(PropertyID::JustifyItems, maybe_align_items_value->to_keyword()))
  5261. return nullptr;
  5262. transaction.commit();
  5263. return ShorthandStyleValue::create(PropertyID::PlaceItems,
  5264. { PropertyID::AlignItems, PropertyID::JustifyItems },
  5265. { *maybe_align_items_value, *maybe_align_items_value });
  5266. }
  5267. auto maybe_justify_items_value = parse_css_value_for_property(PropertyID::JustifyItems, tokens);
  5268. if (!maybe_justify_items_value)
  5269. return nullptr;
  5270. transaction.commit();
  5271. return ShorthandStyleValue::create(PropertyID::PlaceItems,
  5272. { PropertyID::AlignItems, PropertyID::JustifyItems },
  5273. { *maybe_align_items_value, *maybe_justify_items_value });
  5274. }
  5275. RefPtr<CSSStyleValue> Parser::parse_place_self_value(TokenStream<ComponentValue>& tokens)
  5276. {
  5277. auto transaction = tokens.begin_transaction();
  5278. auto maybe_align_self_value = parse_css_value_for_property(PropertyID::AlignSelf, tokens);
  5279. if (!maybe_align_self_value)
  5280. return nullptr;
  5281. if (!tokens.has_next_token()) {
  5282. if (!property_accepts_keyword(PropertyID::JustifySelf, maybe_align_self_value->to_keyword()))
  5283. return nullptr;
  5284. transaction.commit();
  5285. return ShorthandStyleValue::create(PropertyID::PlaceSelf,
  5286. { PropertyID::AlignSelf, PropertyID::JustifySelf },
  5287. { *maybe_align_self_value, *maybe_align_self_value });
  5288. }
  5289. auto maybe_justify_self_value = parse_css_value_for_property(PropertyID::JustifySelf, tokens);
  5290. if (!maybe_justify_self_value)
  5291. return nullptr;
  5292. transaction.commit();
  5293. return ShorthandStyleValue::create(PropertyID::PlaceSelf,
  5294. { PropertyID::AlignSelf, PropertyID::JustifySelf },
  5295. { *maybe_align_self_value, *maybe_justify_self_value });
  5296. }
  5297. RefPtr<CSSStyleValue> Parser::parse_quotes_value(TokenStream<ComponentValue>& tokens)
  5298. {
  5299. // https://www.w3.org/TR/css-content-3/#quotes-property
  5300. // auto | none | [ <string> <string> ]+
  5301. auto transaction = tokens.begin_transaction();
  5302. if (tokens.remaining_token_count() == 1) {
  5303. auto keyword = parse_keyword_value(tokens);
  5304. if (keyword && property_accepts_keyword(PropertyID::Quotes, keyword->to_keyword())) {
  5305. transaction.commit();
  5306. return keyword;
  5307. }
  5308. return nullptr;
  5309. }
  5310. // Parse an even number of <string> values.
  5311. if (tokens.remaining_token_count() % 2 != 0)
  5312. return nullptr;
  5313. StyleValueVector string_values;
  5314. while (tokens.has_next_token()) {
  5315. auto maybe_string = parse_string_value(tokens);
  5316. if (!maybe_string)
  5317. return nullptr;
  5318. string_values.append(maybe_string.release_nonnull());
  5319. }
  5320. transaction.commit();
  5321. return StyleValueList::create(move(string_values), StyleValueList::Separator::Space);
  5322. }
  5323. RefPtr<CSSStyleValue> Parser::parse_text_decoration_value(TokenStream<ComponentValue>& tokens)
  5324. {
  5325. RefPtr<CSSStyleValue> decoration_line;
  5326. RefPtr<CSSStyleValue> decoration_thickness;
  5327. RefPtr<CSSStyleValue> decoration_style;
  5328. RefPtr<CSSStyleValue> decoration_color;
  5329. auto remaining_longhands = Vector { PropertyID::TextDecorationColor, PropertyID::TextDecorationLine, PropertyID::TextDecorationStyle, PropertyID::TextDecorationThickness };
  5330. auto transaction = tokens.begin_transaction();
  5331. while (tokens.has_next_token()) {
  5332. auto property_and_value = parse_css_value_for_properties(remaining_longhands, tokens);
  5333. if (!property_and_value.has_value())
  5334. return nullptr;
  5335. auto& value = property_and_value->style_value;
  5336. remove_property(remaining_longhands, property_and_value->property);
  5337. switch (property_and_value->property) {
  5338. case PropertyID::TextDecorationColor: {
  5339. VERIFY(!decoration_color);
  5340. decoration_color = value.release_nonnull();
  5341. continue;
  5342. }
  5343. case PropertyID::TextDecorationLine: {
  5344. VERIFY(!decoration_line);
  5345. tokens.reconsume_current_input_token();
  5346. auto parsed_decoration_line = parse_text_decoration_line_value(tokens);
  5347. if (!parsed_decoration_line)
  5348. return nullptr;
  5349. decoration_line = parsed_decoration_line.release_nonnull();
  5350. continue;
  5351. }
  5352. case PropertyID::TextDecorationThickness: {
  5353. VERIFY(!decoration_thickness);
  5354. decoration_thickness = value.release_nonnull();
  5355. continue;
  5356. }
  5357. case PropertyID::TextDecorationStyle: {
  5358. VERIFY(!decoration_style);
  5359. decoration_style = value.release_nonnull();
  5360. continue;
  5361. }
  5362. default:
  5363. VERIFY_NOT_REACHED();
  5364. }
  5365. }
  5366. if (!decoration_line)
  5367. decoration_line = property_initial_value(m_context.realm(), PropertyID::TextDecorationLine);
  5368. if (!decoration_thickness)
  5369. decoration_thickness = property_initial_value(m_context.realm(), PropertyID::TextDecorationThickness);
  5370. if (!decoration_style)
  5371. decoration_style = property_initial_value(m_context.realm(), PropertyID::TextDecorationStyle);
  5372. if (!decoration_color)
  5373. decoration_color = property_initial_value(m_context.realm(), PropertyID::TextDecorationColor);
  5374. transaction.commit();
  5375. return ShorthandStyleValue::create(PropertyID::TextDecoration,
  5376. { PropertyID::TextDecorationLine, PropertyID::TextDecorationThickness, PropertyID::TextDecorationStyle, PropertyID::TextDecorationColor },
  5377. { decoration_line.release_nonnull(), decoration_thickness.release_nonnull(), decoration_style.release_nonnull(), decoration_color.release_nonnull() });
  5378. }
  5379. RefPtr<CSSStyleValue> Parser::parse_text_decoration_line_value(TokenStream<ComponentValue>& tokens)
  5380. {
  5381. StyleValueVector style_values;
  5382. while (tokens.has_next_token()) {
  5383. auto maybe_value = parse_css_value_for_property(PropertyID::TextDecorationLine, tokens);
  5384. if (!maybe_value)
  5385. break;
  5386. auto value = maybe_value.release_nonnull();
  5387. if (auto maybe_line = keyword_to_text_decoration_line(value->to_keyword()); maybe_line.has_value()) {
  5388. if (maybe_line == TextDecorationLine::None) {
  5389. if (!style_values.is_empty())
  5390. break;
  5391. return value;
  5392. }
  5393. if (style_values.contains_slow(value))
  5394. break;
  5395. style_values.append(move(value));
  5396. continue;
  5397. }
  5398. break;
  5399. }
  5400. if (style_values.is_empty())
  5401. return nullptr;
  5402. return StyleValueList::create(move(style_values), StyleValueList::Separator::Space);
  5403. }
  5404. RefPtr<CSSStyleValue> Parser::parse_easing_value(TokenStream<ComponentValue>& tokens)
  5405. {
  5406. auto transaction = tokens.begin_transaction();
  5407. tokens.discard_whitespace();
  5408. auto const& part = tokens.consume_a_token();
  5409. if (part.is(Token::Type::Ident)) {
  5410. auto name = part.token().ident();
  5411. auto maybe_simple_easing = [&] -> RefPtr<EasingStyleValue> {
  5412. if (name.equals_ignoring_ascii_case("linear"sv))
  5413. return EasingStyleValue::create(EasingStyleValue::Linear::identity());
  5414. if (name.equals_ignoring_ascii_case("ease"sv))
  5415. return EasingStyleValue::create(EasingStyleValue::CubicBezier::ease());
  5416. if (name.equals_ignoring_ascii_case("ease-in"sv))
  5417. return EasingStyleValue::create(EasingStyleValue::CubicBezier::ease_in());
  5418. if (name.equals_ignoring_ascii_case("ease-out"sv))
  5419. return EasingStyleValue::create(EasingStyleValue::CubicBezier::ease_out());
  5420. if (name.equals_ignoring_ascii_case("ease-in-out"sv))
  5421. return EasingStyleValue::create(EasingStyleValue::CubicBezier::ease_in_out());
  5422. if (name.equals_ignoring_ascii_case("step-start"sv))
  5423. return EasingStyleValue::create(EasingStyleValue::Steps::step_start());
  5424. if (name.equals_ignoring_ascii_case("step-end"sv))
  5425. return EasingStyleValue::create(EasingStyleValue::Steps::step_end());
  5426. return {};
  5427. }();
  5428. if (!maybe_simple_easing)
  5429. return nullptr;
  5430. transaction.commit();
  5431. return maybe_simple_easing;
  5432. }
  5433. if (!part.is_function())
  5434. return nullptr;
  5435. TokenStream argument_tokens { part.function().value };
  5436. auto comma_separated_arguments = parse_a_comma_separated_list_of_component_values(argument_tokens);
  5437. // Remove whitespace
  5438. for (auto& argument : comma_separated_arguments)
  5439. argument.remove_all_matching([](auto& value) { return value.is(Token::Type::Whitespace); });
  5440. auto name = part.function().name;
  5441. if (name.equals_ignoring_ascii_case("linear"sv)) {
  5442. // linear() = linear( [ <number> && <percentage>{0,2} ]# )
  5443. Vector<EasingStyleValue::Linear::Stop> stops;
  5444. for (auto const& argument : comma_separated_arguments) {
  5445. TokenStream argument_tokens { argument };
  5446. Optional<double> output;
  5447. Optional<double> first_input;
  5448. Optional<double> second_input;
  5449. if (argument_tokens.next_token().is(Token::Type::Number))
  5450. output = argument_tokens.consume_a_token().token().number_value();
  5451. if (argument_tokens.next_token().is(Token::Type::Percentage)) {
  5452. first_input = argument_tokens.consume_a_token().token().percentage() / 100;
  5453. if (argument_tokens.next_token().is(Token::Type::Percentage)) {
  5454. second_input = argument_tokens.consume_a_token().token().percentage() / 100;
  5455. }
  5456. }
  5457. if (argument_tokens.next_token().is(Token::Type::Number)) {
  5458. if (output.has_value())
  5459. return nullptr;
  5460. output = argument_tokens.consume_a_token().token().number_value();
  5461. }
  5462. if (argument_tokens.has_next_token() || !output.has_value())
  5463. return nullptr;
  5464. stops.append({ output.value(), first_input, first_input.has_value() });
  5465. if (second_input.has_value())
  5466. stops.append({ output.value(), second_input, true });
  5467. }
  5468. if (stops.is_empty())
  5469. return nullptr;
  5470. transaction.commit();
  5471. return EasingStyleValue::create(EasingStyleValue::Linear { move(stops) });
  5472. }
  5473. if (name.equals_ignoring_ascii_case("cubic-bezier"sv)) {
  5474. if (comma_separated_arguments.size() != 4)
  5475. return nullptr;
  5476. for (auto const& argument : comma_separated_arguments) {
  5477. if (argument.size() != 1)
  5478. return nullptr;
  5479. if (!argument[0].is(Token::Type::Number))
  5480. return nullptr;
  5481. }
  5482. EasingStyleValue::CubicBezier bezier {
  5483. comma_separated_arguments[0][0].token().number_value(),
  5484. comma_separated_arguments[1][0].token().number_value(),
  5485. comma_separated_arguments[2][0].token().number_value(),
  5486. comma_separated_arguments[3][0].token().number_value(),
  5487. };
  5488. if (bezier.x1 < 0.0 || bezier.x1 > 1.0 || bezier.x2 < 0.0 || bezier.x2 > 1.0)
  5489. return nullptr;
  5490. transaction.commit();
  5491. return EasingStyleValue::create(bezier);
  5492. }
  5493. if (name.equals_ignoring_ascii_case("steps"sv)) {
  5494. if (comma_separated_arguments.is_empty() || comma_separated_arguments.size() > 2)
  5495. return nullptr;
  5496. for (auto const& argument : comma_separated_arguments) {
  5497. if (argument.size() != 1)
  5498. return nullptr;
  5499. }
  5500. EasingStyleValue::Steps steps;
  5501. auto const& intervals_argument = comma_separated_arguments[0][0];
  5502. if (!intervals_argument.is(Token::Type::Number))
  5503. return nullptr;
  5504. if (!intervals_argument.token().number().is_integer())
  5505. return nullptr;
  5506. auto intervals = intervals_argument.token().to_integer();
  5507. if (comma_separated_arguments.size() == 2) {
  5508. TokenStream identifier_stream { comma_separated_arguments[1] };
  5509. auto keyword_value = parse_keyword_value(identifier_stream);
  5510. if (!keyword_value)
  5511. return nullptr;
  5512. switch (keyword_value->to_keyword()) {
  5513. case Keyword::JumpStart:
  5514. steps.position = EasingStyleValue::Steps::Position::JumpStart;
  5515. break;
  5516. case Keyword::JumpEnd:
  5517. steps.position = EasingStyleValue::Steps::Position::JumpEnd;
  5518. break;
  5519. case Keyword::JumpBoth:
  5520. steps.position = EasingStyleValue::Steps::Position::JumpBoth;
  5521. break;
  5522. case Keyword::JumpNone:
  5523. steps.position = EasingStyleValue::Steps::Position::JumpNone;
  5524. break;
  5525. case Keyword::Start:
  5526. steps.position = EasingStyleValue::Steps::Position::Start;
  5527. break;
  5528. case Keyword::End:
  5529. steps.position = EasingStyleValue::Steps::Position::End;
  5530. break;
  5531. default:
  5532. return nullptr;
  5533. }
  5534. }
  5535. // Perform extra validation
  5536. // https://drafts.csswg.org/css-easing/#step-easing-functions
  5537. // If the <step-position> is jump-none, the <integer> must be at least 2, or the function is invalid.
  5538. // Otherwise, the <integer> must be at least 1, or the function is invalid.
  5539. if (steps.position == EasingStyleValue::Steps::Position::JumpNone) {
  5540. if (intervals <= 1)
  5541. return nullptr;
  5542. } else if (intervals <= 0) {
  5543. return nullptr;
  5544. }
  5545. steps.number_of_intervals = intervals;
  5546. transaction.commit();
  5547. return EasingStyleValue::create(steps);
  5548. }
  5549. return nullptr;
  5550. }
  5551. // https://www.w3.org/TR/css-transforms-1/#transform-property
  5552. RefPtr<CSSStyleValue> Parser::parse_transform_value(TokenStream<ComponentValue>& tokens)
  5553. {
  5554. // <transform> = none | <transform-list>
  5555. // <transform-list> = <transform-function>+
  5556. if (auto none = parse_all_as_single_keyword_value(tokens, Keyword::None))
  5557. return none;
  5558. StyleValueVector transformations;
  5559. auto transaction = tokens.begin_transaction();
  5560. while (tokens.has_next_token()) {
  5561. auto const& part = tokens.consume_a_token();
  5562. if (!part.is_function())
  5563. return nullptr;
  5564. auto maybe_function = transform_function_from_string(part.function().name);
  5565. if (!maybe_function.has_value())
  5566. return nullptr;
  5567. auto function = maybe_function.release_value();
  5568. auto function_metadata = transform_function_metadata(function);
  5569. auto function_tokens = TokenStream { part.function().value };
  5570. auto arguments = parse_a_comma_separated_list_of_component_values(function_tokens);
  5571. if (arguments.size() > function_metadata.parameters.size()) {
  5572. dbgln_if(CSS_PARSER_DEBUG, "Too many arguments to {}. max: {}", part.function().name, function_metadata.parameters.size());
  5573. return nullptr;
  5574. }
  5575. if (arguments.size() < function_metadata.parameters.size() && function_metadata.parameters[arguments.size()].required) {
  5576. dbgln_if(CSS_PARSER_DEBUG, "Required parameter at position {} is missing", arguments.size());
  5577. return nullptr;
  5578. }
  5579. StyleValueVector values;
  5580. for (auto argument_index = 0u; argument_index < arguments.size(); ++argument_index) {
  5581. TokenStream argument_tokens { arguments[argument_index] };
  5582. argument_tokens.discard_whitespace();
  5583. auto const& value = argument_tokens.consume_a_token();
  5584. RefPtr<CSSMathValue> maybe_calc_value = parse_calculated_value(value);
  5585. switch (function_metadata.parameters[argument_index].type) {
  5586. case TransformFunctionParameterType::Angle: {
  5587. // These are `<angle> | <zero>` in the spec, so we have to check for both kinds.
  5588. if (maybe_calc_value && maybe_calc_value->resolves_to_angle()) {
  5589. values.append(maybe_calc_value.release_nonnull());
  5590. } else if (value.is(Token::Type::Number) && value.token().number_value() == 0) {
  5591. values.append(AngleStyleValue::create(Angle::make_degrees(0)));
  5592. } else {
  5593. // FIXME: Remove this reconsume once all parsing functions are TokenStream-based.
  5594. argument_tokens.reconsume_current_input_token();
  5595. auto dimension_value = parse_dimension_value(argument_tokens);
  5596. if (!dimension_value || !dimension_value->is_angle())
  5597. return nullptr;
  5598. values.append(dimension_value.release_nonnull());
  5599. }
  5600. break;
  5601. }
  5602. case TransformFunctionParameterType::Length:
  5603. case TransformFunctionParameterType::LengthNone: {
  5604. if (maybe_calc_value && maybe_calc_value->resolves_to_length()) {
  5605. argument_tokens.discard_a_token(); // calc()
  5606. values.append(maybe_calc_value.release_nonnull());
  5607. } else {
  5608. // FIXME: Remove this reconsume once all parsing functions are TokenStream-based.
  5609. argument_tokens.reconsume_current_input_token();
  5610. if (function_metadata.parameters[argument_index].type == TransformFunctionParameterType::LengthNone) {
  5611. auto keyword_transaction = argument_tokens.begin_transaction();
  5612. auto keyword_value = parse_keyword_value(argument_tokens);
  5613. if (keyword_value && keyword_value->to_keyword() == Keyword::None) {
  5614. values.append(keyword_value.release_nonnull());
  5615. keyword_transaction.commit();
  5616. break;
  5617. }
  5618. }
  5619. auto dimension_value = parse_dimension_value(argument_tokens);
  5620. if (!dimension_value || !dimension_value->is_length())
  5621. return nullptr;
  5622. values.append(dimension_value.release_nonnull());
  5623. }
  5624. break;
  5625. }
  5626. case TransformFunctionParameterType::LengthPercentage: {
  5627. if (maybe_calc_value && maybe_calc_value->resolves_to_length_percentage()) {
  5628. values.append(maybe_calc_value.release_nonnull());
  5629. } else {
  5630. // FIXME: Remove this reconsume once all parsing functions are TokenStream-based.
  5631. argument_tokens.reconsume_current_input_token();
  5632. auto dimension_value = parse_dimension_value(argument_tokens);
  5633. if (!dimension_value)
  5634. return nullptr;
  5635. if (dimension_value->is_percentage() || dimension_value->is_length())
  5636. values.append(dimension_value.release_nonnull());
  5637. else
  5638. return nullptr;
  5639. }
  5640. break;
  5641. }
  5642. case TransformFunctionParameterType::Number: {
  5643. if (maybe_calc_value && maybe_calc_value->resolves_to_number()) {
  5644. values.append(maybe_calc_value.release_nonnull());
  5645. } else {
  5646. // FIXME: Remove this reconsume once all parsing functions are TokenStream-based.
  5647. argument_tokens.reconsume_current_input_token();
  5648. auto number = parse_number_value(argument_tokens);
  5649. if (!number)
  5650. return nullptr;
  5651. values.append(number.release_nonnull());
  5652. }
  5653. break;
  5654. }
  5655. case TransformFunctionParameterType::NumberPercentage: {
  5656. if (maybe_calc_value && maybe_calc_value->resolves_to_number()) {
  5657. values.append(maybe_calc_value.release_nonnull());
  5658. } else {
  5659. // FIXME: Remove this reconsume once all parsing functions are TokenStream-based.
  5660. argument_tokens.reconsume_current_input_token();
  5661. auto number_or_percentage = parse_number_percentage_value(argument_tokens);
  5662. if (!number_or_percentage)
  5663. return nullptr;
  5664. values.append(number_or_percentage.release_nonnull());
  5665. }
  5666. break;
  5667. }
  5668. }
  5669. argument_tokens.discard_whitespace();
  5670. if (argument_tokens.has_next_token())
  5671. return nullptr;
  5672. }
  5673. transformations.append(TransformationStyleValue::create(function, move(values)));
  5674. }
  5675. transaction.commit();
  5676. return StyleValueList::create(move(transformations), StyleValueList::Separator::Space);
  5677. }
  5678. // https://www.w3.org/TR/css-transforms-1/#propdef-transform-origin
  5679. // FIXME: This only supports a 2D position
  5680. RefPtr<CSSStyleValue> Parser::parse_transform_origin_value(TokenStream<ComponentValue>& tokens)
  5681. {
  5682. enum class Axis {
  5683. None,
  5684. X,
  5685. Y,
  5686. };
  5687. struct AxisOffset {
  5688. Axis axis;
  5689. NonnullRefPtr<CSSStyleValue> offset;
  5690. };
  5691. auto to_axis_offset = [](RefPtr<CSSStyleValue> value) -> Optional<AxisOffset> {
  5692. if (!value)
  5693. return OptionalNone {};
  5694. if (value->is_percentage())
  5695. return AxisOffset { Axis::None, value->as_percentage() };
  5696. if (value->is_length())
  5697. return AxisOffset { Axis::None, value->as_length() };
  5698. if (value->is_keyword()) {
  5699. switch (value->to_keyword()) {
  5700. case Keyword::Top:
  5701. return AxisOffset { Axis::Y, PercentageStyleValue::create(Percentage(0)) };
  5702. case Keyword::Left:
  5703. return AxisOffset { Axis::X, PercentageStyleValue::create(Percentage(0)) };
  5704. case Keyword::Center:
  5705. return AxisOffset { Axis::None, PercentageStyleValue::create(Percentage(50)) };
  5706. case Keyword::Bottom:
  5707. return AxisOffset { Axis::Y, PercentageStyleValue::create(Percentage(100)) };
  5708. case Keyword::Right:
  5709. return AxisOffset { Axis::X, PercentageStyleValue::create(Percentage(100)) };
  5710. default:
  5711. return OptionalNone {};
  5712. }
  5713. }
  5714. if (value->is_math()) {
  5715. return AxisOffset { Axis::None, value->as_math() };
  5716. }
  5717. return OptionalNone {};
  5718. };
  5719. auto transaction = tokens.begin_transaction();
  5720. auto make_list = [&transaction](NonnullRefPtr<CSSStyleValue> const& x_value, NonnullRefPtr<CSSStyleValue> const& y_value) -> NonnullRefPtr<StyleValueList> {
  5721. transaction.commit();
  5722. return StyleValueList::create(StyleValueVector { x_value, y_value }, StyleValueList::Separator::Space);
  5723. };
  5724. switch (tokens.remaining_token_count()) {
  5725. case 1: {
  5726. auto single_value = to_axis_offset(parse_css_value_for_property(PropertyID::TransformOrigin, tokens));
  5727. if (!single_value.has_value())
  5728. return nullptr;
  5729. // If only one value is specified, the second value is assumed to be center.
  5730. // FIXME: If one or two values are specified, the third value is assumed to be 0px.
  5731. switch (single_value->axis) {
  5732. case Axis::None:
  5733. case Axis::X:
  5734. return make_list(single_value->offset, PercentageStyleValue::create(Percentage(50)));
  5735. case Axis::Y:
  5736. return make_list(PercentageStyleValue::create(Percentage(50)), single_value->offset);
  5737. }
  5738. VERIFY_NOT_REACHED();
  5739. }
  5740. case 2: {
  5741. auto first_value = to_axis_offset(parse_css_value_for_property(PropertyID::TransformOrigin, tokens));
  5742. auto second_value = to_axis_offset(parse_css_value_for_property(PropertyID::TransformOrigin, tokens));
  5743. if (!first_value.has_value() || !second_value.has_value())
  5744. return nullptr;
  5745. RefPtr<CSSStyleValue> x_value;
  5746. RefPtr<CSSStyleValue> y_value;
  5747. if (first_value->axis == Axis::X) {
  5748. x_value = first_value->offset;
  5749. } else if (first_value->axis == Axis::Y) {
  5750. y_value = first_value->offset;
  5751. }
  5752. if (second_value->axis == Axis::X) {
  5753. if (x_value)
  5754. return nullptr;
  5755. x_value = second_value->offset;
  5756. // Put the other in Y since its axis can't have been X
  5757. y_value = first_value->offset;
  5758. } else if (second_value->axis == Axis::Y) {
  5759. if (y_value)
  5760. return nullptr;
  5761. y_value = second_value->offset;
  5762. // Put the other in X since its axis can't have been Y
  5763. x_value = first_value->offset;
  5764. } else {
  5765. if (x_value) {
  5766. VERIFY(!y_value);
  5767. y_value = second_value->offset;
  5768. } else {
  5769. VERIFY(!x_value);
  5770. x_value = second_value->offset;
  5771. }
  5772. }
  5773. // If two or more values are defined and either no value is a keyword, or the only used keyword is center,
  5774. // then the first value represents the horizontal position (or offset) and the second represents the vertical position (or offset).
  5775. // FIXME: A third value always represents the Z position (or offset) and must be of type <length>.
  5776. if (first_value->axis == Axis::None && second_value->axis == Axis::None) {
  5777. x_value = first_value->offset;
  5778. y_value = second_value->offset;
  5779. }
  5780. return make_list(x_value.release_nonnull(), y_value.release_nonnull());
  5781. }
  5782. }
  5783. return nullptr;
  5784. }
  5785. RefPtr<CSSStyleValue> Parser::parse_transition_value(TokenStream<ComponentValue>& tokens)
  5786. {
  5787. if (auto none = parse_all_as_single_keyword_value(tokens, Keyword::None))
  5788. return none;
  5789. Vector<TransitionStyleValue::Transition> transitions;
  5790. auto transaction = tokens.begin_transaction();
  5791. while (tokens.has_next_token()) {
  5792. TransitionStyleValue::Transition transition;
  5793. auto time_value_count = 0;
  5794. while (tokens.has_next_token() && !tokens.next_token().is(Token::Type::Comma)) {
  5795. if (auto time = parse_time(tokens); time.has_value()) {
  5796. switch (time_value_count) {
  5797. case 0:
  5798. transition.duration = time.release_value();
  5799. break;
  5800. case 1:
  5801. transition.delay = time.release_value();
  5802. break;
  5803. default:
  5804. dbgln_if(CSS_PARSER_DEBUG, "Transition property has more than two time values");
  5805. return {};
  5806. }
  5807. time_value_count++;
  5808. continue;
  5809. }
  5810. if (auto easing = parse_easing_value(tokens)) {
  5811. if (transition.easing) {
  5812. dbgln_if(CSS_PARSER_DEBUG, "Transition property has multiple easing values");
  5813. return {};
  5814. }
  5815. transition.easing = easing->as_easing();
  5816. continue;
  5817. }
  5818. if (tokens.next_token().is(Token::Type::Ident)) {
  5819. if (transition.property_name) {
  5820. dbgln_if(CSS_PARSER_DEBUG, "Transition property has multiple property identifiers");
  5821. return {};
  5822. }
  5823. auto ident = tokens.consume_a_token().token().ident();
  5824. if (auto property = property_id_from_string(ident); property.has_value())
  5825. transition.property_name = CustomIdentStyleValue::create(ident);
  5826. continue;
  5827. }
  5828. dbgln_if(CSS_PARSER_DEBUG, "Transition property has unexpected token \"{}\"", tokens.next_token().to_string());
  5829. return {};
  5830. }
  5831. if (!transition.property_name)
  5832. transition.property_name = CustomIdentStyleValue::create("all"_fly_string);
  5833. if (!transition.easing)
  5834. transition.easing = EasingStyleValue::create(EasingStyleValue::CubicBezier::ease());
  5835. transitions.append(move(transition));
  5836. if (!tokens.next_token().is(Token::Type::Comma))
  5837. break;
  5838. tokens.discard_a_token();
  5839. }
  5840. transaction.commit();
  5841. return TransitionStyleValue::create(move(transitions));
  5842. }
  5843. RefPtr<CSSStyleValue> Parser::parse_as_css_value(PropertyID property_id)
  5844. {
  5845. auto component_values = parse_a_list_of_component_values(m_token_stream);
  5846. auto tokens = TokenStream(component_values);
  5847. auto parsed_value = parse_css_value(property_id, tokens);
  5848. if (parsed_value.is_error())
  5849. return nullptr;
  5850. return parsed_value.release_value();
  5851. }
  5852. Optional<CSS::GridSize> Parser::parse_grid_size(ComponentValue const& component_value)
  5853. {
  5854. if (component_value.is_function()) {
  5855. if (auto maybe_calculated = parse_calculated_value(component_value)) {
  5856. if (maybe_calculated->resolves_to_length_percentage())
  5857. return GridSize(LengthPercentage(maybe_calculated.release_nonnull()));
  5858. // FIXME: Support calculated <flex>
  5859. }
  5860. return {};
  5861. }
  5862. if (component_value.is_ident("auto"sv))
  5863. return GridSize::make_auto();
  5864. if (component_value.is_ident("max-content"sv))
  5865. return GridSize(GridSize::Type::MaxContent);
  5866. if (component_value.is_ident("min-content"sv))
  5867. return GridSize(GridSize::Type::MinContent);
  5868. auto dimension = parse_dimension(component_value);
  5869. if (!dimension.has_value())
  5870. return {};
  5871. if (dimension->is_length())
  5872. return GridSize(dimension->length());
  5873. else if (dimension->is_percentage())
  5874. return GridSize(dimension->percentage());
  5875. else if (dimension->is_flex())
  5876. return GridSize(dimension->flex());
  5877. return {};
  5878. }
  5879. RefPtr<CSSStyleValue> Parser::parse_translate_value(TokenStream<ComponentValue>& tokens)
  5880. {
  5881. if (tokens.remaining_token_count() == 1) {
  5882. // "none"
  5883. if (auto none = parse_all_as_single_keyword_value(tokens, Keyword::None))
  5884. return none;
  5885. }
  5886. auto transaction = tokens.begin_transaction();
  5887. auto maybe_x = parse_length_percentage(tokens);
  5888. if (!maybe_x.has_value())
  5889. return nullptr;
  5890. if (!tokens.has_next_token()) {
  5891. transaction.commit();
  5892. return TranslationStyleValue::create(maybe_x.release_value(), LengthPercentage(Length::make_px(0)));
  5893. }
  5894. auto maybe_y = parse_length_percentage(tokens);
  5895. if (!maybe_y.has_value())
  5896. return nullptr;
  5897. transaction.commit();
  5898. return TranslationStyleValue::create(maybe_x.release_value(), maybe_y.release_value());
  5899. }
  5900. Optional<CSS::GridFitContent> Parser::parse_fit_content(Vector<ComponentValue> const& component_values)
  5901. {
  5902. // https://www.w3.org/TR/css-grid-2/#valdef-grid-template-columns-fit-content
  5903. // 'fit-content( <length-percentage> )'
  5904. // Represents the formula max(minimum, min(limit, max-content)), where minimum represents an auto minimum (which is often, but not always,
  5905. // equal to a min-content minimum), and limit is the track sizing function passed as an argument to fit-content().
  5906. // This is essentially calculated as the smaller of minmax(auto, max-content) and minmax(auto, limit).
  5907. auto function_tokens = TokenStream(component_values);
  5908. function_tokens.discard_whitespace();
  5909. auto maybe_length_percentage = parse_length_percentage(function_tokens);
  5910. if (maybe_length_percentage.has_value())
  5911. return CSS::GridFitContent(CSS::GridSize(CSS::GridSize::Type::FitContent, maybe_length_percentage.value()));
  5912. return {};
  5913. }
  5914. Optional<CSS::GridMinMax> Parser::parse_min_max(Vector<ComponentValue> const& component_values)
  5915. {
  5916. // https://www.w3.org/TR/css-grid-2/#valdef-grid-template-columns-minmax
  5917. // 'minmax(min, max)'
  5918. // Defines a size range greater than or equal to min and less than or equal to max. If the max is
  5919. // less than the min, then the max will be floored by the min (essentially yielding minmax(min,
  5920. // min)). As a maximum, a <flex> value sets the track’s flex factor; it is invalid as a minimum.
  5921. auto function_tokens = TokenStream(component_values);
  5922. auto comma_separated_list = parse_a_comma_separated_list_of_component_values(function_tokens);
  5923. if (comma_separated_list.size() != 2)
  5924. return {};
  5925. TokenStream part_one_tokens { comma_separated_list[0] };
  5926. part_one_tokens.discard_whitespace();
  5927. if (!part_one_tokens.has_next_token())
  5928. return {};
  5929. NonnullRawPtr<ComponentValue const> current_token = part_one_tokens.consume_a_token();
  5930. auto min_grid_size = parse_grid_size(current_token);
  5931. TokenStream part_two_tokens { comma_separated_list[1] };
  5932. part_two_tokens.discard_whitespace();
  5933. if (!part_two_tokens.has_next_token())
  5934. return {};
  5935. current_token = part_two_tokens.consume_a_token();
  5936. auto max_grid_size = parse_grid_size(current_token);
  5937. if (min_grid_size.has_value() && max_grid_size.has_value()) {
  5938. // https://www.w3.org/TR/css-grid-2/#valdef-grid-template-columns-minmax
  5939. // As a maximum, a <flex> value sets the track’s flex factor; it is invalid as a minimum.
  5940. if (min_grid_size.value().is_flexible_length())
  5941. return {};
  5942. return CSS::GridMinMax(min_grid_size.value(), max_grid_size.value());
  5943. }
  5944. return {};
  5945. }
  5946. Optional<CSS::GridRepeat> Parser::parse_repeat(Vector<ComponentValue> const& component_values)
  5947. {
  5948. // https://www.w3.org/TR/css-grid-2/#repeat-syntax
  5949. // 7.2.3.1. Syntax of repeat()
  5950. // The generic form of the repeat() syntax is, approximately,
  5951. // repeat( [ <integer [1,∞]> | auto-fill | auto-fit ] , <track-list> )
  5952. auto is_auto_fill = false;
  5953. auto is_auto_fit = false;
  5954. auto function_tokens = TokenStream(component_values);
  5955. auto comma_separated_list = parse_a_comma_separated_list_of_component_values(function_tokens);
  5956. if (comma_separated_list.size() != 2)
  5957. return {};
  5958. // The first argument specifies the number of repetitions.
  5959. TokenStream part_one_tokens { comma_separated_list[0] };
  5960. part_one_tokens.discard_whitespace();
  5961. if (!part_one_tokens.has_next_token())
  5962. return {};
  5963. auto& current_token = part_one_tokens.consume_a_token();
  5964. auto repeat_count = 0;
  5965. if (current_token.is(Token::Type::Number) && current_token.token().number().is_integer() && current_token.token().number_value() > 0)
  5966. repeat_count = current_token.token().number_value();
  5967. else if (current_token.is_ident("auto-fill"sv))
  5968. is_auto_fill = true;
  5969. else if (current_token.is_ident("auto-fit"sv))
  5970. is_auto_fit = true;
  5971. // The second argument is a track list, which is repeated that number of times.
  5972. TokenStream part_two_tokens { comma_separated_list[1] };
  5973. part_two_tokens.discard_whitespace();
  5974. if (!part_two_tokens.has_next_token())
  5975. return {};
  5976. Vector<Variant<ExplicitGridTrack, GridLineNames>> repeat_params;
  5977. auto last_object_was_line_names = false;
  5978. while (part_two_tokens.has_next_token()) {
  5979. auto const& token = part_two_tokens.consume_a_token();
  5980. Vector<String> line_names;
  5981. if (token.is_block()) {
  5982. if (last_object_was_line_names)
  5983. return {};
  5984. last_object_was_line_names = true;
  5985. if (!token.block().is_square())
  5986. return {};
  5987. TokenStream block_tokens { token.block().value };
  5988. while (block_tokens.has_next_token()) {
  5989. auto const& current_block_token = block_tokens.consume_a_token();
  5990. line_names.append(current_block_token.token().ident().to_string());
  5991. block_tokens.discard_whitespace();
  5992. }
  5993. repeat_params.append(GridLineNames { move(line_names) });
  5994. part_two_tokens.discard_whitespace();
  5995. } else {
  5996. last_object_was_line_names = false;
  5997. auto track_sizing_function = parse_track_sizing_function(token);
  5998. if (!track_sizing_function.has_value())
  5999. return {};
  6000. // However, there are some restrictions:
  6001. // The repeat() notation can’t be nested.
  6002. if (track_sizing_function.value().is_repeat())
  6003. return {};
  6004. // Automatic repetitions (auto-fill or auto-fit) cannot be combined with intrinsic or flexible sizes.
  6005. // Note that 'auto' is also an intrinsic size (and thus not permitted) but we can't use
  6006. // track_sizing_function.is_auto(..) to check for it, as it requires AvailableSize, which is why there is
  6007. // a separate check for it below.
  6008. // https://www.w3.org/TR/css-grid-2/#repeat-syntax
  6009. // https://www.w3.org/TR/css-grid-2/#intrinsic-sizing-function
  6010. if (track_sizing_function.value().is_default()
  6011. && (track_sizing_function.value().grid_size().is_flexible_length() || token.is_ident("auto"sv))
  6012. && (is_auto_fill || is_auto_fit))
  6013. return {};
  6014. repeat_params.append(track_sizing_function.value());
  6015. part_two_tokens.discard_whitespace();
  6016. }
  6017. }
  6018. // Thus the precise syntax of the repeat() notation has several forms:
  6019. // <track-repeat> = repeat( [ <integer [1,∞]> ] , [ <line-names>? <track-size> ]+ <line-names>? )
  6020. // <auto-repeat> = repeat( [ auto-fill | auto-fit ] , [ <line-names>? <fixed-size> ]+ <line-names>? )
  6021. // <fixed-repeat> = repeat( [ <integer [1,∞]> ] , [ <line-names>? <fixed-size> ]+ <line-names>? )
  6022. // <name-repeat> = repeat( [ <integer [1,∞]> | auto-fill ], <line-names>+)
  6023. // The <track-repeat> variant can represent the repetition of any <track-size>, but is limited to a
  6024. // fixed number of repetitions.
  6025. // The <auto-repeat> variant can repeat automatically to fill a space, but requires definite track
  6026. // sizes so that the number of repetitions can be calculated. It can only appear once in the track
  6027. // list, but the same track list can also contain <fixed-repeat>s.
  6028. // The <name-repeat> variant is for adding line names to subgrids. It can only be used with the
  6029. // subgrid keyword and cannot specify track sizes, only line names.
  6030. // If a repeat() function that is not a <name-repeat> ends up placing two <line-names> adjacent to
  6031. // each other, the name lists are merged. For example, repeat(2, [a] 1fr [b]) is equivalent to [a]
  6032. // 1fr [b a] 1fr [b].
  6033. if (is_auto_fill)
  6034. return GridRepeat(GridTrackSizeList(move(repeat_params)), GridRepeat::Type::AutoFill);
  6035. else if (is_auto_fit)
  6036. return GridRepeat(GridTrackSizeList(move(repeat_params)), GridRepeat::Type::AutoFit);
  6037. else
  6038. return GridRepeat(GridTrackSizeList(move(repeat_params)), repeat_count);
  6039. }
  6040. Optional<CSS::ExplicitGridTrack> Parser::parse_track_sizing_function(ComponentValue const& token)
  6041. {
  6042. if (token.is_function()) {
  6043. auto const& function_token = token.function();
  6044. if (function_token.name.equals_ignoring_ascii_case("repeat"sv)) {
  6045. auto maybe_repeat = parse_repeat(function_token.value);
  6046. if (maybe_repeat.has_value())
  6047. return CSS::ExplicitGridTrack(maybe_repeat.value());
  6048. else
  6049. return {};
  6050. } else if (function_token.name.equals_ignoring_ascii_case("minmax"sv)) {
  6051. auto maybe_min_max_value = parse_min_max(function_token.value);
  6052. if (maybe_min_max_value.has_value())
  6053. return CSS::ExplicitGridTrack(maybe_min_max_value.value());
  6054. else
  6055. return {};
  6056. } else if (function_token.name.equals_ignoring_ascii_case("fit-content"sv)) {
  6057. auto maybe_fit_content_value = parse_fit_content(function_token.value);
  6058. if (maybe_fit_content_value.has_value())
  6059. return CSS::ExplicitGridTrack(maybe_fit_content_value.value());
  6060. return {};
  6061. } else if (auto maybe_calculated = parse_calculated_value(token)) {
  6062. return CSS::ExplicitGridTrack(GridSize(LengthPercentage(maybe_calculated.release_nonnull())));
  6063. }
  6064. return {};
  6065. } else if (token.is_ident("auto"sv)) {
  6066. return CSS::ExplicitGridTrack(GridSize(Length::make_auto()));
  6067. } else if (token.is_block()) {
  6068. return {};
  6069. } else {
  6070. auto grid_size = parse_grid_size(token);
  6071. if (!grid_size.has_value())
  6072. return {};
  6073. return CSS::ExplicitGridTrack(grid_size.value());
  6074. }
  6075. }
  6076. RefPtr<CSSStyleValue> Parser::parse_grid_track_size_list(TokenStream<ComponentValue>& tokens, bool allow_separate_line_name_blocks)
  6077. {
  6078. if (auto none = parse_all_as_single_keyword_value(tokens, Keyword::None))
  6079. return GridTrackSizeListStyleValue::make_none();
  6080. auto transaction = tokens.begin_transaction();
  6081. Vector<Variant<ExplicitGridTrack, GridLineNames>> track_list;
  6082. auto last_object_was_line_names = false;
  6083. while (tokens.has_next_token()) {
  6084. auto const& token = tokens.consume_a_token();
  6085. if (token.is_block()) {
  6086. if (last_object_was_line_names && !allow_separate_line_name_blocks) {
  6087. transaction.commit();
  6088. return GridTrackSizeListStyleValue::make_auto();
  6089. }
  6090. last_object_was_line_names = true;
  6091. Vector<String> line_names;
  6092. if (!token.block().is_square()) {
  6093. transaction.commit();
  6094. return GridTrackSizeListStyleValue::make_auto();
  6095. }
  6096. TokenStream block_tokens { token.block().value };
  6097. block_tokens.discard_whitespace();
  6098. while (block_tokens.has_next_token()) {
  6099. auto const& current_block_token = block_tokens.consume_a_token();
  6100. line_names.append(current_block_token.token().ident().to_string());
  6101. block_tokens.discard_whitespace();
  6102. }
  6103. track_list.append(GridLineNames { move(line_names) });
  6104. } else {
  6105. last_object_was_line_names = false;
  6106. auto track_sizing_function = parse_track_sizing_function(token);
  6107. if (!track_sizing_function.has_value()) {
  6108. transaction.commit();
  6109. return GridTrackSizeListStyleValue::make_auto();
  6110. }
  6111. // FIXME: Handle multiple repeat values (should combine them here, or remove
  6112. // any other ones if the first one is auto-fill, etc.)
  6113. track_list.append(track_sizing_function.value());
  6114. }
  6115. }
  6116. transaction.commit();
  6117. return GridTrackSizeListStyleValue::create(GridTrackSizeList(move(track_list)));
  6118. }
  6119. // https://www.w3.org/TR/css-grid-1/#grid-auto-flow-property
  6120. RefPtr<GridAutoFlowStyleValue> Parser::parse_grid_auto_flow_value(TokenStream<ComponentValue>& tokens)
  6121. {
  6122. // [ row | column ] || dense
  6123. if (!tokens.has_next_token())
  6124. return nullptr;
  6125. auto transaction = tokens.begin_transaction();
  6126. auto parse_axis = [&]() -> Optional<GridAutoFlowStyleValue::Axis> {
  6127. auto transaction = tokens.begin_transaction();
  6128. auto const& token = tokens.consume_a_token();
  6129. if (!token.is(Token::Type::Ident))
  6130. return {};
  6131. auto const& ident = token.token().ident();
  6132. if (ident.equals_ignoring_ascii_case("row"sv)) {
  6133. transaction.commit();
  6134. return GridAutoFlowStyleValue::Axis::Row;
  6135. } else if (ident.equals_ignoring_ascii_case("column"sv)) {
  6136. transaction.commit();
  6137. return GridAutoFlowStyleValue::Axis::Column;
  6138. }
  6139. return {};
  6140. };
  6141. auto parse_dense = [&]() -> Optional<GridAutoFlowStyleValue::Dense> {
  6142. auto transaction = tokens.begin_transaction();
  6143. auto const& token = tokens.consume_a_token();
  6144. if (!token.is(Token::Type::Ident))
  6145. return {};
  6146. auto const& ident = token.token().ident();
  6147. if (ident.equals_ignoring_ascii_case("dense"sv)) {
  6148. transaction.commit();
  6149. return GridAutoFlowStyleValue::Dense::Yes;
  6150. }
  6151. return {};
  6152. };
  6153. Optional<GridAutoFlowStyleValue::Axis> axis;
  6154. Optional<GridAutoFlowStyleValue::Dense> dense;
  6155. if (axis = parse_axis(); axis.has_value()) {
  6156. dense = parse_dense();
  6157. } else if (dense = parse_dense(); dense.has_value()) {
  6158. axis = parse_axis();
  6159. }
  6160. if (tokens.has_next_token())
  6161. return nullptr;
  6162. transaction.commit();
  6163. return GridAutoFlowStyleValue::create(axis.value_or(GridAutoFlowStyleValue::Axis::Row), dense.value_or(GridAutoFlowStyleValue::Dense::No));
  6164. }
  6165. // https://drafts.csswg.org/css-overflow/#propdef-scrollbar-gutter
  6166. RefPtr<CSSStyleValue> Parser::parse_scrollbar_gutter_value(TokenStream<ComponentValue>& tokens)
  6167. {
  6168. // auto | stable && both-edges?
  6169. if (!tokens.has_next_token())
  6170. return nullptr;
  6171. auto transaction = tokens.begin_transaction();
  6172. auto parse_stable = [&]() -> Optional<bool> {
  6173. auto transaction = tokens.begin_transaction();
  6174. auto const& token = tokens.consume_a_token();
  6175. if (!token.is(Token::Type::Ident))
  6176. return {};
  6177. auto const& ident = token.token().ident();
  6178. if (ident.equals_ignoring_ascii_case("auto"sv)) {
  6179. transaction.commit();
  6180. return false;
  6181. } else if (ident.equals_ignoring_ascii_case("stable"sv)) {
  6182. transaction.commit();
  6183. return true;
  6184. }
  6185. return {};
  6186. };
  6187. auto parse_both_edges = [&]() -> Optional<bool> {
  6188. auto transaction = tokens.begin_transaction();
  6189. auto const& token = tokens.consume_a_token();
  6190. if (!token.is(Token::Type::Ident))
  6191. return {};
  6192. auto const& ident = token.token().ident();
  6193. if (ident.equals_ignoring_ascii_case("both-edges"sv)) {
  6194. transaction.commit();
  6195. return true;
  6196. }
  6197. return {};
  6198. };
  6199. Optional<bool> stable;
  6200. Optional<bool> both_edges;
  6201. if (stable = parse_stable(); stable.has_value()) {
  6202. if (stable.value())
  6203. both_edges = parse_both_edges();
  6204. } else if (both_edges = parse_both_edges(); both_edges.has_value()) {
  6205. stable = parse_stable();
  6206. if (!stable.has_value() || !stable.value())
  6207. return nullptr;
  6208. }
  6209. if (tokens.has_next_token())
  6210. return nullptr;
  6211. transaction.commit();
  6212. ScrollbarGutter gutter_value;
  6213. if (both_edges.has_value())
  6214. gutter_value = ScrollbarGutter::BothEdges;
  6215. else if (stable.has_value() && stable.value())
  6216. gutter_value = ScrollbarGutter::Stable;
  6217. else
  6218. gutter_value = ScrollbarGutter::Auto;
  6219. return ScrollbarGutterStyleValue::create(gutter_value);
  6220. }
  6221. RefPtr<CSSStyleValue> Parser::parse_grid_auto_track_sizes(TokenStream<ComponentValue>& tokens)
  6222. {
  6223. // https://www.w3.org/TR/css-grid-2/#auto-tracks
  6224. // <track-size>+
  6225. Vector<Variant<ExplicitGridTrack, GridLineNames>> track_list;
  6226. auto transaction = tokens.begin_transaction();
  6227. while (tokens.has_next_token()) {
  6228. auto const& token = tokens.consume_a_token();
  6229. auto track_sizing_function = parse_track_sizing_function(token);
  6230. if (!track_sizing_function.has_value()) {
  6231. transaction.commit();
  6232. return GridTrackSizeListStyleValue::make_auto();
  6233. }
  6234. // FIXME: Handle multiple repeat values (should combine them here, or remove
  6235. // any other ones if the first one is auto-fill, etc.)
  6236. track_list.append(track_sizing_function.value());
  6237. }
  6238. transaction.commit();
  6239. return GridTrackSizeListStyleValue::create(GridTrackSizeList(move(track_list)));
  6240. }
  6241. RefPtr<GridTrackPlacementStyleValue> Parser::parse_grid_track_placement(TokenStream<ComponentValue>& tokens)
  6242. {
  6243. // FIXME: This shouldn't be needed. Right now, the below code returns a CSSStyleValue even if no tokens are consumed!
  6244. if (!tokens.has_next_token())
  6245. return nullptr;
  6246. // https://www.w3.org/TR/css-grid-2/#line-placement
  6247. // Line-based Placement: the grid-row-start, grid-column-start, grid-row-end, and grid-column-end properties
  6248. // <grid-line> =
  6249. // auto |
  6250. // <custom-ident> |
  6251. // [ <integer> && <custom-ident>? ] |
  6252. // [ span && [ <integer> || <custom-ident> ] ]
  6253. auto is_valid_integer = [](auto& token) -> bool {
  6254. // An <integer> value of zero makes the declaration invalid.
  6255. if (token.is(Token::Type::Number) && token.token().number().is_integer() && token.token().number_value() != 0)
  6256. return true;
  6257. return false;
  6258. };
  6259. auto parse_custom_ident = [this](auto& tokens) {
  6260. // The <custom-ident> additionally excludes the keywords span and auto.
  6261. return parse_custom_ident_value(tokens, { "span"sv, "auto"sv });
  6262. };
  6263. auto transaction = tokens.begin_transaction();
  6264. // FIXME: Handle the single-token case inside the loop instead, so that we can more easily call this from
  6265. // `parse_grid_area_shorthand_value()` using a single TokenStream.
  6266. if (tokens.remaining_token_count() == 1) {
  6267. if (auto custom_ident = parse_custom_ident(tokens)) {
  6268. transaction.commit();
  6269. return GridTrackPlacementStyleValue::create(GridTrackPlacement::make_line({}, custom_ident->custom_ident().to_string()));
  6270. }
  6271. auto const& token = tokens.consume_a_token();
  6272. if (auto maybe_calculated = parse_calculated_value(token); maybe_calculated && maybe_calculated->resolves_to_number()) {
  6273. transaction.commit();
  6274. return GridTrackPlacementStyleValue::create(GridTrackPlacement::make_line(static_cast<int>(maybe_calculated->resolve_integer().value()), {}));
  6275. }
  6276. if (token.is_ident("auto"sv)) {
  6277. transaction.commit();
  6278. return GridTrackPlacementStyleValue::create(GridTrackPlacement::make_auto());
  6279. }
  6280. if (token.is_ident("span"sv)) {
  6281. transaction.commit();
  6282. return GridTrackPlacementStyleValue::create(GridTrackPlacement::make_span(1));
  6283. }
  6284. if (is_valid_integer(token)) {
  6285. transaction.commit();
  6286. return GridTrackPlacementStyleValue::create(GridTrackPlacement::make_line(static_cast<int>(token.token().number_value()), {}));
  6287. }
  6288. return nullptr;
  6289. }
  6290. auto span_value = false;
  6291. auto span_or_position_value = 0;
  6292. String identifier_value;
  6293. while (tokens.has_next_token()) {
  6294. auto const& token = tokens.next_token();
  6295. if (token.is_ident("auto"sv))
  6296. return nullptr;
  6297. if (token.is_ident("span"sv)) {
  6298. if (span_value)
  6299. return nullptr;
  6300. tokens.discard_a_token(); // span
  6301. span_value = true;
  6302. continue;
  6303. }
  6304. if (is_valid_integer(token)) {
  6305. if (span_or_position_value != 0)
  6306. return nullptr;
  6307. span_or_position_value = static_cast<int>(tokens.consume_a_token().token().to_integer());
  6308. continue;
  6309. }
  6310. if (auto custom_ident = parse_custom_ident(tokens)) {
  6311. if (!identifier_value.is_empty())
  6312. return nullptr;
  6313. identifier_value = custom_ident->custom_ident().to_string();
  6314. continue;
  6315. }
  6316. break;
  6317. }
  6318. // Negative integers or zero are invalid.
  6319. if (span_value && span_or_position_value < 1)
  6320. return nullptr;
  6321. // If the <integer> is omitted, it defaults to 1.
  6322. if (span_or_position_value == 0)
  6323. span_or_position_value = 1;
  6324. transaction.commit();
  6325. if (!identifier_value.is_empty())
  6326. return GridTrackPlacementStyleValue::create(GridTrackPlacement::make_line(span_or_position_value, identifier_value));
  6327. return GridTrackPlacementStyleValue::create(GridTrackPlacement::make_span(span_or_position_value));
  6328. }
  6329. RefPtr<CSSStyleValue> Parser::parse_grid_track_placement_shorthand_value(PropertyID property_id, TokenStream<ComponentValue>& tokens)
  6330. {
  6331. auto start_property = (property_id == PropertyID::GridColumn) ? PropertyID::GridColumnStart : PropertyID::GridRowStart;
  6332. auto end_property = (property_id == PropertyID::GridColumn) ? PropertyID::GridColumnEnd : PropertyID::GridRowEnd;
  6333. auto transaction = tokens.begin_transaction();
  6334. NonnullRawPtr<ComponentValue const> current_token = tokens.consume_a_token();
  6335. Vector<ComponentValue> track_start_placement_tokens;
  6336. while (true) {
  6337. if (current_token->is_delim('/'))
  6338. break;
  6339. track_start_placement_tokens.append(current_token);
  6340. if (!tokens.has_next_token())
  6341. break;
  6342. current_token = tokens.consume_a_token();
  6343. }
  6344. Vector<ComponentValue> track_end_placement_tokens;
  6345. if (tokens.has_next_token()) {
  6346. current_token = tokens.consume_a_token();
  6347. while (true) {
  6348. track_end_placement_tokens.append(current_token);
  6349. if (!tokens.has_next_token())
  6350. break;
  6351. current_token = tokens.consume_a_token();
  6352. }
  6353. }
  6354. TokenStream track_start_placement_token_stream { track_start_placement_tokens };
  6355. auto parsed_start_value = parse_grid_track_placement(track_start_placement_token_stream);
  6356. if (parsed_start_value && track_end_placement_tokens.is_empty()) {
  6357. transaction.commit();
  6358. if (parsed_start_value->grid_track_placement().has_identifier()) {
  6359. auto custom_ident = parsed_start_value.release_nonnull();
  6360. return ShorthandStyleValue::create(property_id, { start_property, end_property }, { custom_ident, custom_ident });
  6361. }
  6362. return ShorthandStyleValue::create(property_id,
  6363. { start_property, end_property },
  6364. { parsed_start_value.release_nonnull(), GridTrackPlacementStyleValue::create(GridTrackPlacement::make_auto()) });
  6365. }
  6366. TokenStream track_end_placement_token_stream { track_end_placement_tokens };
  6367. auto parsed_end_value = parse_grid_track_placement(track_end_placement_token_stream);
  6368. if (parsed_start_value && parsed_end_value) {
  6369. transaction.commit();
  6370. return ShorthandStyleValue::create(property_id,
  6371. { start_property, end_property },
  6372. { parsed_start_value.release_nonnull(), parsed_end_value.release_nonnull() });
  6373. }
  6374. return nullptr;
  6375. }
  6376. // https://www.w3.org/TR/css-grid-2/#explicit-grid-shorthand
  6377. // 7.4. Explicit Grid Shorthand: the grid-template property
  6378. RefPtr<CSSStyleValue> Parser::parse_grid_track_size_list_shorthand_value(PropertyID property_id, TokenStream<ComponentValue>& tokens)
  6379. {
  6380. // The grid-template property is a shorthand for setting grid-template-columns, grid-template-rows,
  6381. // and grid-template-areas in a single declaration. It has several distinct syntax forms:
  6382. // none
  6383. // - Sets all three properties to their initial values (none).
  6384. // <'grid-template-rows'> / <'grid-template-columns'>
  6385. // - Sets grid-template-rows and grid-template-columns to the specified values, respectively, and sets grid-template-areas to none.
  6386. // [ <line-names>? <string> <track-size>? <line-names>? ]+ [ / <explicit-track-list> ]?
  6387. // - Sets grid-template-areas to the strings listed.
  6388. // - Sets grid-template-rows to the <track-size>s following each string (filling in auto for any missing sizes),
  6389. // and splicing in the named lines defined before/after each size.
  6390. // - Sets grid-template-columns to the track listing specified after the slash (or none, if not specified).
  6391. auto transaction = tokens.begin_transaction();
  6392. // FIXME: Read the parts in place if possible, instead of constructing separate vectors and streams.
  6393. Vector<ComponentValue> template_rows_tokens;
  6394. Vector<ComponentValue> template_columns_tokens;
  6395. Vector<ComponentValue> template_area_tokens;
  6396. bool found_forward_slash = false;
  6397. while (tokens.has_next_token()) {
  6398. auto& token = tokens.consume_a_token();
  6399. if (token.is_delim('/')) {
  6400. if (found_forward_slash)
  6401. return nullptr;
  6402. found_forward_slash = true;
  6403. continue;
  6404. }
  6405. if (found_forward_slash) {
  6406. template_columns_tokens.append(token);
  6407. continue;
  6408. }
  6409. if (token.is(Token::Type::String))
  6410. template_area_tokens.append(token);
  6411. else
  6412. template_rows_tokens.append(token);
  6413. }
  6414. TokenStream template_area_token_stream { template_area_tokens };
  6415. TokenStream template_rows_token_stream { template_rows_tokens };
  6416. TokenStream template_columns_token_stream { template_columns_tokens };
  6417. auto parsed_template_areas_values = parse_grid_template_areas_value(template_area_token_stream);
  6418. auto parsed_template_rows_values = parse_grid_track_size_list(template_rows_token_stream, true);
  6419. auto parsed_template_columns_values = parse_grid_track_size_list(template_columns_token_stream);
  6420. if (template_area_token_stream.has_next_token()
  6421. || template_rows_token_stream.has_next_token()
  6422. || template_columns_token_stream.has_next_token())
  6423. return nullptr;
  6424. transaction.commit();
  6425. return ShorthandStyleValue::create(property_id,
  6426. { PropertyID::GridTemplateAreas, PropertyID::GridTemplateRows, PropertyID::GridTemplateColumns },
  6427. { parsed_template_areas_values.release_nonnull(), parsed_template_rows_values.release_nonnull(), parsed_template_columns_values.release_nonnull() });
  6428. }
  6429. RefPtr<CSSStyleValue> Parser::parse_grid_area_shorthand_value(TokenStream<ComponentValue>& tokens)
  6430. {
  6431. auto transaction = tokens.begin_transaction();
  6432. auto parse_placement_tokens = [&](Vector<ComponentValue>& placement_tokens, bool check_for_delimiter = true) -> void {
  6433. while (tokens.has_next_token()) {
  6434. auto& current_token = tokens.consume_a_token();
  6435. if (check_for_delimiter && current_token.is_delim('/'))
  6436. break;
  6437. placement_tokens.append(current_token);
  6438. }
  6439. };
  6440. Vector<ComponentValue> row_start_placement_tokens;
  6441. parse_placement_tokens(row_start_placement_tokens);
  6442. Vector<ComponentValue> column_start_placement_tokens;
  6443. if (tokens.has_next_token())
  6444. parse_placement_tokens(column_start_placement_tokens);
  6445. Vector<ComponentValue> row_end_placement_tokens;
  6446. if (tokens.has_next_token())
  6447. parse_placement_tokens(row_end_placement_tokens);
  6448. Vector<ComponentValue> column_end_placement_tokens;
  6449. if (tokens.has_next_token())
  6450. parse_placement_tokens(column_end_placement_tokens, false);
  6451. // https://www.w3.org/TR/css-grid-2/#placement-shorthands
  6452. // The grid-area property is a shorthand for grid-row-start, grid-column-start, grid-row-end and
  6453. // grid-column-end.
  6454. TokenStream row_start_placement_token_stream { row_start_placement_tokens };
  6455. auto row_start_style_value = parse_grid_track_placement(row_start_placement_token_stream);
  6456. if (row_start_placement_token_stream.has_next_token())
  6457. return nullptr;
  6458. TokenStream column_start_placement_token_stream { column_start_placement_tokens };
  6459. auto column_start_style_value = parse_grid_track_placement(column_start_placement_token_stream);
  6460. if (column_start_placement_token_stream.has_next_token())
  6461. return nullptr;
  6462. TokenStream row_end_placement_token_stream { row_end_placement_tokens };
  6463. auto row_end_style_value = parse_grid_track_placement(row_end_placement_token_stream);
  6464. if (row_end_placement_token_stream.has_next_token())
  6465. return nullptr;
  6466. TokenStream column_end_placement_token_stream { column_end_placement_tokens };
  6467. auto column_end_style_value = parse_grid_track_placement(column_end_placement_token_stream);
  6468. if (column_end_placement_token_stream.has_next_token())
  6469. return nullptr;
  6470. // If four <grid-line> values are specified, grid-row-start is set to the first value, grid-column-start
  6471. // is set to the second value, grid-row-end is set to the third value, and grid-column-end is set to the
  6472. // fourth value.
  6473. auto row_start = GridTrackPlacement::make_auto();
  6474. auto column_start = GridTrackPlacement::make_auto();
  6475. auto row_end = GridTrackPlacement::make_auto();
  6476. auto column_end = GridTrackPlacement::make_auto();
  6477. if (row_start_style_value)
  6478. row_start = row_start_style_value.release_nonnull()->as_grid_track_placement().grid_track_placement();
  6479. // When grid-column-start is omitted, if grid-row-start is a <custom-ident>, all four longhands are set to
  6480. // that value. Otherwise, it is set to auto.
  6481. if (column_start_style_value)
  6482. column_start = column_start_style_value.release_nonnull()->as_grid_track_placement().grid_track_placement();
  6483. else
  6484. column_start = row_start;
  6485. // When grid-row-end is omitted, if grid-row-start is a <custom-ident>, grid-row-end is set to that
  6486. // <custom-ident>; otherwise, it is set to auto.
  6487. if (row_end_style_value)
  6488. row_end = row_end_style_value.release_nonnull()->as_grid_track_placement().grid_track_placement();
  6489. else
  6490. row_end = column_start;
  6491. // When grid-column-end is omitted, if grid-column-start is a <custom-ident>, grid-column-end is set to
  6492. // that <custom-ident>; otherwise, it is set to auto.
  6493. if (column_end_style_value)
  6494. column_end = column_end_style_value.release_nonnull()->as_grid_track_placement().grid_track_placement();
  6495. else
  6496. column_end = row_end;
  6497. transaction.commit();
  6498. return ShorthandStyleValue::create(PropertyID::GridArea,
  6499. { PropertyID::GridRowStart, PropertyID::GridColumnStart, PropertyID::GridRowEnd, PropertyID::GridColumnEnd },
  6500. { GridTrackPlacementStyleValue::create(row_start), GridTrackPlacementStyleValue::create(column_start), GridTrackPlacementStyleValue::create(row_end), GridTrackPlacementStyleValue::create(column_end) });
  6501. }
  6502. RefPtr<CSSStyleValue> Parser::parse_grid_shorthand_value(TokenStream<ComponentValue>& tokens)
  6503. {
  6504. // <'grid-template'> |
  6505. // FIXME: <'grid-template-rows'> / [ auto-flow && dense? ] <'grid-auto-columns'>? |
  6506. // FIXME: [ auto-flow && dense? ] <'grid-auto-rows'>? / <'grid-template-columns'>
  6507. return parse_grid_track_size_list_shorthand_value(PropertyID::Grid, tokens);
  6508. }
  6509. // https://www.w3.org/TR/css-grid-1/#grid-template-areas-property
  6510. RefPtr<CSSStyleValue> Parser::parse_grid_template_areas_value(TokenStream<ComponentValue>& tokens)
  6511. {
  6512. // none | <string>+
  6513. Vector<Vector<String>> grid_area_rows;
  6514. if (auto none = parse_all_as_single_keyword_value(tokens, Keyword::None))
  6515. return GridTemplateAreaStyleValue::create(move(grid_area_rows));
  6516. auto transaction = tokens.begin_transaction();
  6517. while (tokens.has_next_token() && tokens.next_token().is(Token::Type::String)) {
  6518. Vector<String> grid_area_columns;
  6519. auto const parts = MUST(tokens.consume_a_token().token().string().to_string().split(' '));
  6520. for (auto& part : parts) {
  6521. grid_area_columns.append(part);
  6522. }
  6523. grid_area_rows.append(move(grid_area_columns));
  6524. }
  6525. transaction.commit();
  6526. return GridTemplateAreaStyleValue::create(grid_area_rows);
  6527. }
  6528. static bool block_contains_var_or_attr(SimpleBlock const& block);
  6529. static bool function_contains_var_or_attr(Function const& function)
  6530. {
  6531. if (function.name.equals_ignoring_ascii_case("var"sv) || function.name.equals_ignoring_ascii_case("attr"sv))
  6532. return true;
  6533. for (auto const& token : function.value) {
  6534. if (token.is_function() && function_contains_var_or_attr(token.function()))
  6535. return true;
  6536. if (token.is_block() && block_contains_var_or_attr(token.block()))
  6537. return true;
  6538. }
  6539. return false;
  6540. }
  6541. bool block_contains_var_or_attr(SimpleBlock const& block)
  6542. {
  6543. for (auto const& token : block.value) {
  6544. if (token.is_function() && function_contains_var_or_attr(token.function()))
  6545. return true;
  6546. if (token.is_block() && block_contains_var_or_attr(token.block()))
  6547. return true;
  6548. }
  6549. return false;
  6550. }
  6551. Parser::ParseErrorOr<NonnullRefPtr<CSSStyleValue>> Parser::parse_css_value(PropertyID property_id, TokenStream<ComponentValue>& unprocessed_tokens, Optional<String> original_source_text)
  6552. {
  6553. m_context.set_current_property_id(property_id);
  6554. Vector<ComponentValue> component_values;
  6555. bool contains_var_or_attr = false;
  6556. bool const property_accepts_custom_ident = property_accepts_type(property_id, ValueType::CustomIdent);
  6557. while (unprocessed_tokens.has_next_token()) {
  6558. auto const& token = unprocessed_tokens.consume_a_token();
  6559. if (token.is(Token::Type::Semicolon)) {
  6560. unprocessed_tokens.reconsume_current_input_token();
  6561. break;
  6562. }
  6563. if (property_id != PropertyID::Custom) {
  6564. if (token.is(Token::Type::Whitespace))
  6565. continue;
  6566. if (!property_accepts_custom_ident && token.is(Token::Type::Ident) && has_ignored_vendor_prefix(token.token().ident()))
  6567. return ParseError::IncludesIgnoredVendorPrefix;
  6568. }
  6569. if (!contains_var_or_attr) {
  6570. if (token.is_function() && function_contains_var_or_attr(token.function()))
  6571. contains_var_or_attr = true;
  6572. else if (token.is_block() && block_contains_var_or_attr(token.block()))
  6573. contains_var_or_attr = true;
  6574. }
  6575. component_values.append(token);
  6576. }
  6577. if (property_id == PropertyID::Custom || contains_var_or_attr)
  6578. return UnresolvedStyleValue::create(move(component_values), contains_var_or_attr, original_source_text);
  6579. if (component_values.is_empty())
  6580. return ParseError::SyntaxError;
  6581. auto tokens = TokenStream { component_values };
  6582. if (component_values.size() == 1) {
  6583. if (auto parsed_value = parse_builtin_value(tokens))
  6584. return parsed_value.release_nonnull();
  6585. }
  6586. // Special-case property handling
  6587. switch (property_id) {
  6588. case PropertyID::AspectRatio:
  6589. if (auto parsed_value = parse_aspect_ratio_value(tokens); parsed_value && !tokens.has_next_token())
  6590. return parsed_value.release_nonnull();
  6591. return ParseError::SyntaxError;
  6592. case PropertyID::BackdropFilter:
  6593. case PropertyID::Filter:
  6594. if (auto parsed_value = parse_filter_value_list_value(tokens); parsed_value && !tokens.has_next_token())
  6595. return parsed_value.release_nonnull();
  6596. return ParseError::SyntaxError;
  6597. case PropertyID::Background:
  6598. if (auto parsed_value = parse_background_value(tokens); parsed_value && !tokens.has_next_token())
  6599. return parsed_value.release_nonnull();
  6600. return ParseError::SyntaxError;
  6601. case PropertyID::BackgroundAttachment:
  6602. case PropertyID::BackgroundClip:
  6603. case PropertyID::BackgroundImage:
  6604. case PropertyID::BackgroundOrigin:
  6605. if (auto parsed_value = parse_simple_comma_separated_value_list(property_id, tokens))
  6606. return parsed_value.release_nonnull();
  6607. return ParseError::SyntaxError;
  6608. case PropertyID::BackgroundPosition:
  6609. if (auto parsed_value = parse_comma_separated_value_list(tokens, [this](auto& tokens) { return parse_position_value(tokens, PositionParsingMode::BackgroundPosition); }))
  6610. return parsed_value.release_nonnull();
  6611. return ParseError::SyntaxError;
  6612. case PropertyID::BackgroundPositionX:
  6613. case PropertyID::BackgroundPositionY:
  6614. if (auto parsed_value = parse_comma_separated_value_list(tokens, [this, property_id](auto& tokens) { return parse_single_background_position_x_or_y_value(tokens, property_id); }))
  6615. return parsed_value.release_nonnull();
  6616. return ParseError::SyntaxError;
  6617. case PropertyID::BackgroundRepeat:
  6618. if (auto parsed_value = parse_comma_separated_value_list(tokens, [this](auto& tokens) { return parse_single_background_repeat_value(tokens); }))
  6619. return parsed_value.release_nonnull();
  6620. return ParseError::SyntaxError;
  6621. case PropertyID::BackgroundSize:
  6622. if (auto parsed_value = parse_comma_separated_value_list(tokens, [this](auto& tokens) { return parse_single_background_size_value(tokens); }))
  6623. return parsed_value.release_nonnull();
  6624. return ParseError::SyntaxError;
  6625. case PropertyID::Border:
  6626. case PropertyID::BorderBottom:
  6627. case PropertyID::BorderLeft:
  6628. case PropertyID::BorderRight:
  6629. case PropertyID::BorderTop:
  6630. if (auto parsed_value = parse_border_value(property_id, tokens); parsed_value && !tokens.has_next_token())
  6631. return parsed_value.release_nonnull();
  6632. return ParseError::SyntaxError;
  6633. case PropertyID::BorderTopLeftRadius:
  6634. case PropertyID::BorderTopRightRadius:
  6635. case PropertyID::BorderBottomRightRadius:
  6636. case PropertyID::BorderBottomLeftRadius:
  6637. if (auto parsed_value = parse_border_radius_value(tokens); parsed_value && !tokens.has_next_token())
  6638. return parsed_value.release_nonnull();
  6639. return ParseError::SyntaxError;
  6640. case PropertyID::BorderRadius:
  6641. if (auto parsed_value = parse_border_radius_shorthand_value(tokens); parsed_value && !tokens.has_next_token())
  6642. return parsed_value.release_nonnull();
  6643. return ParseError::SyntaxError;
  6644. case PropertyID::BoxShadow:
  6645. if (auto parsed_value = parse_shadow_value(tokens, AllowInsetKeyword::Yes); parsed_value && !tokens.has_next_token())
  6646. return parsed_value.release_nonnull();
  6647. return ParseError::SyntaxError;
  6648. case PropertyID::Columns:
  6649. if (auto parsed_value = parse_columns_value(tokens); parsed_value && !tokens.has_next_token())
  6650. return parsed_value.release_nonnull();
  6651. return ParseError::SyntaxError;
  6652. case PropertyID::Content:
  6653. if (auto parsed_value = parse_content_value(tokens); parsed_value && !tokens.has_next_token())
  6654. return parsed_value.release_nonnull();
  6655. return ParseError::SyntaxError;
  6656. case PropertyID::CounterIncrement:
  6657. if (auto parsed_value = parse_counter_increment_value(tokens); parsed_value && !tokens.has_next_token())
  6658. return parsed_value.release_nonnull();
  6659. return ParseError::SyntaxError;
  6660. case PropertyID::CounterReset:
  6661. if (auto parsed_value = parse_counter_reset_value(tokens); parsed_value && !tokens.has_next_token())
  6662. return parsed_value.release_nonnull();
  6663. return ParseError::SyntaxError;
  6664. case PropertyID::CounterSet:
  6665. if (auto parsed_value = parse_counter_set_value(tokens); parsed_value && !tokens.has_next_token())
  6666. return parsed_value.release_nonnull();
  6667. return ParseError::SyntaxError;
  6668. case PropertyID::Display:
  6669. if (auto parsed_value = parse_display_value(tokens); parsed_value && !tokens.has_next_token())
  6670. return parsed_value.release_nonnull();
  6671. return ParseError::SyntaxError;
  6672. case PropertyID::Flex:
  6673. if (auto parsed_value = parse_flex_shorthand_value(tokens); parsed_value && !tokens.has_next_token())
  6674. return parsed_value.release_nonnull();
  6675. return ParseError::SyntaxError;
  6676. case PropertyID::FlexFlow:
  6677. if (auto parsed_value = parse_flex_flow_value(tokens); parsed_value && !tokens.has_next_token())
  6678. return parsed_value.release_nonnull();
  6679. return ParseError::SyntaxError;
  6680. case PropertyID::Font:
  6681. if (auto parsed_value = parse_font_value(tokens); parsed_value && !tokens.has_next_token())
  6682. return parsed_value.release_nonnull();
  6683. return ParseError::SyntaxError;
  6684. case PropertyID::FontFamily:
  6685. if (auto parsed_value = parse_font_family_value(tokens); parsed_value && !tokens.has_next_token())
  6686. return parsed_value.release_nonnull();
  6687. return ParseError::SyntaxError;
  6688. case PropertyID::FontFeatureSettings:
  6689. if (auto parsed_value = parse_font_feature_settings_value(tokens); parsed_value && !tokens.has_next_token())
  6690. return parsed_value.release_nonnull();
  6691. return ParseError::SyntaxError;
  6692. case PropertyID::FontLanguageOverride:
  6693. if (auto parsed_value = parse_font_language_override_value(tokens); parsed_value && !tokens.has_next_token())
  6694. return parsed_value.release_nonnull();
  6695. return ParseError::SyntaxError;
  6696. case PropertyID::FontVariationSettings:
  6697. if (auto parsed_value = parse_font_variation_settings_value(tokens); parsed_value && !tokens.has_next_token())
  6698. return parsed_value.release_nonnull();
  6699. return ParseError::SyntaxError;
  6700. case PropertyID::GridArea:
  6701. if (auto parsed_value = parse_grid_area_shorthand_value(tokens); parsed_value && !tokens.has_next_token())
  6702. return parsed_value.release_nonnull();
  6703. return ParseError::SyntaxError;
  6704. case PropertyID::GridAutoFlow:
  6705. if (auto parsed_value = parse_grid_auto_flow_value(tokens); parsed_value && !tokens.has_next_token())
  6706. return parsed_value.release_nonnull();
  6707. return ParseError::SyntaxError;
  6708. case PropertyID::GridColumn:
  6709. if (auto parsed_value = parse_grid_track_placement_shorthand_value(property_id, tokens); parsed_value && !tokens.has_next_token())
  6710. return parsed_value.release_nonnull();
  6711. return ParseError::SyntaxError;
  6712. case PropertyID::GridColumnEnd:
  6713. if (auto parsed_value = parse_grid_track_placement(tokens); parsed_value && !tokens.has_next_token())
  6714. return parsed_value.release_nonnull();
  6715. return ParseError::SyntaxError;
  6716. case PropertyID::GridColumnStart:
  6717. if (auto parsed_value = parse_grid_track_placement(tokens); parsed_value && !tokens.has_next_token())
  6718. return parsed_value.release_nonnull();
  6719. return ParseError::SyntaxError;
  6720. case PropertyID::GridRow:
  6721. if (auto parsed_value = parse_grid_track_placement_shorthand_value(property_id, tokens); parsed_value && !tokens.has_next_token())
  6722. return parsed_value.release_nonnull();
  6723. return ParseError::SyntaxError;
  6724. case PropertyID::GridRowEnd:
  6725. if (auto parsed_value = parse_grid_track_placement(tokens); parsed_value && !tokens.has_next_token())
  6726. return parsed_value.release_nonnull();
  6727. return ParseError::SyntaxError;
  6728. case PropertyID::GridRowStart:
  6729. if (auto parsed_value = parse_grid_track_placement(tokens); parsed_value && !tokens.has_next_token())
  6730. return parsed_value.release_nonnull();
  6731. return ParseError::SyntaxError;
  6732. case PropertyID::Grid:
  6733. if (auto parsed_value = parse_grid_shorthand_value(tokens); parsed_value && !tokens.has_next_token())
  6734. return parsed_value.release_nonnull();
  6735. return ParseError::SyntaxError;
  6736. case PropertyID::GridTemplate:
  6737. if (auto parsed_value = parse_grid_track_size_list_shorthand_value(property_id, tokens); parsed_value && !tokens.has_next_token())
  6738. return parsed_value.release_nonnull();
  6739. return ParseError::SyntaxError;
  6740. case PropertyID::GridTemplateAreas:
  6741. if (auto parsed_value = parse_grid_template_areas_value(tokens); parsed_value && !tokens.has_next_token())
  6742. return parsed_value.release_nonnull();
  6743. return ParseError::SyntaxError;
  6744. case PropertyID::GridTemplateColumns:
  6745. if (auto parsed_value = parse_grid_track_size_list(tokens); parsed_value && !tokens.has_next_token())
  6746. return parsed_value.release_nonnull();
  6747. return ParseError::SyntaxError;
  6748. case PropertyID::GridTemplateRows:
  6749. if (auto parsed_value = parse_grid_track_size_list(tokens); parsed_value && !tokens.has_next_token())
  6750. return parsed_value.release_nonnull();
  6751. return ParseError::SyntaxError;
  6752. case PropertyID::GridAutoColumns:
  6753. if (auto parsed_value = parse_grid_auto_track_sizes(tokens); parsed_value && !tokens.has_next_token())
  6754. return parsed_value.release_nonnull();
  6755. return ParseError::SyntaxError;
  6756. case PropertyID::GridAutoRows:
  6757. if (auto parsed_value = parse_grid_auto_track_sizes(tokens); parsed_value && !tokens.has_next_token())
  6758. return parsed_value.release_nonnull();
  6759. return ParseError::SyntaxError;
  6760. case PropertyID::ListStyle:
  6761. if (auto parsed_value = parse_list_style_value(tokens); parsed_value && !tokens.has_next_token())
  6762. return parsed_value.release_nonnull();
  6763. return ParseError::SyntaxError;
  6764. case PropertyID::MathDepth:
  6765. if (auto parsed_value = parse_math_depth_value(tokens); parsed_value && !tokens.has_next_token())
  6766. return parsed_value.release_nonnull();
  6767. return ParseError::SyntaxError;
  6768. case PropertyID::Overflow:
  6769. if (auto parsed_value = parse_overflow_value(tokens); parsed_value && !tokens.has_next_token())
  6770. return parsed_value.release_nonnull();
  6771. return ParseError::SyntaxError;
  6772. case PropertyID::PlaceContent:
  6773. if (auto parsed_value = parse_place_content_value(tokens); parsed_value && !tokens.has_next_token())
  6774. return parsed_value.release_nonnull();
  6775. return ParseError::SyntaxError;
  6776. case PropertyID::PlaceItems:
  6777. if (auto parsed_value = parse_place_items_value(tokens); parsed_value && !tokens.has_next_token())
  6778. return parsed_value.release_nonnull();
  6779. return ParseError::SyntaxError;
  6780. case PropertyID::PlaceSelf:
  6781. if (auto parsed_value = parse_place_self_value(tokens); parsed_value && !tokens.has_next_token())
  6782. return parsed_value.release_nonnull();
  6783. return ParseError::SyntaxError;
  6784. case PropertyID::Quotes:
  6785. if (auto parsed_value = parse_quotes_value(tokens); parsed_value && !tokens.has_next_token())
  6786. return parsed_value.release_nonnull();
  6787. return ParseError::SyntaxError;
  6788. case PropertyID::Rotate:
  6789. if (auto parsed_value = parse_rotate_value(tokens); parsed_value && !tokens.has_next_token())
  6790. return parsed_value.release_nonnull();
  6791. return ParseError::SyntaxError;
  6792. case PropertyID::ScrollbarGutter:
  6793. if (auto parsed_value = parse_scrollbar_gutter_value(tokens); parsed_value && !tokens.has_next_token())
  6794. return parsed_value.release_nonnull();
  6795. return ParseError::SyntaxError;
  6796. case PropertyID::StrokeDasharray:
  6797. if (auto parsed_value = parse_stroke_dasharray_value(tokens); parsed_value && !tokens.has_next_token())
  6798. return parsed_value.release_nonnull();
  6799. return ParseError::SyntaxError;
  6800. case PropertyID::TextDecoration:
  6801. if (auto parsed_value = parse_text_decoration_value(tokens); parsed_value && !tokens.has_next_token())
  6802. return parsed_value.release_nonnull();
  6803. return ParseError::SyntaxError;
  6804. case PropertyID::TextDecorationLine:
  6805. if (auto parsed_value = parse_text_decoration_line_value(tokens); parsed_value && !tokens.has_next_token())
  6806. return parsed_value.release_nonnull();
  6807. return ParseError::SyntaxError;
  6808. case PropertyID::TextShadow:
  6809. if (auto parsed_value = parse_shadow_value(tokens, AllowInsetKeyword::No); parsed_value && !tokens.has_next_token())
  6810. return parsed_value.release_nonnull();
  6811. return ParseError::SyntaxError;
  6812. case PropertyID::Transform:
  6813. if (auto parsed_value = parse_transform_value(tokens); parsed_value && !tokens.has_next_token())
  6814. return parsed_value.release_nonnull();
  6815. return ParseError::SyntaxError;
  6816. case PropertyID::TransformOrigin:
  6817. if (auto parsed_value = parse_transform_origin_value(tokens); parsed_value && !tokens.has_next_token())
  6818. return parsed_value.release_nonnull();
  6819. return ParseError::SyntaxError;
  6820. case PropertyID::Transition:
  6821. if (auto parsed_value = parse_transition_value(tokens); parsed_value && !tokens.has_next_token())
  6822. return parsed_value.release_nonnull();
  6823. return ParseError::SyntaxError;
  6824. case PropertyID::Translate:
  6825. if (auto parsed_value = parse_translate_value(tokens); parsed_value && !tokens.has_next_token())
  6826. return parsed_value.release_nonnull();
  6827. return ParseError::SyntaxError;
  6828. default:
  6829. break;
  6830. }
  6831. // If there's only 1 ComponentValue, we can only produce a single CSSStyleValue.
  6832. if (component_values.size() == 1) {
  6833. auto stream = TokenStream { component_values };
  6834. if (auto parsed_value = parse_css_value_for_property(property_id, stream))
  6835. return parsed_value.release_nonnull();
  6836. } else {
  6837. StyleValueVector parsed_values;
  6838. auto stream = TokenStream { component_values };
  6839. while (auto parsed_value = parse_css_value_for_property(property_id, stream)) {
  6840. parsed_values.append(parsed_value.release_nonnull());
  6841. if (!stream.has_next_token())
  6842. break;
  6843. }
  6844. // Some types (such as <ratio>) can be made from multiple ComponentValues, so if we only made 1 CSSStyleValue, return it directly.
  6845. if (parsed_values.size() == 1)
  6846. return *parsed_values.take_first();
  6847. if (!parsed_values.is_empty() && parsed_values.size() <= property_maximum_value_count(property_id))
  6848. return StyleValueList::create(move(parsed_values), StyleValueList::Separator::Space);
  6849. }
  6850. // We have multiple values, but the property claims to accept only a single one, check if it's a shorthand property.
  6851. auto unassigned_properties = longhands_for_shorthand(property_id);
  6852. if (unassigned_properties.is_empty())
  6853. return ParseError::SyntaxError;
  6854. auto stream = TokenStream { component_values };
  6855. HashMap<UnderlyingType<PropertyID>, Vector<ValueComparingNonnullRefPtr<CSSStyleValue const>>> assigned_values;
  6856. while (stream.has_next_token() && !unassigned_properties.is_empty()) {
  6857. auto property_and_value = parse_css_value_for_properties(unassigned_properties, stream);
  6858. if (property_and_value.has_value()) {
  6859. auto property = property_and_value->property;
  6860. auto value = property_and_value->style_value;
  6861. auto& values = assigned_values.ensure(to_underlying(property));
  6862. if (values.size() + 1 == property_maximum_value_count(property)) {
  6863. // We're done with this property, move on to the next one.
  6864. unassigned_properties.remove_first_matching([&](auto& unassigned_property) { return unassigned_property == property; });
  6865. }
  6866. values.append(value.release_nonnull());
  6867. continue;
  6868. }
  6869. // No property matched, so we're done.
  6870. dbgln("No property (from {} properties) matched {}", unassigned_properties.size(), stream.next_token().to_debug_string());
  6871. for (auto id : unassigned_properties)
  6872. dbgln(" {}", string_from_property_id(id));
  6873. break;
  6874. }
  6875. for (auto& property : unassigned_properties)
  6876. assigned_values.ensure(to_underlying(property)).append(property_initial_value(m_context.realm(), property));
  6877. stream.discard_whitespace();
  6878. if (stream.has_next_token())
  6879. return ParseError::SyntaxError;
  6880. Vector<PropertyID> longhand_properties;
  6881. longhand_properties.ensure_capacity(assigned_values.size());
  6882. for (auto& it : assigned_values)
  6883. longhand_properties.unchecked_append(static_cast<PropertyID>(it.key));
  6884. StyleValueVector longhand_values;
  6885. longhand_values.ensure_capacity(assigned_values.size());
  6886. for (auto& it : assigned_values) {
  6887. if (it.value.size() == 1)
  6888. longhand_values.unchecked_append(it.value.take_first());
  6889. else
  6890. longhand_values.unchecked_append(StyleValueList::create(move(it.value), StyleValueList::Separator::Space));
  6891. }
  6892. return { ShorthandStyleValue::create(property_id, move(longhand_properties), move(longhand_values)) };
  6893. }
  6894. RefPtr<CSSStyleValue> Parser::parse_css_value_for_property(PropertyID property_id, TokenStream<ComponentValue>& tokens)
  6895. {
  6896. return parse_css_value_for_properties({ &property_id, 1 }, tokens)
  6897. .map([](auto& it) { return it.style_value; })
  6898. .value_or(nullptr);
  6899. }
  6900. Optional<Parser::PropertyAndValue> Parser::parse_css_value_for_properties(ReadonlySpan<PropertyID> property_ids, TokenStream<ComponentValue>& tokens)
  6901. {
  6902. auto any_property_accepts_type = [](ReadonlySpan<PropertyID> property_ids, ValueType value_type) -> Optional<PropertyID> {
  6903. for (auto const& property : property_ids) {
  6904. if (property_accepts_type(property, value_type))
  6905. return property;
  6906. }
  6907. return {};
  6908. };
  6909. auto any_property_accepts_type_percentage = [](ReadonlySpan<PropertyID> property_ids, ValueType value_type) -> Optional<PropertyID> {
  6910. for (auto const& property : property_ids) {
  6911. if (property_accepts_type(property, value_type) && property_accepts_type(property, ValueType::Percentage))
  6912. return property;
  6913. }
  6914. return {};
  6915. };
  6916. auto any_property_accepts_keyword = [](ReadonlySpan<PropertyID> property_ids, Keyword keyword) -> Optional<PropertyID> {
  6917. for (auto const& property : property_ids) {
  6918. if (property_accepts_keyword(property, keyword))
  6919. return property;
  6920. }
  6921. return {};
  6922. };
  6923. auto& peek_token = tokens.next_token();
  6924. if (auto property = any_property_accepts_type(property_ids, ValueType::EasingFunction); property.has_value()) {
  6925. if (auto maybe_easing_function = parse_easing_value(tokens))
  6926. return PropertyAndValue { *property, maybe_easing_function };
  6927. }
  6928. if (peek_token.is(Token::Type::Ident)) {
  6929. // NOTE: We do not try to parse "CSS-wide keywords" here. https://www.w3.org/TR/css-values-4/#common-keywords
  6930. // These are only valid on their own, and so should be parsed directly in `parse_css_value()`.
  6931. auto keyword = keyword_from_string(peek_token.token().ident());
  6932. if (keyword.has_value()) {
  6933. if (auto property = any_property_accepts_keyword(property_ids, keyword.value()); property.has_value()) {
  6934. tokens.discard_a_token();
  6935. return PropertyAndValue { *property, CSSKeywordValue::create(keyword.value()) };
  6936. }
  6937. }
  6938. // Custom idents
  6939. if (auto property = any_property_accepts_type(property_ids, ValueType::CustomIdent); property.has_value()) {
  6940. if (auto custom_ident = parse_custom_ident_value(tokens, {}))
  6941. return PropertyAndValue { *property, custom_ident };
  6942. }
  6943. }
  6944. if (auto property = any_property_accepts_type(property_ids, ValueType::Color); property.has_value()) {
  6945. if (auto maybe_color = parse_color_value(tokens))
  6946. return PropertyAndValue { *property, maybe_color };
  6947. }
  6948. if (auto property = any_property_accepts_type(property_ids, ValueType::Counter); property.has_value()) {
  6949. if (auto maybe_counter = parse_counter_value(tokens))
  6950. return PropertyAndValue { *property, maybe_counter };
  6951. }
  6952. if (auto property = any_property_accepts_type(property_ids, ValueType::Image); property.has_value()) {
  6953. if (auto maybe_image = parse_image_value(tokens))
  6954. return PropertyAndValue { *property, maybe_image };
  6955. }
  6956. if (auto property = any_property_accepts_type(property_ids, ValueType::Position); property.has_value()) {
  6957. if (auto maybe_position = parse_position_value(tokens))
  6958. return PropertyAndValue { *property, maybe_position };
  6959. }
  6960. if (auto property = any_property_accepts_type(property_ids, ValueType::BackgroundPosition); property.has_value()) {
  6961. if (auto maybe_position = parse_position_value(tokens, PositionParsingMode::BackgroundPosition))
  6962. return PropertyAndValue { *property, maybe_position };
  6963. }
  6964. if (auto property = any_property_accepts_type(property_ids, ValueType::BasicShape); property.has_value()) {
  6965. if (auto maybe_basic_shape = parse_basic_shape_value(tokens))
  6966. return PropertyAndValue { *property, maybe_basic_shape };
  6967. }
  6968. if (auto property = any_property_accepts_type(property_ids, ValueType::Ratio); property.has_value()) {
  6969. if (auto maybe_ratio = parse_ratio_value(tokens))
  6970. return PropertyAndValue { *property, maybe_ratio };
  6971. }
  6972. auto property_accepting_integer = any_property_accepts_type(property_ids, ValueType::Integer);
  6973. auto property_accepting_number = any_property_accepts_type(property_ids, ValueType::Number);
  6974. bool property_accepts_numeric = property_accepting_integer.has_value() || property_accepting_number.has_value();
  6975. if (peek_token.is(Token::Type::Number) && property_accepts_numeric) {
  6976. if (peek_token.token().number().is_integer() && property_accepting_integer.has_value()) {
  6977. auto integer = IntegerStyleValue::create(peek_token.token().number().integer_value());
  6978. if (property_accepts_integer(*property_accepting_integer, integer->as_integer().integer())) {
  6979. tokens.discard_a_token(); // integer
  6980. return PropertyAndValue { *property_accepting_integer, integer };
  6981. }
  6982. }
  6983. if (property_accepting_number.has_value()) {
  6984. auto number = NumberStyleValue::create(peek_token.token().number().value());
  6985. if (property_accepts_number(*property_accepting_number, number->as_number().number())) {
  6986. tokens.discard_a_token(); // number
  6987. return PropertyAndValue { *property_accepting_number, number };
  6988. }
  6989. }
  6990. }
  6991. if (auto property = any_property_accepts_type(property_ids, ValueType::OpenTypeTag); property.has_value()) {
  6992. if (auto maybe_rect = parse_opentype_tag_value(tokens))
  6993. return PropertyAndValue { *property, maybe_rect };
  6994. }
  6995. if (peek_token.is(Token::Type::Percentage)) {
  6996. auto percentage = Percentage(peek_token.token().percentage());
  6997. if (auto property = any_property_accepts_type(property_ids, ValueType::Percentage); property.has_value() && property_accepts_percentage(*property, percentage)) {
  6998. tokens.discard_a_token();
  6999. return PropertyAndValue { *property, PercentageStyleValue::create(percentage) };
  7000. }
  7001. }
  7002. if (auto property = any_property_accepts_type(property_ids, ValueType::Rect); property.has_value()) {
  7003. if (auto maybe_rect = parse_rect_value(tokens))
  7004. return PropertyAndValue { *property, maybe_rect };
  7005. }
  7006. if (peek_token.is(Token::Type::String)) {
  7007. if (auto property = any_property_accepts_type(property_ids, ValueType::String); property.has_value())
  7008. return PropertyAndValue { *property, StringStyleValue::create(tokens.consume_a_token().token().string()) };
  7009. }
  7010. if (auto property = any_property_accepts_type(property_ids, ValueType::Url); property.has_value()) {
  7011. if (auto url = parse_url_value(tokens))
  7012. return PropertyAndValue { *property, url };
  7013. }
  7014. bool property_accepts_dimension = any_property_accepts_type(property_ids, ValueType::Angle).has_value()
  7015. || any_property_accepts_type(property_ids, ValueType::Flex).has_value()
  7016. || any_property_accepts_type(property_ids, ValueType::Frequency).has_value()
  7017. || any_property_accepts_type(property_ids, ValueType::Length).has_value()
  7018. || any_property_accepts_type(property_ids, ValueType::Percentage).has_value()
  7019. || any_property_accepts_type(property_ids, ValueType::Resolution).has_value()
  7020. || any_property_accepts_type(property_ids, ValueType::Time).has_value();
  7021. if (property_accepts_dimension) {
  7022. if (peek_token.is(Token::Type::Number) && m_context.is_parsing_svg_presentation_attribute()) {
  7023. auto transaction = tokens.begin_transaction();
  7024. auto const& token = tokens.consume_a_token();
  7025. // https://svgwg.org/svg2-draft/types.html#presentation-attribute-css-value
  7026. // We need to allow <number> in any place that expects a <length> or <angle>.
  7027. // FIXME: How should these numbers be interpreted? https://github.com/w3c/svgwg/issues/792
  7028. // For now: Convert them to px lengths, or deg angles.
  7029. auto angle = Angle::make_degrees(token.token().number_value());
  7030. if (auto property = any_property_accepts_type(property_ids, ValueType::Angle); property.has_value() && property_accepts_angle(*property, angle)) {
  7031. transaction.commit();
  7032. return PropertyAndValue { *property, AngleStyleValue::create(angle) };
  7033. }
  7034. auto length = Length::make_px(CSSPixels::nearest_value_for(token.token().number_value()));
  7035. if (auto property = any_property_accepts_type(property_ids, ValueType::Length); property.has_value() && property_accepts_length(*property, length)) {
  7036. transaction.commit();
  7037. return PropertyAndValue { *property, LengthStyleValue::create(length) };
  7038. }
  7039. }
  7040. auto transaction = tokens.begin_transaction();
  7041. if (auto maybe_dimension = parse_dimension(peek_token); maybe_dimension.has_value()) {
  7042. tokens.discard_a_token();
  7043. auto dimension = maybe_dimension.release_value();
  7044. if (dimension.is_angle()) {
  7045. auto angle = dimension.angle();
  7046. if (auto property = any_property_accepts_type(property_ids, ValueType::Angle); property.has_value() && property_accepts_angle(*property, angle)) {
  7047. transaction.commit();
  7048. return PropertyAndValue { *property, AngleStyleValue::create(angle) };
  7049. }
  7050. }
  7051. if (dimension.is_flex()) {
  7052. auto flex = dimension.flex();
  7053. if (auto property = any_property_accepts_type(property_ids, ValueType::Flex); property.has_value() && property_accepts_flex(*property, flex)) {
  7054. transaction.commit();
  7055. return PropertyAndValue { *property, FlexStyleValue::create(flex) };
  7056. }
  7057. }
  7058. if (dimension.is_frequency()) {
  7059. auto frequency = dimension.frequency();
  7060. if (auto property = any_property_accepts_type(property_ids, ValueType::Frequency); property.has_value() && property_accepts_frequency(*property, frequency)) {
  7061. transaction.commit();
  7062. return PropertyAndValue { *property, FrequencyStyleValue::create(frequency) };
  7063. }
  7064. }
  7065. if (dimension.is_length()) {
  7066. auto length = dimension.length();
  7067. if (auto property = any_property_accepts_type(property_ids, ValueType::Length); property.has_value() && property_accepts_length(*property, length)) {
  7068. transaction.commit();
  7069. return PropertyAndValue { *property, LengthStyleValue::create(length) };
  7070. }
  7071. }
  7072. if (dimension.is_resolution()) {
  7073. auto resolution = dimension.resolution();
  7074. if (auto property = any_property_accepts_type(property_ids, ValueType::Resolution); property.has_value() && property_accepts_resolution(*property, resolution)) {
  7075. transaction.commit();
  7076. return PropertyAndValue { *property, ResolutionStyleValue::create(resolution) };
  7077. }
  7078. }
  7079. if (dimension.is_time()) {
  7080. auto time = dimension.time();
  7081. if (auto property = any_property_accepts_type(property_ids, ValueType::Time); property.has_value() && property_accepts_time(*property, time)) {
  7082. transaction.commit();
  7083. return PropertyAndValue { *property, TimeStyleValue::create(time) };
  7084. }
  7085. }
  7086. }
  7087. }
  7088. // In order to not end up parsing `calc()` and other math expressions multiple times,
  7089. // we parse it once, and then see if its resolved type matches what the property accepts.
  7090. if (peek_token.is_function() && (property_accepts_dimension || property_accepts_numeric)) {
  7091. if (auto maybe_calculated = parse_calculated_value(peek_token); maybe_calculated) {
  7092. tokens.discard_a_token();
  7093. auto& calculated = *maybe_calculated;
  7094. // This is a bit sensitive to ordering: `<foo>` and `<percentage>` have to be checked before `<foo-percentage>`.
  7095. // FIXME: When parsing SVG presentation attributes, <number> is permitted wherever <length>, <length-percentage>, or <angle> are.
  7096. // The specifics are unclear, so I'm ignoring this for calculated values for now.
  7097. // See https://github.com/w3c/svgwg/issues/792
  7098. if (calculated.resolves_to_percentage()) {
  7099. if (auto property = any_property_accepts_type(property_ids, ValueType::Percentage); property.has_value())
  7100. return PropertyAndValue { *property, calculated };
  7101. } else if (calculated.resolves_to_angle()) {
  7102. if (auto property = any_property_accepts_type(property_ids, ValueType::Angle); property.has_value())
  7103. return PropertyAndValue { *property, calculated };
  7104. } else if (calculated.resolves_to_angle_percentage()) {
  7105. if (auto property = any_property_accepts_type_percentage(property_ids, ValueType::Angle); property.has_value())
  7106. return PropertyAndValue { *property, calculated };
  7107. } else if (calculated.resolves_to_flex()) {
  7108. if (auto property = any_property_accepts_type(property_ids, ValueType::Flex); property.has_value())
  7109. return PropertyAndValue { *property, calculated };
  7110. } else if (calculated.resolves_to_frequency()) {
  7111. if (auto property = any_property_accepts_type(property_ids, ValueType::Frequency); property.has_value())
  7112. return PropertyAndValue { *property, calculated };
  7113. } else if (calculated.resolves_to_frequency_percentage()) {
  7114. if (auto property = any_property_accepts_type_percentage(property_ids, ValueType::Frequency); property.has_value())
  7115. return PropertyAndValue { *property, calculated };
  7116. } else if (calculated.resolves_to_number()) {
  7117. if (property_accepts_numeric) {
  7118. auto property_or_resolved = property_accepting_integer.value_or_lazy_evaluated([property_accepting_number]() { return property_accepting_number.value(); });
  7119. return PropertyAndValue { property_or_resolved, calculated };
  7120. }
  7121. } else if (calculated.resolves_to_number_percentage()) {
  7122. if (auto property = any_property_accepts_type_percentage(property_ids, ValueType::Number); property.has_value())
  7123. return PropertyAndValue { *property, calculated };
  7124. } else if (calculated.resolves_to_length()) {
  7125. if (auto property = any_property_accepts_type(property_ids, ValueType::Length); property.has_value())
  7126. return PropertyAndValue { *property, calculated };
  7127. } else if (calculated.resolves_to_length_percentage()) {
  7128. if (auto property = any_property_accepts_type_percentage(property_ids, ValueType::Length); property.has_value())
  7129. return PropertyAndValue { *property, calculated };
  7130. } else if (calculated.resolves_to_time()) {
  7131. if (auto property = any_property_accepts_type(property_ids, ValueType::Time); property.has_value())
  7132. return PropertyAndValue { *property, calculated };
  7133. } else if (calculated.resolves_to_time_percentage()) {
  7134. if (auto property = any_property_accepts_type_percentage(property_ids, ValueType::Time); property.has_value())
  7135. return PropertyAndValue { *property, calculated };
  7136. }
  7137. }
  7138. }
  7139. if (auto property = any_property_accepts_type(property_ids, ValueType::Paint); property.has_value()) {
  7140. if (auto value = parse_paint_value(tokens))
  7141. return PropertyAndValue { *property, value.release_nonnull() };
  7142. }
  7143. return OptionalNone {};
  7144. }
  7145. class UnparsedCalculationNode final : public CalculationNode {
  7146. public:
  7147. static NonnullOwnPtr<UnparsedCalculationNode> create(ComponentValue component_value)
  7148. {
  7149. return adopt_own(*new (nothrow) UnparsedCalculationNode(move(component_value)));
  7150. }
  7151. virtual ~UnparsedCalculationNode() = default;
  7152. ComponentValue& component_value() { return m_component_value; }
  7153. virtual String to_string() const override { VERIFY_NOT_REACHED(); }
  7154. virtual Optional<CSSMathValue::ResolvedType> resolved_type() const override { VERIFY_NOT_REACHED(); }
  7155. virtual Optional<CSSNumericType> determine_type(Web::CSS::PropertyID) const override { VERIFY_NOT_REACHED(); }
  7156. virtual bool contains_percentage() const override { VERIFY_NOT_REACHED(); }
  7157. virtual CSSMathValue::CalculationResult resolve(Optional<Length::ResolutionContext const&>, CSSMathValue::PercentageBasis const&) const override { VERIFY_NOT_REACHED(); }
  7158. virtual void for_each_child_node(AK::Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override { }
  7159. virtual void dump(StringBuilder& builder, int indent) const override
  7160. {
  7161. builder.appendff("{: >{}}UNPARSED({})\n", "", indent, m_component_value.to_debug_string());
  7162. }
  7163. virtual bool equals(CalculationNode const&) const override { return false; }
  7164. private:
  7165. UnparsedCalculationNode(ComponentValue component_value)
  7166. : CalculationNode(Type::Unparsed)
  7167. , m_component_value(move(component_value))
  7168. {
  7169. }
  7170. ComponentValue m_component_value;
  7171. };
  7172. // https://html.spec.whatwg.org/multipage/images.html#parsing-a-sizes-attribute
  7173. LengthOrCalculated Parser::Parser::parse_as_sizes_attribute(DOM::Element const& element, HTML::HTMLImageElement const* img)
  7174. {
  7175. // When asked to parse a sizes attribute from an element element, with an img element or null img:
  7176. // AD-HOC: If element has no sizes attribute, this algorithm always logs a parse error and then returns 100vw.
  7177. // The attribute is optional, so avoid spamming the debug log with false positives by just returning early.
  7178. if (!element.has_attribute(HTML::AttributeNames::sizes))
  7179. return Length(100, Length::Type::Vw);
  7180. // 1. Let unparsed sizes list be the result of parsing a comma-separated list of component values
  7181. // from the value of element's sizes attribute (or the empty string, if the attribute is absent).
  7182. // NOTE: The sizes attribute has already been tokenized into m_token_stream by this point.
  7183. auto unparsed_sizes_list = parse_a_comma_separated_list_of_component_values(m_token_stream);
  7184. // 2. Let size be null.
  7185. Optional<LengthOrCalculated> size;
  7186. auto size_is_auto = [&size]() {
  7187. return !size->is_calculated() && size->value().is_auto();
  7188. };
  7189. auto remove_all_consecutive_whitespace_tokens_from_the_end_of = [](auto& tokens) {
  7190. while (!tokens.is_empty() && tokens.last().is_token() && tokens.last().token().is(Token::Type::Whitespace))
  7191. tokens.take_last();
  7192. };
  7193. // 3. For each unparsed size in unparsed sizes list:
  7194. for (auto i = 0u; i < unparsed_sizes_list.size(); i++) {
  7195. auto& unparsed_size = unparsed_sizes_list[i];
  7196. // 1. Remove all consecutive <whitespace-token>s from the end of unparsed size.
  7197. // If unparsed size is now empty, that is a parse error; continue.
  7198. remove_all_consecutive_whitespace_tokens_from_the_end_of(unparsed_size);
  7199. if (unparsed_size.is_empty()) {
  7200. log_parse_error();
  7201. dbgln_if(CSS_PARSER_DEBUG, "-> Failed in step 3.1; all whitespace");
  7202. continue;
  7203. }
  7204. // 2. If the last component value in unparsed size is a valid non-negative <source-size-value>,
  7205. // then set size to its value and remove the component value from unparsed size.
  7206. // Any CSS function other than the math functions is invalid.
  7207. // Otherwise, there is a parse error; continue.
  7208. auto last_value_stream = TokenStream<ComponentValue>::of_single_token(unparsed_size.last());
  7209. if (auto source_size_value = parse_source_size_value(last_value_stream); source_size_value.has_value()) {
  7210. size = source_size_value.value();
  7211. unparsed_size.take_last();
  7212. } else {
  7213. log_parse_error();
  7214. dbgln_if(CSS_PARSER_DEBUG, "-> Failed in step 3.2; couldn't parse {} as a <source-size-value>", unparsed_size.last().to_debug_string());
  7215. continue;
  7216. }
  7217. // 3. If size is auto, and img is not null, and img is being rendered, and img allows auto-sizes,
  7218. // then set size to the concrete object size width of img, in CSS pixels.
  7219. // FIXME: "img is being rendered" - we just see if it has a bitmap for now
  7220. if (size_is_auto() && img && img->immutable_bitmap() && img->allows_auto_sizes()) {
  7221. // FIXME: The spec doesn't seem to tell us how to determine the concrete size of an <img>, so use the default sizing algorithm.
  7222. // Should this use some of the methods from FormattingContext?
  7223. auto concrete_size = run_default_sizing_algorithm(
  7224. img->width(), img->height(),
  7225. img->natural_width(), img->natural_height(), img->intrinsic_aspect_ratio(),
  7226. // NOTE: https://html.spec.whatwg.org/multipage/rendering.html#img-contain-size
  7227. CSSPixelSize { 300, 150 });
  7228. size = Length::make_px(concrete_size.width());
  7229. }
  7230. // 4. Remove all consecutive <whitespace-token>s from the end of unparsed size.
  7231. // If unparsed size is now empty:
  7232. remove_all_consecutive_whitespace_tokens_from_the_end_of(unparsed_size);
  7233. if (unparsed_size.is_empty()) {
  7234. // 1. If this was not the last item in unparsed sizes list, that is a parse error.
  7235. if (i != unparsed_sizes_list.size() - 1) {
  7236. log_parse_error();
  7237. dbgln_if(CSS_PARSER_DEBUG, "-> Failed in step 3.4.1; is unparsed size #{}, count {}", i, unparsed_sizes_list.size());
  7238. }
  7239. // 2. If size is not auto, then return size. Otherwise, continue.
  7240. if (!size_is_auto())
  7241. return size.release_value();
  7242. continue;
  7243. }
  7244. // 5. Parse the remaining component values in unparsed size as a <media-condition>.
  7245. // If it does not parse correctly, or it does parse correctly but the <media-condition> evaluates to false, continue.
  7246. TokenStream<ComponentValue> token_stream { unparsed_size };
  7247. auto media_condition = parse_media_condition(token_stream, MediaCondition::AllowOr::Yes);
  7248. auto const* context_window = m_context.window();
  7249. if (!media_condition || (context_window && media_condition->evaluate(*context_window) == MatchResult::False)) {
  7250. continue;
  7251. }
  7252. // 5. If size is not auto, then return size. Otherwise, continue.
  7253. if (!size_is_auto())
  7254. return size.value();
  7255. }
  7256. // 4. Return 100vw.
  7257. return Length(100, Length::Type::Vw);
  7258. }
  7259. // https://www.w3.org/TR/css-values-4/#parse-a-calculation
  7260. OwnPtr<CalculationNode> Parser::parse_a_calculation(Vector<ComponentValue> const& original_values)
  7261. {
  7262. // 1. Discard any <whitespace-token>s from values.
  7263. // 2. An item in values is an “operator” if it’s a <delim-token> with the value "+", "-", "*", or "/". Otherwise, it’s a “value”.
  7264. struct Operator {
  7265. char delim;
  7266. };
  7267. using Value = Variant<NonnullOwnPtr<CalculationNode>, Operator>;
  7268. Vector<Value> values;
  7269. for (auto const& value : original_values) {
  7270. if (value.is(Token::Type::Whitespace))
  7271. continue;
  7272. if (value.is(Token::Type::Delim)) {
  7273. if (first_is_one_of(value.token().delim(), static_cast<u32>('+'), static_cast<u32>('-'), static_cast<u32>('*'), static_cast<u32>('/'))) {
  7274. // NOTE: Sequential operators are invalid syntax.
  7275. if (!values.is_empty() && values.last().has<Operator>())
  7276. return nullptr;
  7277. values.append(Operator { static_cast<char>(value.token().delim()) });
  7278. continue;
  7279. }
  7280. }
  7281. if (value.is(Token::Type::Ident)) {
  7282. auto maybe_constant = CalculationNode::constant_type_from_string(value.token().ident());
  7283. if (maybe_constant.has_value()) {
  7284. values.append({ ConstantCalculationNode::create(maybe_constant.value()) });
  7285. continue;
  7286. }
  7287. }
  7288. if (value.is(Token::Type::Number)) {
  7289. values.append({ NumericCalculationNode::create(value.token().number()) });
  7290. continue;
  7291. }
  7292. if (auto dimension = parse_dimension(value); dimension.has_value()) {
  7293. if (dimension->is_angle())
  7294. values.append({ NumericCalculationNode::create(dimension->angle()) });
  7295. else if (dimension->is_frequency())
  7296. values.append({ NumericCalculationNode::create(dimension->frequency()) });
  7297. else if (dimension->is_length())
  7298. values.append({ NumericCalculationNode::create(dimension->length()) });
  7299. else if (dimension->is_percentage())
  7300. values.append({ NumericCalculationNode::create(dimension->percentage()) });
  7301. else if (dimension->is_resolution())
  7302. values.append({ NumericCalculationNode::create(dimension->resolution()) });
  7303. else if (dimension->is_time())
  7304. values.append({ NumericCalculationNode::create(dimension->time()) });
  7305. else if (dimension->is_flex()) {
  7306. // https://www.w3.org/TR/css3-grid-layout/#fr-unit
  7307. // NOTE: <flex> values are not <length>s (nor are they compatible with <length>s, like some <percentage> values),
  7308. // so they cannot be represented in or combined with other unit types in calc() expressions.
  7309. return nullptr;
  7310. } else {
  7311. VERIFY_NOT_REACHED();
  7312. }
  7313. continue;
  7314. }
  7315. values.append({ UnparsedCalculationNode::create(value) });
  7316. }
  7317. // If we have no values, the syntax is invalid.
  7318. if (values.is_empty())
  7319. return nullptr;
  7320. // NOTE: If the first or last value is an operator, the syntax is invalid.
  7321. if (values.first().has<Operator>() || values.last().has<Operator>())
  7322. return nullptr;
  7323. // 3. Collect children into Product and Invert nodes.
  7324. // For every consecutive run of value items in values separated by "*" or "/" operators:
  7325. while (true) {
  7326. Optional<size_t> first_product_operator = values.find_first_index_if([](auto const& item) {
  7327. return item.template has<Operator>()
  7328. && first_is_one_of(item.template get<Operator>().delim, '*', '/');
  7329. });
  7330. if (!first_product_operator.has_value())
  7331. break;
  7332. auto start_of_run = first_product_operator.value() - 1;
  7333. auto end_of_run = first_product_operator.value() + 1;
  7334. for (auto i = start_of_run + 1; i < values.size(); i += 2) {
  7335. auto& item = values[i];
  7336. if (!item.has<Operator>()) {
  7337. end_of_run = i - 1;
  7338. break;
  7339. }
  7340. auto delim = item.get<Operator>().delim;
  7341. if (!first_is_one_of(delim, '*', '/')) {
  7342. end_of_run = i - 1;
  7343. break;
  7344. }
  7345. }
  7346. // 1. For each "/" operator in the run, replace its right-hand value item rhs with an Invert node containing rhs as its child.
  7347. Vector<NonnullOwnPtr<CalculationNode>> run_values;
  7348. run_values.append(move(values[start_of_run].get<NonnullOwnPtr<CalculationNode>>()));
  7349. for (auto i = start_of_run + 1; i <= end_of_run; i += 2) {
  7350. auto& operator_ = values[i].get<Operator>().delim;
  7351. auto& rhs = values[i + 1];
  7352. if (operator_ == '/') {
  7353. run_values.append(InvertCalculationNode::create(move(rhs.get<NonnullOwnPtr<CalculationNode>>())));
  7354. continue;
  7355. }
  7356. VERIFY(operator_ == '*');
  7357. run_values.append(move(rhs.get<NonnullOwnPtr<CalculationNode>>()));
  7358. }
  7359. // 2. Replace the entire run with a Product node containing the value items of the run as its children.
  7360. auto product_node = ProductCalculationNode::create(move(run_values));
  7361. values.remove(start_of_run, end_of_run - start_of_run + 1);
  7362. values.insert(start_of_run, { move(product_node) });
  7363. }
  7364. // 4. Collect children into Sum and Negate nodes.
  7365. Optional<NonnullOwnPtr<CalculationNode>> single_value;
  7366. {
  7367. // 1. For each "-" operator item in values, replace its right-hand value item rhs with a Negate node containing rhs as its child.
  7368. for (auto i = 0u; i < values.size(); ++i) {
  7369. auto& maybe_minus_operator = values[i];
  7370. if (!maybe_minus_operator.has<Operator>() || maybe_minus_operator.get<Operator>().delim != '-')
  7371. continue;
  7372. auto rhs_index = ++i;
  7373. auto& rhs = values[rhs_index];
  7374. NonnullOwnPtr<CalculationNode> negate_node = NegateCalculationNode::create(move(rhs.get<NonnullOwnPtr<CalculationNode>>()));
  7375. values.remove(rhs_index);
  7376. values.insert(rhs_index, move(negate_node));
  7377. }
  7378. // 2. If values has only one item, and it is a Product node or a parenthesized simple block, replace values with that item.
  7379. if (values.size() == 1) {
  7380. values.first().visit(
  7381. [&](ComponentValue& component_value) {
  7382. if (component_value.is_block() && component_value.block().is_paren())
  7383. single_value = UnparsedCalculationNode::create(move(component_value));
  7384. },
  7385. [&](NonnullOwnPtr<CalculationNode>& node) {
  7386. if (node->type() == CalculationNode::Type::Product)
  7387. single_value = move(node);
  7388. },
  7389. [](auto&) {});
  7390. }
  7391. // Otherwise, replace values with a Sum node containing the value items of values as its children.
  7392. if (!single_value.has_value()) {
  7393. values.remove_all_matching([](Value& value) { return value.has<Operator>(); });
  7394. Vector<NonnullOwnPtr<CalculationNode>> value_items;
  7395. value_items.ensure_capacity(values.size());
  7396. for (auto& value : values) {
  7397. if (value.has<Operator>())
  7398. continue;
  7399. value_items.unchecked_append(move(value.get<NonnullOwnPtr<CalculationNode>>()));
  7400. }
  7401. single_value = SumCalculationNode::create(move(value_items));
  7402. }
  7403. }
  7404. // 5. At this point values is a tree of Sum, Product, Negate, and Invert nodes, with other types of values at the leaf nodes. Process the leaf nodes.
  7405. // For every leaf node leaf in values:
  7406. bool parsing_failed_for_child_node = false;
  7407. single_value.value()->for_each_child_node([&](NonnullOwnPtr<CalculationNode>& node) {
  7408. if (node->type() != CalculationNode::Type::Unparsed)
  7409. return;
  7410. auto& unparsed_node = static_cast<UnparsedCalculationNode&>(*node);
  7411. auto& component_value = unparsed_node.component_value();
  7412. // 1. If leaf is a parenthesized simple block, replace leaf with the result of parsing a calculation from leaf’s contents.
  7413. if (component_value.is_block() && component_value.block().is_paren()) {
  7414. auto leaf_calculation = parse_a_calculation(component_value.block().value);
  7415. if (!leaf_calculation) {
  7416. parsing_failed_for_child_node = true;
  7417. return;
  7418. }
  7419. node = leaf_calculation.release_nonnull();
  7420. return;
  7421. }
  7422. // 2. If leaf is a math function, replace leaf with the internal representation of that math function.
  7423. // NOTE: All function tokens at this point should be math functions.
  7424. else if (component_value.is_function()) {
  7425. auto& function = component_value.function();
  7426. auto leaf_calculation = parse_a_calc_function_node(function);
  7427. if (!leaf_calculation) {
  7428. parsing_failed_for_child_node = true;
  7429. return;
  7430. }
  7431. node = leaf_calculation.release_nonnull();
  7432. return;
  7433. }
  7434. // NOTE: If we get here, then we have an UnparsedCalculationNode that didn't get replaced with something else.
  7435. // So, the calc() is invalid.
  7436. dbgln_if(CSS_PARSER_DEBUG, "Leftover UnparsedCalculationNode in calc tree! That probably means the syntax is invalid, but maybe we just didn't implement `{}` yet.", component_value.to_debug_string());
  7437. parsing_failed_for_child_node = true;
  7438. return;
  7439. });
  7440. if (parsing_failed_for_child_node)
  7441. return nullptr;
  7442. // FIXME: 6. Return the result of simplifying a calculation tree from values.
  7443. return single_value.release_value();
  7444. }
  7445. bool Parser::has_ignored_vendor_prefix(StringView string)
  7446. {
  7447. if (!string.starts_with('-'))
  7448. return false;
  7449. if (string.starts_with("--"sv))
  7450. return false;
  7451. if (string.starts_with("-libweb-"sv))
  7452. return false;
  7453. return true;
  7454. }
  7455. NonnullRefPtr<CSSStyleValue> Parser::resolve_unresolved_style_value(ParsingContext const& context, DOM::Element& element, Optional<Selector::PseudoElement::Type> pseudo_element, PropertyID property_id, UnresolvedStyleValue const& unresolved)
  7456. {
  7457. // Unresolved always contains a var() or attr(), unless it is a custom property's value, in which case we shouldn't be trying
  7458. // to produce a different CSSStyleValue from it.
  7459. VERIFY(unresolved.contains_var_or_attr());
  7460. // If the value is invalid, we fall back to `unset`: https://www.w3.org/TR/css-variables-1/#invalid-at-computed-value-time
  7461. auto parser = Parser::create(context, ""sv);
  7462. return parser.resolve_unresolved_style_value(element, pseudo_element, property_id, unresolved);
  7463. }
  7464. class PropertyDependencyNode : public RefCounted<PropertyDependencyNode> {
  7465. public:
  7466. static NonnullRefPtr<PropertyDependencyNode> create(FlyString name)
  7467. {
  7468. return adopt_ref(*new PropertyDependencyNode(move(name)));
  7469. }
  7470. void add_child(NonnullRefPtr<PropertyDependencyNode> new_child)
  7471. {
  7472. for (auto const& child : m_children) {
  7473. if (child->m_name == new_child->m_name)
  7474. return;
  7475. }
  7476. // We detect self-reference already.
  7477. VERIFY(new_child->m_name != m_name);
  7478. m_children.append(move(new_child));
  7479. }
  7480. bool has_cycles()
  7481. {
  7482. if (m_marked)
  7483. return true;
  7484. TemporaryChange change { m_marked, true };
  7485. for (auto& child : m_children) {
  7486. if (child->has_cycles())
  7487. return true;
  7488. }
  7489. return false;
  7490. }
  7491. private:
  7492. explicit PropertyDependencyNode(FlyString name)
  7493. : m_name(move(name))
  7494. {
  7495. }
  7496. FlyString m_name;
  7497. Vector<NonnullRefPtr<PropertyDependencyNode>> m_children;
  7498. bool m_marked { false };
  7499. };
  7500. NonnullRefPtr<CSSStyleValue> Parser::resolve_unresolved_style_value(DOM::Element& element, Optional<Selector::PseudoElement::Type> pseudo_element, PropertyID property_id, UnresolvedStyleValue const& unresolved)
  7501. {
  7502. TokenStream unresolved_values_without_variables_expanded { unresolved.values() };
  7503. Vector<ComponentValue> values_with_variables_expanded;
  7504. HashMap<FlyString, NonnullRefPtr<PropertyDependencyNode>> dependencies;
  7505. if (!expand_variables(element, pseudo_element, string_from_property_id(property_id), dependencies, unresolved_values_without_variables_expanded, values_with_variables_expanded))
  7506. return CSSKeywordValue::create(Keyword::Unset);
  7507. TokenStream unresolved_values_with_variables_expanded { values_with_variables_expanded };
  7508. Vector<ComponentValue> expanded_values;
  7509. if (!expand_unresolved_values(element, string_from_property_id(property_id), unresolved_values_with_variables_expanded, expanded_values))
  7510. return CSSKeywordValue::create(Keyword::Unset);
  7511. auto expanded_value_tokens = TokenStream { expanded_values };
  7512. if (auto parsed_value = parse_css_value(property_id, expanded_value_tokens); !parsed_value.is_error())
  7513. return parsed_value.release_value();
  7514. return CSSKeywordValue::create(Keyword::Unset);
  7515. }
  7516. static RefPtr<CSSStyleValue const> get_custom_property(DOM::Element const& element, Optional<CSS::Selector::PseudoElement::Type> pseudo_element, FlyString const& custom_property_name)
  7517. {
  7518. if (pseudo_element.has_value()) {
  7519. if (auto it = element.custom_properties(pseudo_element).find(custom_property_name); it != element.custom_properties(pseudo_element).end())
  7520. return it->value.value;
  7521. }
  7522. for (auto const* current_element = &element; current_element; current_element = current_element->parent_or_shadow_host_element()) {
  7523. if (auto it = current_element->custom_properties({}).find(custom_property_name); it != current_element->custom_properties({}).end())
  7524. return it->value.value;
  7525. }
  7526. return nullptr;
  7527. }
  7528. bool Parser::expand_variables(DOM::Element& element, Optional<Selector::PseudoElement::Type> pseudo_element, FlyString const& property_name, HashMap<FlyString, NonnullRefPtr<PropertyDependencyNode>>& dependencies, TokenStream<ComponentValue>& source, Vector<ComponentValue>& dest)
  7529. {
  7530. // Arbitrary large value chosen to avoid the billion-laughs attack.
  7531. // https://www.w3.org/TR/css-variables-1/#long-variables
  7532. size_t const MAX_VALUE_COUNT = 16384;
  7533. if (source.remaining_token_count() + dest.size() > MAX_VALUE_COUNT) {
  7534. dbgln("Stopped expanding CSS variables: maximum length reached.");
  7535. return false;
  7536. }
  7537. auto get_dependency_node = [&](FlyString const& name) -> NonnullRefPtr<PropertyDependencyNode> {
  7538. if (auto existing = dependencies.get(name); existing.has_value())
  7539. return *existing.value();
  7540. auto new_node = PropertyDependencyNode::create(name);
  7541. dependencies.set(name, new_node);
  7542. return new_node;
  7543. };
  7544. while (source.has_next_token()) {
  7545. auto const& value = source.consume_a_token();
  7546. if (value.is_block()) {
  7547. auto const& source_block = value.block();
  7548. Vector<ComponentValue> block_values;
  7549. TokenStream source_block_contents { source_block.value };
  7550. if (!expand_variables(element, pseudo_element, property_name, dependencies, source_block_contents, block_values))
  7551. return false;
  7552. dest.empend(SimpleBlock { source_block.token, move(block_values) });
  7553. continue;
  7554. }
  7555. if (!value.is_function()) {
  7556. dest.empend(value.token());
  7557. continue;
  7558. }
  7559. if (!value.function().name.equals_ignoring_ascii_case("var"sv)) {
  7560. auto const& source_function = value.function();
  7561. Vector<ComponentValue> function_values;
  7562. TokenStream source_function_contents { source_function.value };
  7563. if (!expand_variables(element, pseudo_element, property_name, dependencies, source_function_contents, function_values))
  7564. return false;
  7565. dest.empend(Function { source_function.name, move(function_values) });
  7566. continue;
  7567. }
  7568. TokenStream var_contents { value.function().value };
  7569. var_contents.discard_whitespace();
  7570. if (!var_contents.has_next_token())
  7571. return false;
  7572. auto const& custom_property_name_token = var_contents.consume_a_token();
  7573. if (!custom_property_name_token.is(Token::Type::Ident))
  7574. return false;
  7575. auto custom_property_name = custom_property_name_token.token().ident();
  7576. if (!custom_property_name.bytes_as_string_view().starts_with("--"sv))
  7577. return false;
  7578. // Detect dependency cycles. https://www.w3.org/TR/css-variables-1/#cycles
  7579. // We do not do this by the spec, since we are not keeping a graph of var dependencies around,
  7580. // but rebuilding it every time.
  7581. if (custom_property_name == property_name)
  7582. return false;
  7583. auto parent = get_dependency_node(property_name);
  7584. auto child = get_dependency_node(custom_property_name);
  7585. parent->add_child(child);
  7586. if (parent->has_cycles())
  7587. return false;
  7588. if (auto custom_property_value = get_custom_property(element, pseudo_element, custom_property_name)) {
  7589. VERIFY(custom_property_value->is_unresolved());
  7590. TokenStream custom_property_tokens { custom_property_value->as_unresolved().values() };
  7591. if (!expand_variables(element, pseudo_element, custom_property_name, dependencies, custom_property_tokens, dest))
  7592. return false;
  7593. continue;
  7594. }
  7595. // Use the provided fallback value, if any.
  7596. var_contents.discard_whitespace();
  7597. if (var_contents.has_next_token()) {
  7598. auto const& comma_token = var_contents.consume_a_token();
  7599. if (!comma_token.is(Token::Type::Comma))
  7600. return false;
  7601. var_contents.discard_whitespace();
  7602. if (!expand_variables(element, pseudo_element, property_name, dependencies, var_contents, dest))
  7603. return false;
  7604. }
  7605. }
  7606. return true;
  7607. }
  7608. bool Parser::expand_unresolved_values(DOM::Element& element, FlyString const& property_name, TokenStream<ComponentValue>& source, Vector<ComponentValue>& dest)
  7609. {
  7610. auto property = property_id_from_string(property_name);
  7611. while (source.has_next_token()) {
  7612. auto const& value = source.consume_a_token();
  7613. if (value.is_function()) {
  7614. if (value.function().name.equals_ignoring_ascii_case("attr"sv)) {
  7615. if (!substitute_attr_function(element, property_name, value.function(), dest))
  7616. return false;
  7617. continue;
  7618. }
  7619. if (property.has_value()) {
  7620. if (auto maybe_calc_value = parse_calculated_value(value); maybe_calc_value && maybe_calc_value->is_math()) {
  7621. // FIXME: Run the actual simplification algorithm
  7622. auto& calc_value = maybe_calc_value->as_math();
  7623. if (property_accepts_type(*property, ValueType::Angle) && calc_value.resolves_to_angle()) {
  7624. auto resolved_value = calc_value.resolve_angle();
  7625. dest.empend(Token::create_dimension(resolved_value->to_degrees(), "deg"_fly_string));
  7626. continue;
  7627. }
  7628. if (property_accepts_type(*property, ValueType::Frequency) && calc_value.resolves_to_frequency()) {
  7629. auto resolved_value = calc_value.resolve_frequency();
  7630. dest.empend(Token::create_dimension(resolved_value->to_hertz(), "hz"_fly_string));
  7631. continue;
  7632. }
  7633. if (property_accepts_type(*property, ValueType::Length) && calc_value.resolves_to_length()) {
  7634. // FIXME: In order to resolve lengths, we need to know the font metrics in case a font-relative unit
  7635. // is used. So... we can't do that until style is computed?
  7636. // This might be easier once we have calc-simplification implemented.
  7637. }
  7638. if (property_accepts_type(*property, ValueType::Percentage) && calc_value.resolves_to_percentage()) {
  7639. auto resolved_value = calc_value.resolve_percentage();
  7640. dest.empend(Token::create_percentage(resolved_value.value().value()));
  7641. continue;
  7642. }
  7643. if (property_accepts_type(*property, ValueType::Time) && calc_value.resolves_to_time()) {
  7644. auto resolved_value = calc_value.resolve_time();
  7645. dest.empend(Token::create_dimension(resolved_value->to_seconds(), "s"_fly_string));
  7646. continue;
  7647. }
  7648. if (property_accepts_type(*property, ValueType::Number) && calc_value.resolves_to_number()) {
  7649. auto resolved_value = calc_value.resolve_number();
  7650. dest.empend(Token::create_number(resolved_value.value(), Number::Type::Number));
  7651. continue;
  7652. }
  7653. if (property_accepts_type(*property, ValueType::Integer) && calc_value.resolves_to_number()) {
  7654. auto resolved_value = calc_value.resolve_integer();
  7655. dest.empend(Token::create_number(resolved_value.value(), Number::Type::Integer));
  7656. continue;
  7657. }
  7658. }
  7659. }
  7660. auto const& source_function = value.function();
  7661. Vector<ComponentValue> function_values;
  7662. TokenStream source_function_contents { source_function.value };
  7663. if (!expand_unresolved_values(element, property_name, source_function_contents, function_values))
  7664. return false;
  7665. dest.empend(Function { source_function.name, move(function_values) });
  7666. continue;
  7667. }
  7668. if (value.is_block()) {
  7669. auto const& source_block = value.block();
  7670. TokenStream source_block_values { source_block.value };
  7671. Vector<ComponentValue> block_values;
  7672. if (!expand_unresolved_values(element, property_name, source_block_values, block_values))
  7673. return false;
  7674. dest.empend(SimpleBlock { source_block.token, move(block_values) });
  7675. continue;
  7676. }
  7677. dest.empend(value.token());
  7678. }
  7679. return true;
  7680. }
  7681. // https://drafts.csswg.org/css-values-5/#attr-substitution
  7682. bool Parser::substitute_attr_function(DOM::Element& element, FlyString const& property_name, Function const& attr_function, Vector<ComponentValue>& dest)
  7683. {
  7684. // First, parse the arguments to attr():
  7685. // attr() = attr( <q-name> <attr-type>? , <declaration-value>?)
  7686. // <attr-type> = string | url | ident | color | number | percentage | length | angle | time | frequency | flex | <dimension-unit>
  7687. TokenStream attr_contents { attr_function.value };
  7688. attr_contents.discard_whitespace();
  7689. if (!attr_contents.has_next_token())
  7690. return false;
  7691. // - Attribute name
  7692. // FIXME: Support optional attribute namespace
  7693. if (!attr_contents.next_token().is(Token::Type::Ident))
  7694. return false;
  7695. auto attribute_name = attr_contents.consume_a_token().token().ident();
  7696. attr_contents.discard_whitespace();
  7697. // - Attribute type (optional)
  7698. auto attribute_type = "string"_fly_string;
  7699. if (attr_contents.next_token().is(Token::Type::Ident)) {
  7700. attribute_type = attr_contents.consume_a_token().token().ident();
  7701. attr_contents.discard_whitespace();
  7702. }
  7703. // - Comma, then fallback values (optional)
  7704. bool has_fallback_values = false;
  7705. if (attr_contents.has_next_token()) {
  7706. if (!attr_contents.next_token().is(Token::Type::Comma))
  7707. return false;
  7708. (void)attr_contents.consume_a_token(); // Comma
  7709. has_fallback_values = true;
  7710. }
  7711. // Then, run the substitution algorithm:
  7712. // 1. If the attr() function has a substitution value, replace the attr() function by the substitution value.
  7713. // https://drafts.csswg.org/css-values-5/#attr-types
  7714. if (element.has_attribute(attribute_name)) {
  7715. auto attribute_value = element.get_attribute_value(attribute_name);
  7716. if (attribute_type.equals_ignoring_ascii_case("angle"_fly_string)) {
  7717. // Parse a component value from the attribute’s value.
  7718. auto component_value = Parser::Parser::create(m_context, attribute_value).parse_as_component_value();
  7719. // If the result is a <dimension-token> whose unit matches the given type, the result is the substitution value.
  7720. // Otherwise, there is no substitution value.
  7721. if (component_value.has_value() && component_value->is(Token::Type::Dimension)) {
  7722. if (Angle::unit_from_name(component_value->token().dimension_unit()).has_value()) {
  7723. dest.append(component_value.release_value());
  7724. return true;
  7725. }
  7726. }
  7727. } else if (attribute_type.equals_ignoring_ascii_case("color"_fly_string)) {
  7728. // Parse a component value from the attribute’s value.
  7729. // If the result is a <hex-color> or a named color ident, the substitution value is that result as a <color>.
  7730. // Otherwise there is no substitution value.
  7731. auto component_value = Parser::Parser::create(m_context, attribute_value).parse_as_component_value();
  7732. if (component_value.has_value()) {
  7733. if ((component_value->is(Token::Type::Hash)
  7734. && Color::from_string(MUST(String::formatted("#{}", component_value->token().hash_value()))).has_value())
  7735. || (component_value->is(Token::Type::Ident)
  7736. && Color::from_string(component_value->token().ident()).has_value())) {
  7737. dest.append(component_value.release_value());
  7738. return true;
  7739. }
  7740. }
  7741. } else if (attribute_type.equals_ignoring_ascii_case("flex"_fly_string)) {
  7742. // Parse a component value from the attribute’s value.
  7743. auto component_value = Parser::Parser::create(m_context, attribute_value).parse_as_component_value();
  7744. // If the result is a <dimension-token> whose unit matches the given type, the result is the substitution value.
  7745. // Otherwise, there is no substitution value.
  7746. if (component_value.has_value() && component_value->is(Token::Type::Dimension)) {
  7747. if (Flex::unit_from_name(component_value->token().dimension_unit()).has_value()) {
  7748. dest.append(component_value.release_value());
  7749. return true;
  7750. }
  7751. }
  7752. } else if (attribute_type.equals_ignoring_ascii_case("frequency"_fly_string)) {
  7753. // Parse a component value from the attribute’s value.
  7754. auto component_value = Parser::Parser::create(m_context, attribute_value).parse_as_component_value();
  7755. // If the result is a <dimension-token> whose unit matches the given type, the result is the substitution value.
  7756. // Otherwise, there is no substitution value.
  7757. if (component_value.has_value() && component_value->is(Token::Type::Dimension)) {
  7758. if (Frequency::unit_from_name(component_value->token().dimension_unit()).has_value()) {
  7759. dest.append(component_value.release_value());
  7760. return true;
  7761. }
  7762. }
  7763. } else if (attribute_type.equals_ignoring_ascii_case("ident"_fly_string)) {
  7764. // The substitution value is a CSS <custom-ident>, whose value is the literal value of the attribute,
  7765. // with leading and trailing ASCII whitespace stripped. (No CSS parsing of the value is performed.)
  7766. // If the attribute value, after trimming, is the empty string, there is instead no substitution value.
  7767. // If the <custom-ident>’s value is a CSS-wide keyword or `default`, there is instead no substitution value.
  7768. auto substitution_value = MUST(attribute_value.trim(Infra::ASCII_WHITESPACE));
  7769. if (!substitution_value.is_empty()
  7770. && !substitution_value.equals_ignoring_ascii_case("default"sv)
  7771. && !is_css_wide_keyword(substitution_value)) {
  7772. dest.empend(Token::create_ident(substitution_value));
  7773. return true;
  7774. }
  7775. } else if (attribute_type.equals_ignoring_ascii_case("length"_fly_string)) {
  7776. // Parse a component value from the attribute’s value.
  7777. auto component_value = Parser::Parser::create(m_context, attribute_value).parse_as_component_value();
  7778. // If the result is a <dimension-token> whose unit matches the given type, the result is the substitution value.
  7779. // Otherwise, there is no substitution value.
  7780. if (component_value.has_value() && component_value->is(Token::Type::Dimension)) {
  7781. if (Length::unit_from_name(component_value->token().dimension_unit()).has_value()) {
  7782. dest.append(component_value.release_value());
  7783. return true;
  7784. }
  7785. }
  7786. } else if (attribute_type.equals_ignoring_ascii_case("number"_fly_string)) {
  7787. // Parse a component value from the attribute’s value.
  7788. // If the result is a <number-token>, the result is the substitution value.
  7789. // Otherwise, there is no substitution value.
  7790. auto component_value = Parser::Parser::create(m_context, attribute_value).parse_as_component_value();
  7791. if (component_value.has_value() && component_value->is(Token::Type::Number)) {
  7792. dest.append(component_value.release_value());
  7793. return true;
  7794. }
  7795. } else if (attribute_type.equals_ignoring_ascii_case("percentage"_fly_string)) {
  7796. // Parse a component value from the attribute’s value.
  7797. auto component_value = Parser::Parser::create(m_context, attribute_value).parse_as_component_value();
  7798. // If the result is a <percentage-token>, the result is the substitution value.
  7799. // Otherwise, there is no substitution value.
  7800. if (component_value.has_value() && component_value->is(Token::Type::Percentage)) {
  7801. dest.append(component_value.release_value());
  7802. return true;
  7803. }
  7804. } else if (attribute_type.equals_ignoring_ascii_case("string"_fly_string)) {
  7805. // The substitution value is a CSS string, whose value is the literal value of the attribute.
  7806. // (No CSS parsing or "cleanup" of the value is performed.)
  7807. // No value triggers fallback.
  7808. dest.empend(Token::create_string(attribute_value));
  7809. return true;
  7810. } else if (attribute_type.equals_ignoring_ascii_case("time"_fly_string)) {
  7811. // Parse a component value from the attribute’s value.
  7812. auto component_value = Parser::Parser::create(m_context, attribute_value).parse_as_component_value();
  7813. // If the result is a <dimension-token> whose unit matches the given type, the result is the substitution value.
  7814. // Otherwise, there is no substitution value.
  7815. if (component_value.has_value() && component_value->is(Token::Type::Dimension)) {
  7816. if (Time::unit_from_name(component_value->token().dimension_unit()).has_value()) {
  7817. dest.append(component_value.release_value());
  7818. return true;
  7819. }
  7820. }
  7821. } else if (attribute_type.equals_ignoring_ascii_case("url"_fly_string)) {
  7822. // The substitution value is a CSS <url> value, whose url is the literal value of the attribute.
  7823. // (No CSS parsing or "cleanup" of the value is performed.)
  7824. // No value triggers fallback.
  7825. dest.empend(Token::create_url(attribute_value));
  7826. return true;
  7827. } else {
  7828. // Dimension units
  7829. // Parse a component value from the attribute’s value.
  7830. // If the result is a <number-token>, the substitution value is a dimension with the result’s value, and the given unit.
  7831. // Otherwise, there is no substitution value.
  7832. auto component_value = Parser::Parser::create(m_context, attribute_value).parse_as_component_value();
  7833. if (component_value.has_value() && component_value->is(Token::Type::Number)) {
  7834. if (attribute_value == "%"sv) {
  7835. dest.empend(Token::create_dimension(component_value->token().number_value(), attribute_type));
  7836. return true;
  7837. } else if (auto angle_unit = Angle::unit_from_name(attribute_type); angle_unit.has_value()) {
  7838. dest.empend(Token::create_dimension(component_value->token().number_value(), attribute_type));
  7839. return true;
  7840. } else if (auto flex_unit = Flex::unit_from_name(attribute_type); flex_unit.has_value()) {
  7841. dest.empend(Token::create_dimension(component_value->token().number_value(), attribute_type));
  7842. return true;
  7843. } else if (auto frequency_unit = Frequency::unit_from_name(attribute_type); frequency_unit.has_value()) {
  7844. dest.empend(Token::create_dimension(component_value->token().number_value(), attribute_type));
  7845. return true;
  7846. } else if (auto length_unit = Length::unit_from_name(attribute_type); length_unit.has_value()) {
  7847. dest.empend(Token::create_dimension(component_value->token().number_value(), attribute_type));
  7848. return true;
  7849. } else if (auto time_unit = Time::unit_from_name(attribute_type); time_unit.has_value()) {
  7850. dest.empend(Token::create_dimension(component_value->token().number_value(), attribute_type));
  7851. return true;
  7852. } else {
  7853. // Not a dimension unit.
  7854. return false;
  7855. }
  7856. }
  7857. }
  7858. }
  7859. // 2. Otherwise, if the attr() function has a fallback value as its last argument, replace the attr() function by the fallback value.
  7860. // If there are any var() or attr() references in the fallback, substitute them as well.
  7861. if (has_fallback_values)
  7862. return expand_unresolved_values(element, property_name, attr_contents, dest);
  7863. if (attribute_type.equals_ignoring_ascii_case("string"_fly_string)) {
  7864. // If the <attr-type> argument is string, defaults to the empty string if omitted
  7865. dest.empend(Token::create_string({}));
  7866. return true;
  7867. }
  7868. // 3. Otherwise, the property containing the attr() function is invalid at computed-value time.
  7869. return false;
  7870. }
  7871. // https://drafts.csswg.org/css-fonts/#typedef-opentype-tag
  7872. RefPtr<StringStyleValue> Parser::parse_opentype_tag_value(TokenStream<ComponentValue>& tokens)
  7873. {
  7874. // <opentype-tag> = <string>
  7875. // The <opentype-tag> is a case-sensitive OpenType feature tag.
  7876. // As specified in the OpenType specification [OPENTYPE], feature tags contain four ASCII characters.
  7877. // Tag strings longer or shorter than four characters, or containing characters outside the U+20–7E codepoint range are invalid.
  7878. auto transaction = tokens.begin_transaction();
  7879. auto string_value = parse_string_value(tokens);
  7880. if (string_value == nullptr)
  7881. return nullptr;
  7882. auto string = string_value->string_value().bytes_as_string_view();
  7883. if (string.length() != 4)
  7884. return nullptr;
  7885. for (char c : string) {
  7886. if (c < 0x20 || c > 0x7E)
  7887. return nullptr;
  7888. }
  7889. transaction.commit();
  7890. return string_value;
  7891. }
  7892. Parser::ContextType Parser::context_type_for_at_rule(FlyString const& name)
  7893. {
  7894. if (name == "media")
  7895. return ContextType::AtMedia;
  7896. if (name == "font-face")
  7897. return ContextType::AtFontFace;
  7898. if (name == "keyframes")
  7899. return ContextType::AtKeyframes;
  7900. if (name == "supports")
  7901. return ContextType::AtSupports;
  7902. if (name == "layer")
  7903. return ContextType::AtLayer;
  7904. if (name == "property")
  7905. return ContextType::AtProperty;
  7906. return ContextType::Unknown;
  7907. }
  7908. }