123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694 |
- {
- This file is part of the Free Pascal Run time library.
- Copyright (c) 2023 by Michael Van Canneyt ([email protected])
- This file contains CSS utility class
- See the File COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************
- }
- {$IFNDEF FPC_DOTTEDUNITS}
- unit fpCSSResParser;
- {$ENDIF FPC_DOTTEDUNITS}
- {$mode ObjFPC}{$H+}
- {$Interfaces CORBA}
- {$ModeSwitch AdvancedRecords}
- {$IF FPC_FULLVERSION>30300}
- {$WARN 6060 off} // Case statement does not handle all possible cases
- {$ENDIF}
- {$WARN 6058 off : Call to subroutine "$1" marked as inline is not inlined}
- interface
- {$IFDEF FPC_DOTTEDUNITS}
- uses
- System.Classes, System.SysUtils, System.Math, System.Contnrs, System.StrUtils,
- Fcl.AVLTree, FpCss.Tree, FpCss.Scanner, FpCss.Parser;
- {$ELSE FPC_DOTTEDUNITS}
- uses
- Classes, SysUtils, Math, Contnrs, AVL_Tree, fpCSSTree, fpCSSScanner,
- fpCSSParser;
- {$ENDIF FPC_DOTTEDUNITS}
- const
- CSSIDNone = 0;
- // built-in attribute IDs
- CSSAttributeID_ID = 1; // id of attribute key 'id'
- CSSAttributeID_Class = 2; // id of attribute key 'class'
- CSSAttributeID_All = 3; // id of attribute key 'all'
- CSSAttributeID_LastResolver = CSSAttributeID_All;
- // built-in type IDs
- CSSTypeID_Universal = 1; // id of type '*'
- CSSTypeID_LastResolver = CSSTypeID_Universal;
- // built-in pseudo class IDs
- CSSPseudoID_Root = 1; // :root
- CSSPseudoID_Empty = CSSPseudoID_Root+1; // :empty
- CSSPseudoID_FirstChild = CSSPseudoID_Empty+1; // :first-child
- CSSPseudoID_LastChild = CSSPseudoID_FirstChild+1; // :last-child
- CSSPseudoID_OnlyChild = CSSPseudoID_LastChild+1; // :only-child
- CSSPseudoID_FirstOfType = CSSPseudoID_OnlyChild+1; // :first-of-type
- CSSPseudoID_LastOfType = CSSPseudoID_FirstOfType+1; // :last-of-type
- CSSPseudoID_OnlyOfType = CSSPseudoID_LastOfType+1; // :only-of-type
- CSSPseudoID_LastResolver = CSSPseudoID_OnlyOfType;
- CSSPseudoClassNames: array[0..CSSPseudoID_LastResolver] of TCSSString = (
- '?',
- ':root',
- ':empty',
- ':first-child',
- ':last-child',
- ':only-child',
- ':first-of-type',
- ':last-of-type',
- ':only-of-type'
- );
- // built-in pseudo function IDs
- CSSCallID_Not = 1; // :not()
- CSSCallID_Is = CSSCallID_Not+1; // :is()
- CSSCallID_Where = CSSCallID_Is+1; // :where()
- CSSCallID_Has = CSSCallID_Where+1; // :has()
- CSSCallID_NthChild = CSSCallID_Has+1; // :nth-child(n)
- CSSCallID_NthLastChild = CSSCallID_NthChild+1; // :nth-last-child(n)
- CSSCallID_NthOfType = CSSCallID_NthLastChild+1; // :nth-of-type(n)
- CSSCallID_NthLastOfType = CSSCallID_NthOfType+1; // :nth-last-of-type(n)
- CSSCallID_LastResolver = CSSCallID_NthLastOfType;
- CSSSelectorCallNames: array[0..CSSCallID_LastResolver] of TCSSString = (
- '?',
- ':not()',
- ':is()',
- ':where()',
- ':has()',
- ':nth-child(n)',
- ':nth-last-child(n)',
- ':nth-of-type(n)',
- ':nth-last-of-type(n)'
- );
- // keywords
- CSSKeywordNone = 1;
- CSSKeywordInitial = CSSKeywordNone+1;
- CSSKeywordInherit = CSSKeywordInitial+1;
- CSSKeywordUnset = CSSKeywordInherit+1;
- CSSKeywordRevert = CSSKeywordUnset+1;
- CSSKeywordRevertLayer = CSSKeywordRevert+1;
- CSSKeywordAuto = CSSKeywordRevertLayer+1;
- CSSKeyword_LastResolver = CSSKeywordAuto;
- // attribute functions
- CSSAttrFuncVar = 1;
- CSSMinSafeIntDouble = -$1fffffffffffff; // -9007199254740991 54 bits (52 plus signed bit plus implicit highest bit)
- CSSMaxSafeIntDouble = $1fffffffffffff; // 9007199254740991
- type
- TCSSMsgID = int64; // used for message numbers, e.g. error codes
- TCSSNumericalID = integer; // used for IDs of each type and attribute
- TCSSNumericalIDArray = array of TCSSNumericalID;
- TCSSNumericalIDKind = (
- nikAttribute,
- nikPseudoClass, // e.g. "hover" of ":hover"
- nikPseudoFunction, // e.g. "is" of ":is()"
- nikType,
- nikKeyword,
- nikAttrFunction // e.g. "calc" of "calc()"
- );
- TCSSNumericalIDs = set of TCSSNumericalIDKind;
- const
- nikAllDescriptors = [nikAttribute,nikPseudoClass,nikType]; // all items having a descriptor
- CSSNumericalIDKindNames: array[TCSSNumericalIDKind] of TCSSString = (
- 'Type',
- 'PseudoClass',
- 'PseudoFunction',
- 'Attribute',
- 'Keyword',
- 'AttributeFunction'
- );
- type
- TCSSAlphaColor = DWord;
- TCSSNamedColor = record
- Name: TCSSString;
- Color: TCSSAlphaColor;
- end;
- const
- CSSNamedColors: array[0..148] of TCSSNamedColor = (
- (Name: 'aliceblue'; Color: TCSSAlphaColor($fff0f8ff)),
- (Name: 'antiquewhite'; Color: TCSSAlphaColor($fffaebd7)),
- (Name: 'aqua'; Color: TCSSAlphaColor($ff00ffff)),
- (Name: 'aquamarine'; Color: TCSSAlphaColor($ff7fffd4)),
- (Name: 'azure'; Color: TCSSAlphaColor($fff0ffff)),
- (Name: 'beige'; Color: TCSSAlphaColor($fff5f5dc)),
- (Name: 'bisque'; Color: TCSSAlphaColor($ffffe4c4)),
- (Name: 'black'; Color: TCSSAlphaColor($ff000000)),
- (Name: 'blanchedalmond'; Color: TCSSAlphaColor($ffffebcd)),
- (Name: 'blue'; Color: TCSSAlphaColor($ff0000ff)),
- (Name: 'blueviolet'; Color: TCSSAlphaColor($ff8a2be2)),
- (Name: 'brown'; Color: TCSSAlphaColor($ffa52a2a)),
- (Name: 'burlywood'; Color: TCSSAlphaColor($ffdeb887)),
- (Name: 'cadetblue'; Color: TCSSAlphaColor($ff5f9ea0)),
- (Name: 'chartreuse'; Color: TCSSAlphaColor($ff7fff00)),
- (Name: 'chocolate'; Color: TCSSAlphaColor($ffd2691e)),
- (Name: 'coral'; Color: TCSSAlphaColor($ffff7f50)),
- (Name: 'cornflowerblue'; Color: TCSSAlphaColor($ff6495ed)),
- (Name: 'cornsilk'; Color: TCSSAlphaColor($fffff8dc)),
- (Name: 'crimson'; Color: TCSSAlphaColor($ffdc143c)),
- (Name: 'cyan'; Color: TCSSAlphaColor($ff00ffff)),
- (Name: 'darkblue'; Color: TCSSAlphaColor($ff00008b)),
- (Name: 'darkcyan'; Color: TCSSAlphaColor($ff008b8b)),
- (Name: 'darkgoldenrod'; Color: TCSSAlphaColor($ffb8860b)),
- (Name: 'darkgray'; Color: TCSSAlphaColor($ffa9a9a9)),
- (Name: 'darkgreen'; Color: TCSSAlphaColor($ff006400)),
- (Name: 'darkgrey'; Color: TCSSAlphaColor($ffa9a9a9)),
- (Name: 'darkkhaki'; Color: TCSSAlphaColor($ffbdb76b)),
- (Name: 'darkmagenta'; Color: TCSSAlphaColor($ff8b008b)),
- (Name: 'darkolivegreen'; Color: TCSSAlphaColor($ff556b2f)),
- (Name: 'darkorange'; Color: TCSSAlphaColor($ffff8c00)),
- (Name: 'darkorchid'; Color: TCSSAlphaColor($ff9932cc)),
- (Name: 'darkred'; Color: TCSSAlphaColor($ff8b0000)),
- (Name: 'darksalmon'; Color: TCSSAlphaColor($ffe9967a)),
- (Name: 'darkseagreen'; Color: TCSSAlphaColor($ff8fbc8f)),
- (Name: 'darkslateblue'; Color: TCSSAlphaColor($ff483d8b)),
- (Name: 'darkslategray'; Color: TCSSAlphaColor($ff2f4f4f)),
- (Name: 'darkslategrey'; Color: TCSSAlphaColor($ff2f4f4f)),
- (Name: 'darkturquoise'; Color: TCSSAlphaColor($ff00ced1)),
- (Name: 'darkviolet'; Color: TCSSAlphaColor($ff9400d3)),
- (Name: 'deeppink'; Color: TCSSAlphaColor($ffff1493)),
- (Name: 'deepskyblue'; Color: TCSSAlphaColor($ff00bfff)),
- (Name: 'dimgray'; Color: TCSSAlphaColor($ff696969)),
- (Name: 'dimgrey'; Color: TCSSAlphaColor($ff696969)),
- (Name: 'dodgerblue'; Color: TCSSAlphaColor($ff1e90ff)),
- (Name: 'firebrick'; Color: TCSSAlphaColor($ffb22222)),
- (Name: 'floralwhite'; Color: TCSSAlphaColor($fffffaf0)),
- (Name: 'forestgreen'; Color: TCSSAlphaColor($ff228b22)),
- (Name: 'fuchsia'; Color: TCSSAlphaColor($ffff00ff)),
- (Name: 'gainsboro'; Color: TCSSAlphaColor($ffdcdcdc)),
- (Name: 'ghostwhite'; Color: TCSSAlphaColor($fff8f8ff)),
- (Name: 'gold'; Color: TCSSAlphaColor($ffffd700)),
- (Name: 'goldenrod'; Color: TCSSAlphaColor($ffdaa520)),
- (Name: 'gray'; Color: TCSSAlphaColor($ff808080)),
- (Name: 'green'; Color: TCSSAlphaColor($ff008000)),
- (Name: 'greenyellow'; Color: TCSSAlphaColor($ffadff2f)),
- (Name: 'grey'; Color: TCSSAlphaColor($ff808080)),
- (Name: 'honeydew'; Color: TCSSAlphaColor($fff0fff0)),
- (Name: 'hotpink'; Color: TCSSAlphaColor($ffff69b4)),
- (Name: 'indianred'; Color: TCSSAlphaColor($ffcd5c5c)),
- (Name: 'indigo'; Color: TCSSAlphaColor($ff4b0082)),
- (Name: 'ivory'; Color: TCSSAlphaColor($fffffff0)),
- (Name: 'khaki'; Color: TCSSAlphaColor($fff0e68c)),
- (Name: 'lavender'; Color: TCSSAlphaColor($ffe6e6fa)),
- (Name: 'lavenderblush'; Color: TCSSAlphaColor($fffff0f5)),
- (Name: 'lawngreen'; Color: TCSSAlphaColor($ff7cfc00)),
- (Name: 'lemonchiffon'; Color: TCSSAlphaColor($fffffacd)),
- (Name: 'lightblue'; Color: TCSSAlphaColor($ffadd8e6)),
- (Name: 'lightcoral'; Color: TCSSAlphaColor($fff08080)),
- (Name: 'lightcyan'; Color: TCSSAlphaColor($ffe0ffff)),
- (Name: 'lightgoldenrodyellow'; Color: TCSSAlphaColor($fffafad2)),
- (Name: 'lightgray'; Color: TCSSAlphaColor($ffd3d3d3)),
- (Name: 'lightgreen'; Color: TCSSAlphaColor($ff90ee90)),
- (Name: 'lightgrey'; Color: TCSSAlphaColor($ffd3d3d3)),
- (Name: 'lightpink'; Color: TCSSAlphaColor($ffffb6c1)),
- (Name: 'lightsalmon'; Color: TCSSAlphaColor($ffffa07a)),
- (Name: 'lightseagreen'; Color: TCSSAlphaColor($ff20b2aa)),
- (Name: 'lightskyblue'; Color: TCSSAlphaColor($ff87cefa)),
- (Name: 'lightslategray'; Color: TCSSAlphaColor($ff778899)),
- (Name: 'lightslategrey'; Color: TCSSAlphaColor($ff778899)),
- (Name: 'lightsteelblue'; Color: TCSSAlphaColor($ffb0c4de)),
- (Name: 'lightyellow'; Color: TCSSAlphaColor($ffffffe0)),
- (Name: 'lime'; Color: TCSSAlphaColor($ff00ff00)),
- (Name: 'limegreen'; Color: TCSSAlphaColor($ff32cd32)),
- (Name: 'linen'; Color: TCSSAlphaColor($fffaf0e6)),
- (Name: 'magenta'; Color: TCSSAlphaColor($ffff00ff)),
- (Name: 'maroon'; Color: TCSSAlphaColor($ff800000)),
- (Name: 'mediumaquamarine'; Color: TCSSAlphaColor($ff66cdaa)),
- (Name: 'mediumblue'; Color: TCSSAlphaColor($ff0000cd)),
- (Name: 'mediumorchid'; Color: TCSSAlphaColor($ffba55d3)),
- (Name: 'mediumpurple'; Color: TCSSAlphaColor($ff9370db)),
- (Name: 'mediumseagreen'; Color: TCSSAlphaColor($ff3cb371)),
- (Name: 'mediumslateblue'; Color: TCSSAlphaColor($ff7b68ee)),
- (Name: 'mediumspringgreen'; Color: TCSSAlphaColor($ff00fa9a)),
- (Name: 'mediumturquoise'; Color: TCSSAlphaColor($ff48d1cc)),
- (Name: 'mediumvioletred'; Color: TCSSAlphaColor($ffc71585)),
- (Name: 'midnightblue'; Color: TCSSAlphaColor($ff191970)),
- (Name: 'mintcream'; Color: TCSSAlphaColor($fff5fffa)),
- (Name: 'mistyrose'; Color: TCSSAlphaColor($ffffe4e1)),
- (Name: 'moccasin'; Color: TCSSAlphaColor($ffffe4b5)),
- (Name: 'navajowhite'; Color: TCSSAlphaColor($ffffdead)),
- (Name: 'navy'; Color: TCSSAlphaColor($ff000080)),
- (Name: 'oldlace'; Color: TCSSAlphaColor($fffdf5e6)),
- (Name: 'olive'; Color: TCSSAlphaColor($ff808000)),
- (Name: 'olivedrab'; Color: TCSSAlphaColor($ff6b8e23)),
- (Name: 'orange'; Color: TCSSAlphaColor($ffffa500)),
- (Name: 'orangered'; Color: TCSSAlphaColor($ffff4500)),
- (Name: 'orchid'; Color: TCSSAlphaColor($ffda70d6)),
- (Name: 'palegoldenrod'; Color: TCSSAlphaColor($ffeee8aa)),
- (Name: 'palegreen'; Color: TCSSAlphaColor($ff98fb98)),
- (Name: 'paleturquoise'; Color: TCSSAlphaColor($ffafeeee)),
- (Name: 'palevioletred'; Color: TCSSAlphaColor($ffdb7093)),
- (Name: 'papayawhip'; Color: TCSSAlphaColor($ffffefd5)),
- (Name: 'peachpuff'; Color: TCSSAlphaColor($ffffdab9)),
- (Name: 'peru'; Color: TCSSAlphaColor($ffcd853f)),
- (Name: 'pink'; Color: TCSSAlphaColor($ffffc0cb)),
- (Name: 'plum'; Color: TCSSAlphaColor($ffdda0dd)),
- (Name: 'powderblue'; Color: TCSSAlphaColor($ffb0e0e6)),
- (Name: 'purple'; Color: TCSSAlphaColor($ff800080)),
- (Name: 'rebeccapurple'; Color: TCSSAlphaColor($ff663399)),
- (Name: 'red'; Color: TCSSAlphaColor($ffff0000)),
- (Name: 'rosybrown'; Color: TCSSAlphaColor($ffbc8f8f)),
- (Name: 'royalblue'; Color: TCSSAlphaColor($ff4169e1)),
- (Name: 'saddlebrown'; Color: TCSSAlphaColor($ff8b4513)),
- (Name: 'salmon'; Color: TCSSAlphaColor($fffa8072)),
- (Name: 'sandybrown'; Color: TCSSAlphaColor($fff4a460)),
- (Name: 'seagreen'; Color: TCSSAlphaColor($ff2e8b57)),
- (Name: 'seashell'; Color: TCSSAlphaColor($fffff5ee)),
- (Name: 'sienna'; Color: TCSSAlphaColor($ffa0522d)),
- (Name: 'silver'; Color: TCSSAlphaColor($ffc0c0c0)),
- (Name: 'skyblue'; Color: TCSSAlphaColor($ff87ceeb)),
- (Name: 'slateblue'; Color: TCSSAlphaColor($ff6a5acd)),
- (Name: 'slategray'; Color: TCSSAlphaColor($ff708090)),
- (Name: 'slategrey'; Color: TCSSAlphaColor($ff708090)),
- (Name: 'snow'; Color: TCSSAlphaColor($fffffafa)),
- (Name: 'springgreen'; Color: TCSSAlphaColor($ff00ff7f)),
- (Name: 'steelblue'; Color: TCSSAlphaColor($ff4682b4)),
- (Name: 'tan'; Color: TCSSAlphaColor($ffd2b48c)),
- (Name: 'teal'; Color: TCSSAlphaColor($ff008080)),
- (Name: 'thistle'; Color: TCSSAlphaColor($ffd8bfd8)),
- (Name: 'tomato'; Color: TCSSAlphaColor($ffff6347)),
- (Name: 'transparent'; Color: TCSSAlphaColor($ff0)),
- (Name: 'turquoise'; Color: TCSSAlphaColor($ff40e0d0)),
- (Name: 'violet'; Color: TCSSAlphaColor($ffee82ee)),
- (Name: 'wheat'; Color: TCSSAlphaColor($fff5deb3)),
- (Name: 'white'; Color: TCSSAlphaColor($ffffffff)),
- (Name: 'whitesmoke'; Color: TCSSAlphaColor($fff5f5f5)),
- (Name: 'yellow'; Color: TCSSAlphaColor($ffffff00)),
- (Name: 'yellowgreen'; Color: TCSSAlphaColor($ff9acd32))
- );
- type
- { TCSSRegistryNamedItem }
- TCSSRegistryNamedItem = class
- public
- Name: TCSSString; // case sensitive
- Index: TCSSNumericalID;
- end;
- { TCSSPseudoClassDesc }
- TCSSPseudoClassDesc = class(TCSSRegistryNamedItem)
- public
- end;
- TCSSPseudoClassDescClass = class of TCSSPseudoClassDesc;
- TCSSPseudoClassDescArray = array of TCSSPseudoClassDesc;
- { TCSSTypeDesc }
- TCSSTypeDesc = class(TCSSRegistryNamedItem)
- public
- end;
- TCSSTypeDescClass = class of TCSSTypeDesc;
- TCSSTypeDescArray = array of TCSSTypeDesc;
- { TCSSAttributeKeyData }
- TCSSAttributeKeyData = class(TCSSElementOwnedData)
- public
- Invalid: boolean;
- Complete: boolean;
- Value: TCSSString;
- end;
- TCSSAttributeKeyDataClass = class of TCSSAttributeKeyData;
- TCSSBaseResolver = class;
- TCSSResolverParser = class;
- { TCSSAttributeDesc - general properties of a CSS attribute }
- TCSSAttributeDesc = class(TCSSRegistryNamedItem)
- public
- type
- TCheckEvent = function(Resolver: TCSSBaseResolver): boolean of object;
- TSplitShorthandEvent = procedure(Resolver: TCSSBaseResolver;
- var AttrIDs: TCSSNumericalIDArray; var Values: TCSSStringArray) of object;
- public
- Inherits: boolean; // true = the default value is the parent's value
- All: boolean; // true = can be changed by the 'all' attribute
- InitialValue: TCSSString;
- CompProps: array of TCSSAttributeDesc; // if this attribute is a shorthand,
- // these are the component properties (longhands + sub-shorthands like border-width)
- // used by the cascade algorithm to delete all overwritten properties
- OnCheck: TCheckEvent; // called by the parser after reading a declaration and there is no var()
- // return false if invalid, so the resolver skips this declaration
- OnSplitShorthand: TSplitShorthandEvent; // called by resolver after resolving var(), if any value is empty, the initialvalue is used
- end;
- TCSSAttributeDescClass = class of TCSSAttributeDesc;
- TCSSAttributeDescArray = array of TCSSAttributeDesc;
- { TCSSRegistry }
- TCSSRegistry = class
- private
- FAttrFunctionCount: TCSSNumericalID;
- FAttributeCount: TCSSNumericalID;
- FHashLists: array[TCSSNumericalIDKind] of TFPHashList; // name to TCSSRegistryNamedItem
- FKeywordCount: TCSSNumericalID;
- FPseudoClassCount: TCSSNumericalID;
- FPseudoFunctionCount: TCSSNumericalID;
- FStamp, FModifiedStamp: TCSSNumericalID;
- FTypeCount: TCSSNumericalID;
- function GetModified: boolean;
- procedure SetModified(const AValue: boolean);
- public
- constructor Create;
- procedure Init; virtual; // add basic items
- destructor Destroy; override;
- function FindNamedItem(Kind: TCSSNumericalIDKind; const aName: TCSSString): TCSSRegistryNamedItem; overload;
- function IndexOfNamedItem(Kind: TCSSNumericalIDKind; const aName: TCSSString): TCSSNumericalID; overload;
- procedure ConsistencyCheck; virtual;
- procedure ChangeStamp;
- property Stamp: TCSSNumericalID read FStamp; // always >0
- property Modified: boolean read GetModified write SetModified;
- public
- // attributes
- Attributes: TCSSAttributeDescArray; // Note: Attributes[0] is nil to spot bugs easily
- Attribute_ClassOf: TCSSAttributeDescClass;
- NotAllAttributes: TCSSAttributeDescArray;
- function AddAttribute(Attr: TCSSAttributeDesc): TCSSAttributeDesc; overload;
- function AddAttribute(const aName: TCSSString; const aInitialValue: TCSSString = '';
- aInherits: boolean = false; aAll: boolean = true; aClass: TCSSAttributeDescClass = nil): TCSSAttributeDesc; overload;
- function FindAttribute(const aName: TCSSString): TCSSAttributeDesc; overload;
- function IndexOfAttributeName(const aName: TCSSString): TCSSNumericalID; overload;
- procedure AddSplitLonghand(var AttrIDs: TCSSNumericalIDArray; var Values: TCSSStringArray; AttrID: TCSSNumericalID; const Value: TCSSString); overload;
- procedure AddSplitLonghandSides(var AttrIDs: TCSSNumericalIDArray; var Values: TCSSStringArray;
- TopID, RightID, BottomID, LeftID: TCSSNumericalID; const Found: TCSSStringArray); overload;
- procedure AddSplitLonghandCorners(var AttrIDs: TCSSNumericalIDArray; var Values: TCSSStringArray;
- TopLeftID, TopRightID, BottomLeftID, BottomRightID: TCSSNumericalID; const Found: TCSSStringArray); overload;
- property AttributeCount: TCSSNumericalID read FAttributeCount;
- public
- // pseudo classes
- PseudoClasses: TCSSPseudoClassDescArray; // Note: PseudoClasses[0] is nil to spot bugs easily
- PseudoClass_ClassOf: TCSSPseudoClassDescClass;
- function AddPseudoClass(aPseudo: TCSSPseudoClassDesc): TCSSPseudoClassDesc; overload;
- function AddPseudoClass(const aName: TCSSString; aClass: TCSSPseudoClassDescClass = nil): TCSSPseudoClassDesc; overload;
- function FindPseudoClass(const aName: TCSSString): TCSSPseudoClassDesc; overload;
- function IndexOfPseudoClassName(const aName: TCSSString): TCSSNumericalID; overload;
- property PseudoClassCount: TCSSNumericalID read FPseudoClassCount;
- public
- // pseudo functions lowercase (they are parsed case insensitive)
- PseudoFunctions: TCSSStringArray;
- function AddPseudoFunction(const aName: TCSSString): TCSSNumericalID; overload;
- function IndexOfPseudoFunction(const aName: TCSSString): TCSSNumericalID; overload;
- property PseudoFunctionCount: TCSSNumericalID read FPseudoFunctionCount;
- public
- // types
- Types: TCSSTypeDescArray; // Note: Types[0] is nil to spot bugs easily
- Type_ClassOf: TCSSTypeDescClass;
- function AddType(aType: TCSSTypeDesc): TCSSTypeDesc; overload;
- function AddType(const aName: TCSSString; aClass: TCSSTypeDescClass = nil): TCSSTypeDesc; overload;
- function FindType(const aName: TCSSString): TCSSTypeDesc; overload;
- function IndexOfTypeName(const aName: TCSSString): TCSSNumericalID; overload;
- property TypeCount: TCSSNumericalID read FTypeCount;
- public
- // keywords
- Keywords: TCSSStringArray;
- kwFirstColor, kwLastColor, kwTransparent: TCSSNumericalID;
- function AddKeyword(const aName: TCSSString): TCSSNumericalID; overload;
- procedure AddKeywords(const Names: TCSSStringArray; out First, Last: TCSSNumericalID); overload;
- function IndexOfKeyword(const aName: TCSSString): TCSSNumericalID; overload;
- procedure AddColorKeywords; virtual;
- function GetNamedColor(const aName: TCSSString): TCSSAlphaColor; virtual; overload;
- function GetKeywordColor(KeywordID: TCSSNumericalID): TCSSAlphaColor; virtual; overload;
- property KeywordCount: TCSSNumericalID read FKeywordCount;
- public
- // attribute functions
- AttrFunctions: TCSSStringArray;
- const afVar = CSSAttrFuncVar;
- function AddAttrFunction(const aName: TCSSString): TCSSNumericalID; overload;
- function IndexOfAttrFunction(const aName: TCSSString): TCSSNumericalID; overload;
- property AttrFunctionCount: TCSSNumericalID read FAttrFunctionCount;
- end;
- TCSSValueParserLogEvent = procedure(MsgType: TEventType; const ID: TCSSMsgID;
- const Msg: TCSSString; PosEl: TCSSElement) of object;
- { TCSSResolvedIdentifierElement }
- TCSSResolvedIdentifierElement = class(TCSSIdentifierElement)
- public
- NumericalID: TCSSNumericalID;
- Kind: TCSSNumericalIDKind;
- end;
- { TCSSResolvedPseudoClassElement }
- TCSSResolvedPseudoClassElement = class(TCSSPseudoClassElement)
- public
- NumericalID: TCSSNumericalID;
- Kind: TCSSNumericalIDKind;
- end;
- { TCSSNthChildParams }
- TCSSNthChildParams = class
- public
- Modulo: integer;
- Start: integer;
- HasOf: boolean; // for nth-of-type() HasOf=true and OfSelector=nil
- OfSelector: TCSSElement;
- end;
- TCSSNthChildParamsClass = class of TCSSNthChildParams;
- { TCSSResolvedCallElement }
- TCSSResolvedCallElement = class(TCSSCallElement)
- public
- NameNumericalID: TCSSNumericalID;
- Kind: TCSSNumericalIDKind;
- Params: TObject; // e.g. TCSSNthChildParams
- destructor Destroy; override;
- end;
- { TCSSValueData }
- TCSSValueData = class(TCSSElementOwnedData)
- public
- NormValue: TCSSString; // normalized value, stripped of comments and e.g. '0.1' instead of '000.100' or '.1'
- end;
- TCSSResValueKind = (
- rvkNone,
- rvkInvalid,
- rvkSymbol,
- rvkFloat,
- rvkCalcFloat,
- rvkKeyword,
- rvkKeywordUnknown,
- rvkFunction,
- rvkFunctionUnknown,
- rvkString,
- rvkHexColor
- );
- { TCSSResCompValue }
- TCSSResCompValue = record
- Kind: TCSSResValueKind;
- StartP, EndP: PCSSChar;
- function AsString: TCSSString;
- function FloatAsString: TCSSString;
- case longint of
- 1: (Float: Double; FloatUnit: TCSSUnit);
- 2: (KeywordID: TCSSNumericalID);
- 3: (FunctionID: TCSSNumericalID; BracketOpen: PCSSChar);
- 4: (Symbol: TCSSToken);
- end;
- { TCSSCheckAttrParams_Dimension }
- TCSSCheckAttrParams_Dimension = record
- public
- AllowedUnits: TCSSUnits;
- AllowNegative, AllowFrac: boolean;
- AllowedKeywordIDs: TCSSNumericalIDArray;
- function Fits(const ResValue: TCSSResCompValue): boolean; overload;
- end;
- { TCSSBaseResolver }
- TCSSBaseResolver = class(TComponent)
- private
- FCSSRegistry: TCSSRegistry;
- protected
- procedure SetCSSRegistry(const AValue: TCSSRegistry); virtual;
- public
- CurAttrData: TCSSAttributeKeyData;
- CurDesc: TCSSAttributeDesc;
- CurValue: TCSSString;
- CurComp: TCSSResCompValue;
- function InitParseAttr(Desc: TCSSAttributeDesc; AttrData: TCSSAttributeKeyData; const Value: TCSSString): boolean; virtual; // true if parsing can start
- procedure InitParseAttr(const Value: TCSSString); virtual;
- // check whole attribute, skipping invalid values, emit warnings:
- function CheckAttribute_Keyword(const AllowedKeywordIDs: TCSSNumericalIDArray): boolean; virtual;
- function CheckAttribute_CommaList_Keyword(const AllowedKeywordIDs: TCSSNumericalIDArray): boolean; virtual;
- function CheckAttribute_Dimension(const Params: TCSSCheckAttrParams_Dimension): boolean; virtual;
- function CheckAttribute_Color(const AllowedKeywordIDs: TCSSNumericalIDArray): boolean; virtual;
- // parse whole attribute, skipping invalid values:
- function ReadNext: boolean;
- function ReadAttribute_Keyword(out Invalid: boolean; const AllowedKeywordIDs: TCSSNumericalIDArray): boolean; virtual;
- function ReadAttribute_Dimension(out Invalid: boolean; const Params: TCSSCheckAttrParams_Dimension): boolean; virtual;
- function ReadAttribute_Color(out Invalid: boolean; const AllowedKeywordIDs: TCSSNumericalIDArray): boolean; virtual;
- function IsBaseKeyword(KeywordID: TCSSNumericalID): boolean;
- function IsKeywordIn(aKeywordID: TCSSNumericalID; const KeywordIDs: TCSSNumericalIDArray): boolean; overload;
- function IsKeywordIn(const KeywordIDs: TCSSNumericalIDArray): boolean; overload;
- function IsLengthOrPercentage(AllowNegative: boolean): boolean; overload;
- function IsLengthOrPercentage(const ResValue: TCSSResCompValue; AllowNegative: boolean): boolean; overload;
- function IsSymbol(Token: TCSSToken): boolean; overload;
- function GetCompString: TCSSString; overload;
- function GetCompString(const aValue: string; const ResValue: TCSSResCompValue): TCSSString; overload;
- // low level functions to parse attribute components
- function ReadComp(var aComp: TCSSResCompValue): boolean; // true if parsing attribute can continue
- procedure ReadWordID(var aComp: TCSSResCompValue);
- class function ReadValue(var aComp: TCSSResCompValue): boolean; // true if parsing attribute can continue, not using CSSRegistry
- class function ReadNumber(var aComp: TCSSResCompValue): boolean;
- class function ReadIdentifier(var aComp: TCSSResCompValue): boolean;
- class procedure SkipToEndOfAttribute(var p: PCSSChar);
- class function SkipString(var p: PCSSChar): boolean;
- class function SkipBrackets(var p: PCSSChar; Lvl: integer = 1): boolean;
- // registry
- function GetAttributeID(const aName: TCSSString; AutoCreate: boolean = false): TCSSNumericalID; virtual;
- function GetAttributeDesc(AttrID: TCSSNumericalID): TCSSAttributeDesc; virtual;
- function GetTypeID(const aName: TCSSString): TCSSNumericalID; virtual;
- function GetPseudoClassID(const aName: TCSSString): TCSSNumericalID; virtual;
- function GetPseudoFunctionID(const aName: TCSSString): TCSSNumericalID; virtual;
- property CSSRegistry: TCSSRegistry read FCSSRegistry write SetCSSRegistry;
- end;
- { TCSSResolverParser
- - resolves identifiers to IDs
- - warns about constructs unsupported by resolver }
- TCSSResolverParser = class(TCSSParser)
- private
- FOnLog: TCSSValueParserLogEvent;
- FResolver: TCSSBaseResolver;
- protected
- function ResolveAttribute(El: TCSSResolvedIdentifierElement): TCSSNumericalID; virtual;
- function ResolveType(El: TCSSResolvedIdentifierElement): TCSSNumericalID; virtual;
- function ResolvePseudoClass(El: TCSSResolvedPseudoClassElement): TCSSNumericalID; virtual;
- function ResolvePseudoFunction(El: TCSSResolvedCallElement): TCSSNumericalID; virtual;
- function ParseCall(aName: TCSSString; IsSelector: boolean): TCSSCallElement; override;
- function ParseDeclaration(aIsAt: Boolean): TCSSDeclarationElement; override;
- function ParseSelector: TCSSElement; override;
- procedure CheckSelector(El: TCSSElement); virtual;
- procedure CheckSelectorArray(anArray: TCSSArrayElement); virtual;
- procedure CheckSelectorArrayBinary(aBinary: TCSSBinaryElement); virtual;
- procedure CheckSelectorBinary(aBinary: TCSSBinaryElement); virtual;
- procedure CheckSelectorList(aList: TCSSListElement); virtual;
- procedure CheckNthChildParams(aCall: TCSSResolvedCallElement); virtual;
- function ComputeValue(El: TCSSElement): TCSSString; virtual;
- public
- CSSNthChildParamsClass: TCSSNthChildParamsClass;
- CSSAttributeKeyDataClass: TCSSAttributeKeyDataClass;
- constructor Create(AScanner: TCSSScanner); override; overload;
- destructor Destroy; override;
- procedure Log(MsgType: TEventType; const ID: TCSSMsgID; const Msg: TCSSString; PosEl: TCSSElement); virtual;
- class function IsWhiteSpace(const s: TCSSString): boolean; virtual; overload;
- property Resolver: TCSSBaseResolver read FResolver write FResolver;
- property OnLog: TCSSValueParserLogEvent read FOnLog write FOnLog;
- end;
- implementation
- Const
- Alpha = ['A'..'Z','a'..'z'];
- Num = ['0'..'9'];
- AlNum = Alpha+Num;
- AlIden = Alpha+['-'];
- AlNumIden = AlNum+['-'];
- Whitespace = [#9,#10,#13,' '];
- //WhitespaceZ = Whitespace+[#0];
- Hex = ['0'..'9','a'..'z','A'..'Z'];
- ValEnd = [#0,#10,#13,#9,' ',';',',','}',')',']']; // used for skipping a component value
- { TCSSRegistry }
- procedure TCSSRegistry.SetModified(const AValue: boolean);
- begin
- if AValue then
- ChangeStamp
- else
- FModifiedStamp:=FStamp;
- end;
- function TCSSRegistry.GetModified: boolean;
- begin
- Result:=FStamp=FModifiedStamp;
- end;
- constructor TCSSRegistry.Create;
- var
- Kind: TCSSNumericalIDKind;
- i: Integer;
- begin
- for Kind in TCSSNumericalIDKind do
- FHashLists[Kind]:=TFPHashList.Create;
- if Attribute_ClassOf=nil then
- Attribute_ClassOf:=TCSSAttributeDesc;
- if PseudoClass_ClassOf=nil then
- PseudoClass_ClassOf:=TCSSPseudoClassDesc;
- if Type_ClassOf=nil then
- Type_ClassOf:=TCSSTypeDesc;
- // init attributes
- SetLength(Attributes,32);
- for i:=0 to length(Attributes)-1 do Attributes[i]:=nil;
- FAttributeCount:=1; // index 0 is CSSIDNone
- // init pseudo classes
- SetLength(PseudoClasses,32);
- for i:=0 to length(PseudoClasses)-1 do PseudoClasses[i]:=nil;
- FPseudoClassCount:=1; // index 0 is CSSIDNone
- // init pseudo functions
- SetLength(PseudoFunctions,16);
- FPseudoFunctionCount:=1; // index 0 is CSSIDNone
- // init types
- SetLength(Types,32);
- for i:=0 to length(Types)-1 do Types[i]:=nil;
- FTypeCount:=1; // index 0 is CSSIDNone
- // init keywords
- SetLength(Keywords,32);
- FKeywordCount:=1; // index 0 is CSSIDNone
- // init keywords
- SetLength(AttrFunctions,32);
- FAttrFunctionCount:=1; // index 0 is CSSIDNone
- end;
- procedure TCSSRegistry.Init;
- begin
- // init attributes
- if AddAttribute('id').Index<>CSSAttributeID_ID then
- raise Exception.Create('20240617191749');
- if AddAttribute('class').Index<>CSSAttributeID_Class then
- raise Exception.Create('20240617191801');
- if AddAttribute('all').Index<>CSSAttributeID_All then
- raise Exception.Create('20240617191816');
- // init pseudo classes
- if AddPseudoClass('root').Index<>CSSPseudoID_Root then
- raise Exception.Create('20240623165848');
- if AddPseudoClass('empty').Index<>CSSPseudoID_Empty then
- raise Exception.Create('20240623170450');
- if AddPseudoClass('first-child').Index<>CSSPseudoID_FirstChild then
- raise Exception.Create('20240623170508');
- if AddPseudoClass('last-child').Index<>CSSPseudoID_LastChild then
- raise Exception.Create('20240623170521');
- if AddPseudoClass('only-child').Index<>CSSPseudoID_OnlyChild then
- raise Exception.Create('20240623170534');
- if AddPseudoClass('first-of-type').Index<>CSSPseudoID_FirstOfType then
- raise Exception.Create('20240623170547');
- if AddPseudoClass('last-of-type').Index<>CSSPseudoID_LastOfType then
- raise Exception.Create('20240623170558');
- if AddPseudoClass('only-of-type').Index<>CSSPseudoID_OnlyOfType then
- raise Exception.Create('20240623170609');
- // init pseudo functions
- if AddPseudoFunction('not')<>CSSCallID_Not then
- raise Exception.Create('20240625183757');
- if AddPseudoFunction('is')<>CSSCallID_Is then
- raise Exception.Create('20240625142038');
- if AddPseudoFunction('where')<>CSSCallID_Where then
- raise Exception.Create('20240625142049');
- if AddPseudoFunction('has')<>CSSCallID_Has then
- raise Exception.Create('20240625142104');
- if AddPseudoFunction('nth-child')<>CSSCallID_NthChild then
- raise Exception.Create('20240625142124');
- if AddPseudoFunction('nth-last-child')<>CSSCallID_NthLastChild then
- raise Exception.Create('20240625142136');
- if AddPseudoFunction('nth-of-type')<>CSSCallID_NthOfType then
- raise Exception.Create('20240625142156');
- if AddPseudoFunction('nth-last-of-type')<>CSSCallID_NthLastOfType then
- raise Exception.Create('20240625142212');
- // init types
- if AddType('*').Index<>CSSTypeID_Universal then
- raise Exception.Create('20240617190914');
- // init keywords
- if AddKeyword('none')<>CSSKeywordNone then
- raise Exception.Create('20240623184021');
- if AddKeyword('initial')<>CSSKeywordInitial then
- raise Exception.Create('20240623184030');
- if AddKeyword('inherit')<>CSSKeywordInherit then
- raise Exception.Create('20240623184042');
- if AddKeyword('unset')<>CSSKeywordUnset then
- raise Exception.Create('20240623184053');
- if AddKeyword('revert')<>CSSKeywordRevert then
- raise Exception.Create('20240623184104');
- if AddKeyword('revert-layer')<>CSSKeywordRevertLayer then
- raise Exception.Create('20240623184114');
- if AddKeyword('auto')<>CSSKeywordAuto then
- raise Exception.Create('20240625182731');
- // init attribute functions
- if AddAttrFunction('var')<>CSSAttrFuncVar then
- raise Exception.Create('20240716124054');
- end;
- destructor TCSSRegistry.Destroy;
- var
- i: Integer;
- Kind: TCSSNumericalIDKind;
- begin
- for Kind in TCSSNumericalIDKind do
- FreeAndNil(FHashLists[Kind]);
- // attributes
- NotAllAttributes:=nil;
- for i:=1 to AttributeCount-1 do
- FreeAndNil(Attributes[i]);
- Attributes:=nil;
- FAttributeCount:=0;
- // pseudo classes
- for i:=1 to PseudoClassCount-1 do
- FreeAndNil(PseudoClasses[i]);
- PseudoClasses:=nil;
- FPseudoClassCount:=0;
- // types
- for i:=1 to TypeCount-1 do
- FreeAndNil(Types[i]);
- Types:=nil;
- FTypeCount:=0;
- // keywords
- FKeywordCount:=0;
- inherited Destroy;
- end;
- function TCSSRegistry.FindNamedItem(Kind: TCSSNumericalIDKind;
- const aName: TCSSString): TCSSRegistryNamedItem;
- begin
- if Kind in nikAllDescriptors then
- Result:=TCSSRegistryNamedItem(FHashLists[Kind].Find(aName))
- else
- raise Exception.Create('20240625141820');
- end;
- function TCSSRegistry.IndexOfNamedItem(Kind: TCSSNumericalIDKind;
- const aName: TCSSString): TCSSNumericalID;
- var
- Item: TCSSRegistryNamedItem;
- p: Pointer;
- begin
- if Kind in nikAllDescriptors then
- begin
- Item:=TCSSRegistryNamedItem(FHashLists[Kind].Find(aName));
- if Item<>nil then
- Result:=Item.Index
- else
- Result:=-1;
- end else begin
- p:=FHashLists[Kind].Find(aName);
- if p=nil then
- exit(CSSIDNone)
- else
- Result:={%H-}TCSSNumericalID(p);
- end;
- end;
- procedure TCSSRegistry.ConsistencyCheck;
- var
- ID, ID2: TCSSNumericalID;
- AttrDesc, SubAttrDesc: TCSSAttributeDesc;
- PseudoClassDesc: TCSSPseudoClassDesc;
- TypeDesc: TCSSTypeDesc;
- i: Integer;
- aName: TCSSString;
- begin
- if AttributeCount>length(Attributes) then
- raise Exception.Create('20240629102438');
- for ID:=1 to AttributeCount-1 do
- begin
- AttrDesc:=Attributes[ID];
- if AttrDesc=nil then
- raise Exception.Create('20240629102530 attr ID='+IntToStr(ID)+' Desc=nil');
- aName:=AttrDesc.Name;
- if aName='' then
- raise Exception.Create('20240629100056 attr ID='+IntToStr(ID)+' missing name');
- if length(aName)>255 then
- raise Exception.Create('20240629100143 attr ID='+IntToStr(ID)+' name too long "'+aName+'"');
- if aName[1]=':' then
- raise Exception.Create('20240701231211 attr ID='+IntToStr(ID)+' invalid name "'+aName+'"');
- if AttrDesc.Index<>ID then
- raise Exception.Create('20240629095849 attr ID='+IntToStr(ID)+' Desc.Index='+IntToStr(AttrDesc.Index)+' "'+aName+'"');
- ID2:=IndexOfAttributeName(aName);
- if ID2<>ID then
- raise Exception.Create('20240629101227 attr ID='+IntToStr(ID)+' "'+aName+'" IndexOf failed: '+IntToStr(ID2));
- if length(AttrDesc.CompProps)>0 then
- begin
- // check shorthand
- for i:=0 to length(AttrDesc.CompProps)-1 do
- begin
- SubAttrDesc:=AttrDesc.CompProps[i];
- if SubAttrDesc=nil then
- raise Exception.Create('20240629102701 attr ID='+IntToStr(ID)+' Shorthand="'+aName+'" CompDesc=nil '+IntToStr(i));
- if (SubAttrDesc.Index<=0) then
- raise Exception.Create('20240629100345 attr ID='+IntToStr(ID)+' Shorthand="'+aName+'" invalid CompAttr '+IntToStr(SubAttrDesc.Index));
- if (SubAttrDesc.Index>=ID) then
- raise Exception.Create('20240629100345 attr ID='+IntToStr(ID)+' Shorthand="'+aName+'" CompAttr after Shorthand: SubID='+IntToStr(SubAttrDesc.Index)+' SubName='+SubAttrDesc.Name);
- end;
- end;
- end;
- if PseudoClassCount>length(PseudoClasses) then
- raise Exception.Create('20240629102438');
- for ID:=1 to PseudoClassCount-1 do
- begin
- PseudoClassDesc:=PseudoClasses[ID];
- if PseudoClassDesc=nil then
- raise Exception.Create('20240629102605 pseudo class ID='+IntToStr(ID)+' Desc=nil');
- aName:=PseudoClassDesc.Name;
- if aName='' then
- raise Exception.Create('20240629100652 pseudo class ID='+IntToStr(ID)+' missing name');
- if length(aName)>255 then
- raise Exception.Create('20240629100657 pseudo class ID='+IntToStr(ID)+' name too long "'+aName+'"');
- if aName[1]=':' then
- raise Exception.Create('20240701231235 pseudo class ID='+IntToStr(ID)+' invalid name "'+aName+'"');
- if PseudoClassDesc.Index<>ID then
- raise Exception.Create('20240629100659 pseudo class ID='+IntToStr(ID)+' Desc.Index='+IntToStr(PseudoClassDesc.Index)+' "'+aName+'"');
- ID2:=IndexOfPseudoClassName(PseudoClassDesc.Name);
- if ID2<>ID then
- raise Exception.Create('20240629101227 pseudo class ID='+IntToStr(ID)+' "'+aName+'" IndexOf failed: '+IntToStr(ID2));
- end;
- if PseudoFunctionCount>length(PseudoFunctions) then
- raise Exception.Create('20240629103430');
- for ID:=1 to PseudoFunctionCount-1 do
- begin
- aName:=PseudoFunctions[ID];
- if aName='' then
- raise Exception.Create('20240629103431 pseudo function ID='+IntToStr(ID)+' missing name');
- if length(aName)>255 then
- raise Exception.Create('20240629103433 pseudo function ID='+IntToStr(ID)+' name too long "'+aName+'"');
- if aName[1]=':' then
- raise Exception.Create('20240701231235 pseudo function ID='+IntToStr(ID)+' invalid name "'+aName+'"');
- ID2:=IndexOfPseudoFunction(aName);
- if ID2<>ID then
- raise Exception.Create('20240629103434 pseudo function ID='+IntToStr(ID)+' "'+aName+'" IndexOf failed: '+IntToStr(ID2));
- end;
- if TypeCount>length(Types) then
- raise Exception.Create('20240629102438');
- for ID:=1 to TypeCount-1 do
- begin
- TypeDesc:=Types[ID];
- if TypeDesc=nil then
- raise Exception.Create('20240629102620 type ID='+IntToStr(ID)+' Desc=nil');
- aName:=TypeDesc.Name;
- if aName='' then
- raise Exception.Create('20240629100812 type ID='+IntToStr(ID)+' missing name');
- if length(aName)>255 then
- raise Exception.Create('20240629100825 type ID='+IntToStr(ID)+' name too long "'+aName+'"');
- if aName[1]=':' then
- raise Exception.Create('20240701231645 type ID='+IntToStr(ID)+' invalid name "'+aName+'"');
- if TypeDesc.Index<>ID then
- raise Exception.Create('20240629101013 type ID='+IntToStr(ID)+' Desc.Index='+IntToStr(TypeDesc.Index)+' "'+aName+'"');
- ID2:=IndexOfTypeName(aName);
- if ID2<>ID then
- raise Exception.Create('20240629101529 type ID='+IntToStr(ID)+' "'+aName+'" IndexOf failed: '+IntToStr(ID2));
- end;
- if KeywordCount>length(Keywords) then
- raise Exception.Create('20240629103200');
- for ID:=1 to KeywordCount-1 do
- begin
- aName:=Keywords[ID];
- if aName='' then
- raise Exception.Create('20240629103223 keyword ID='+IntToStr(ID)+' missing name');
- if length(aName)>255 then
- raise Exception.Create('20240629103242 keyword ID='+IntToStr(ID)+' name too long "'+aName+'"');
- if aName[1]=':' then
- raise Exception.Create('20240701231656 keyword ID='+IntToStr(ID)+' invalid name "'+aName+'"');
- ID2:=IndexOfKeyword(aName);
- if ID2<>ID then
- raise Exception.Create('20240629103303 keyword ID='+IntToStr(ID)+' "'+aName+'" IndexOf failed: '+IntToStr(ID2));
- end;
- end;
- procedure TCSSRegistry.ChangeStamp;
- begin
- if FStamp<high(FStamp) then
- inc(FStamp)
- else
- FStamp:=1;
- end;
- function TCSSRegistry.AddAttribute(Attr: TCSSAttributeDesc
- ): TCSSAttributeDesc;
- begin
- Result:=Attr;
- if Attr.Name='' then
- raise ECSSParser.Create('missing name');
- if FindAttribute(Attr.Name)<>nil then
- raise ECSSParser.Create('duplicate attribute "'+Attr.Name+'"');
- if AttributeCount=length(Attributes) then
- begin
- if AttributeCount<32 then
- SetLength(Attributes,32)
- else
- SetLength(Attributes,2*AttributeCount);
- FillByte(Attributes[AttributeCount],SizeOf(Pointer)*(length(Attributes)-AttributeCount),0);
- end;
- Attributes[AttributeCount]:=Attr;
- Attr.Index:=AttributeCount;
- FHashLists[nikAttribute].Add(Attr.Name,Attr);
- inc(FAttributeCount);
- ChangeStamp;
- end;
- function TCSSRegistry.AddAttribute(const aName: TCSSString;
- const aInitialValue: TCSSString; aInherits: boolean; aAll: boolean;
- aClass: TCSSAttributeDescClass): TCSSAttributeDesc;
- begin
- if aName='' then
- raise ECSSParser.Create('missing name');
- if FindAttribute(aName)<>nil then
- raise ECSSParser.Create('duplicate attribute "'+aName+'"');
- if aClass=nil then
- aClass:=Attribute_ClassOf;
- Result:=aClass.Create;
- Result.Name:=aName;
- Result.InitialValue:=aInitialValue;
- Result.Inherits:=aInherits;
- Result.All:=aAll;
- AddAttribute(Result);
- if not aAll then
- Insert(Result,NotAllAttributes,length(NotAllAttributes));
- end;
- function TCSSRegistry.FindAttribute(const aName: TCSSString
- ): TCSSAttributeDesc;
- begin
- Result:=TCSSAttributeDesc(FHashLists[nikAttribute].Find(aName));
- end;
- function TCSSRegistry.IndexOfAttributeName(const aName: TCSSString
- ): TCSSNumericalID;
- var
- Attr: TCSSAttributeDesc;
- begin
- Attr:=TCSSAttributeDesc(FHashLists[nikAttribute].Find(aName));
- if Attr<>nil then
- Result:=Attr.Index
- else
- Result:=-1;
- end;
- procedure TCSSRegistry.AddSplitLonghand(var AttrIDs: TCSSNumericalIDArray;
- var Values: TCSSStringArray; AttrID: TCSSNumericalID; const Value: TCSSString);
- begin
- System.Insert(AttrID,AttrIDs,length(AttrIDs));
- System.Insert(Value,Values,length(Values));
- end;
- procedure TCSSRegistry.AddSplitLonghandSides(var AttrIDs: TCSSNumericalIDArray;
- var Values: TCSSStringArray; TopID, RightID, BottomID, LeftID: TCSSNumericalID;
- const Found: TCSSStringArray);
- begin
- if length(Found)=0 then
- exit; // invalid
- Setlength(AttrIDs,4);
- Setlength(Values,4);
- AttrIDs[0]:=TopID;
- AttrIDs[1]:=RightID;
- AttrIDs[2]:=BottomID;
- AttrIDs[3]:=LeftID;
- // sets sides depending on how many values were passed:
- // 1: all four the same
- // 2: top and bottom | left and right
- // 3: top | left and right | bottom
- // 4: top | right | bottom | left
- case length(Found) of
- 1:
- begin
- Values[0]:=Found[0];
- Values[1]:=Found[0];
- Values[2]:=Found[0];
- Values[3]:=Found[0];
- end;
- 2:
- begin
- Values[0]:=Found[0];
- Values[1]:=Found[1];
- Values[2]:=Found[0];
- Values[3]:=Found[1];
- end;
- 3:
- begin
- Values[0]:=Found[0];
- Values[1]:=Found[1];
- Values[2]:=Found[2];
- Values[3]:=Found[1];
- end;
- 4:
- begin
- Values[0]:=Found[0];
- Values[1]:=Found[1];
- Values[2]:=Found[2];
- Values[3]:=Found[3];
- end;
- end;
- end;
- procedure TCSSRegistry.AddSplitLonghandCorners(var AttrIDs: TCSSNumericalIDArray;
- var Values: TCSSStringArray; TopLeftID, TopRightID, BottomLeftID, BottomRightID: TCSSNumericalID;
- const Found: TCSSStringArray);
- begin
- if length(Found)=0 then
- exit; // invalid
- Setlength(AttrIDs,4);
- Setlength(Values,4);
- AttrIDs[0]:=TopLeftID;
- AttrIDs[1]:=TopRightID;
- AttrIDs[2]:=BottomRightID;
- AttrIDs[3]:=BottomLeftID;
- // sets corners depending on how many values were passed:
- // 1: all four the same
- // 2: top-left-and-bottom-right | top-right-and-bottom-left
- // 3: top-left | top-right-and-bottom-left | bottom-right
- // 4: top-left | top-right | bottom-right | bottom-left
- case length(Found) of
- 1:
- begin
- Values[0]:=Found[0];
- Values[1]:=Found[0];
- Values[2]:=Found[0];
- Values[3]:=Found[0];
- end;
- 2:
- begin
- Values[0]:=Found[0];
- Values[1]:=Found[1];
- Values[2]:=Found[0];
- Values[3]:=Found[1];
- end;
- 3:
- begin
- Values[0]:=Found[0];
- Values[1]:=Found[1];
- Values[2]:=Found[2];
- Values[3]:=Found[1];
- end;
- 4:
- begin
- Values[0]:=Found[0];
- Values[1]:=Found[1];
- Values[2]:=Found[2];
- Values[3]:=Found[3];
- end;
- end;
- end;
- function TCSSRegistry.AddPseudoClass(aPseudo: TCSSPseudoClassDesc
- ): TCSSPseudoClassDesc;
- begin
- Result:=aPseudo;
- if aPseudo.Name='' then
- raise ECSSParser.Create('missing name');
- if FindPseudoClass(aPseudo.Name)<>nil then
- raise ECSSParser.Create('duplicate pseudo class "'+aPseudo.Name+'"');
- if PseudoClassCount=length(PseudoClasses) then
- begin
- if PseudoClassCount<32 then
- SetLength(PseudoClasses,32)
- else
- SetLength(PseudoClasses,2*PseudoClassCount);
- FillByte(PseudoClasses[PseudoClassCount],SizeOf(Pointer)*(length(PseudoClasses)-PseudoClassCount),0);
- end;
- PseudoClasses[PseudoClassCount]:=aPseudo;
- aPseudo.Index:=PseudoClassCount;
- FHashLists[nikPseudoClass].Add(aPseudo.Name,aPseudo);
- inc(FPseudoClassCount);
- ChangeStamp;
- end;
- function TCSSRegistry.AddPseudoClass(const aName: TCSSString;
- aClass: TCSSPseudoClassDescClass): TCSSPseudoClassDesc;
- begin
- if aName='' then
- raise ECSSParser.Create('missing name');
- if FindPseudoClass(aName)<>nil then
- raise ECSSParser.Create('duplicate pseudo class "'+aName+'"');
- if aClass=nil then
- aClass:=PseudoClass_ClassOf;
- Result:=aClass.Create;
- Result.Name:=aName;
- AddPseudoClass(Result);
- end;
- function TCSSRegistry.FindPseudoClass(const aName: TCSSString
- ): TCSSPseudoClassDesc;
- begin
- Result:=TCSSPseudoClassDesc(FHashLists[nikPseudoClass].Find(aName));
- end;
- function TCSSRegistry.IndexOfPseudoClassName(const aName: TCSSString
- ): TCSSNumericalID;
- var
- aPseudo: TCSSPseudoClassDesc;
- begin
- aPseudo:=TCSSPseudoClassDesc(FHashLists[nikPseudoClass].Find(aName));
- if aPseudo<>nil then
- Result:=aPseudo.Index
- else
- Result:=-1;
- end;
- function TCSSRegistry.AddPseudoFunction(const aName: TCSSString
- ): TCSSNumericalID;
- begin
- if aName='' then
- raise ECSSParser.Create('missing name');
- if length(aName)>255 then
- raise ECSSParser.Create('pseudo function name too long');
- if aName<>LowerCase(aName) then
- raise ECSSParser.Create('pseudo function name not lowercase');
- Result:=IndexOfKeyword(aName);
- if Result>0 then
- raise ECSSParser.Create('duplicate pseudo function "'+aName+'"');
- if PseudoFunctionCount=length(PseudoFunctions) then
- begin
- if PseudoFunctionCount<32 then
- SetLength(PseudoFunctions,32)
- else
- SetLength(PseudoFunctions,2*PseudoFunctionCount);
- end;
- Result:=PseudoFunctionCount;
- PseudoFunctions[Result]:=aName;
- FHashLists[nikPseudoFunction].Add(aName,{%H-}Pointer(Result));
- inc(FPseudoFunctionCount);
- ChangeStamp;
- end;
- function TCSSRegistry.IndexOfPseudoFunction(const aName: TCSSString
- ): TCSSNumericalID;
- var
- p: Pointer;
- begin
- p:=FHashLists[nikPseudoFunction].Find(aName);
- if p=nil then
- exit(CSSIDNone)
- else
- Result:={%H-}TCSSNumericalID(p);
- end;
- function TCSSRegistry.AddType(aType: TCSSTypeDesc): TCSSTypeDesc;
- begin
- Result:=aType;
- if aType.Name='' then
- raise ECSSParser.Create('missing name');
- if FindType(aType.Name)<>nil then
- raise ECSSParser.Create('duplicate type "'+aType.Name+'"');
- if TypeCount=length(Types) then
- begin
- if TypeCount<32 then
- SetLength(Types,32)
- else
- SetLength(Types,2*TypeCount);
- FillByte(Types[TypeCount],SizeOf(Pointer)*(length(Types)-TypeCount),0);
- end;
- Types[TypeCount]:=aType;
- aType.Index:=TypeCount;
- FHashLists[nikType].Add(aType.Name,aType);
- inc(FTypeCount);
- ChangeStamp;
- end;
- function TCSSRegistry.AddType(const aName: TCSSString; aClass: TCSSTypeDescClass
- ): TCSSTypeDesc;
- begin
- if aName='' then
- raise ECSSParser.Create('missing name');
- if FindType(aName)<>nil then
- raise ECSSParser.Create('duplicate type "'+aName+'"');
- if aClass=nil then
- aClass:=Type_ClassOf;
- Result:=aClass.Create;
- Result.Name:=aName;
- AddType(Result);
- end;
- function TCSSRegistry.FindType(const aName: TCSSString): TCSSTypeDesc;
- begin
- Result:=TCSSTypeDesc(FHashLists[nikType].Find(aName));
- end;
- function TCSSRegistry.IndexOfTypeName(const aName: TCSSString): TCSSNumericalID;
- var
- aType: TCSSTypeDesc;
- begin
- aType:=TCSSTypeDesc(FHashLists[nikType].Find(aName));
- if aType<>nil then
- Result:=aType.Index
- else
- Result:=-1;
- end;
- function TCSSRegistry.AddKeyword(const aName: TCSSString): TCSSNumericalID;
- begin
- if aName='' then
- raise ECSSParser.Create('missing name');
- if length(aName)>255 then
- raise ECSSParser.Create('keyword too long');
- Result:=IndexOfKeyword(aName);
- if Result>0 then
- raise ECSSParser.Create('duplicate keyword "'+aName+'"');
- if KeywordCount=length(Keywords) then
- begin
- if KeywordCount<32 then
- SetLength(Keywords,32)
- else
- SetLength(Keywords,2*KeywordCount);
- end;
- Result:=KeywordCount;
- Keywords[Result]:=aName;
- FHashLists[nikKeyword].Add(aName,{%H-}Pointer(Result));
- inc(FKeywordCount);
- ChangeStamp;
- end;
- procedure TCSSRegistry.AddKeywords(const Names: TCSSStringArray; out First, Last: TCSSNumericalID);
- var
- i, NewCnt: integer;
- begin
- if Names=nil then begin
- First:=CSSIDNone;
- Last:=CSSIDNone;
- exit;
- end;
- for i:=0 to length(Names)-1 do
- if IndexOfKeyword(Names[i])>CSSIDNone then
- raise Exception.Create('20240712215853');
- NewCnt:=KeywordCount+length(Names);
- if NewCnt>length(Keywords) then
- begin
- NewCnt:=(NewCnt div 32 +1) *32;
- SetLength(Keywords,NewCnt);
- end;
- First:=KeywordCount;
- for i:=0 to length(Names)-1 do
- begin
- Last:=KeywordCount;
- Keywords[Last]:=Names[i];
- FHashLists[nikKeyword].Add(Names[i],{%H-}Pointer(Last));
- inc(FKeywordCount);
- end;
- ChangeStamp;
- end;
- function TCSSRegistry.IndexOfKeyword(const aName: TCSSString): TCSSNumericalID;
- var
- p: Pointer;
- begin
- p:=FHashLists[nikKeyword].Find(aName);
- if p=nil then
- exit(CSSIDNone)
- else
- Result:={%H-}TCSSNumericalID(p);
- end;
- procedure TCSSRegistry.AddColorKeywords;
- var
- Names: TCSSStringArray;
- i: Integer;
- begin
- SetLength(Names{%H-},length(CSSNamedColors));
- for i:=0 to High(CSSNamedColors) do
- Names[i]:=CSSNamedColors[i].Name;
- AddKeywords(Names,kwFirstColor,kwLastColor);
- kwTransparent:=IndexOfKeyword('transparent');
- end;
- function TCSSRegistry.GetNamedColor(const aName: TCSSString): TCSSAlphaColor;
- begin
- Result:=GetKeywordColor(IndexOfKeyword(aName));
- end;
- function TCSSRegistry.GetKeywordColor(KeywordID: TCSSNumericalID): TCSSAlphaColor;
- begin
- if (KeywordID<kwFirstColor) or (KeywordID>kwLastColor) then
- Result:=$ff000000
- else
- Result:=CSSNamedColors[KeywordID-kwFirstColor].Color;
- end;
- function TCSSRegistry.AddAttrFunction(const aName: TCSSString): TCSSNumericalID;
- begin
- if aName='' then
- raise ECSSParser.Create('missing name');
- if length(aName)>255 then
- raise ECSSParser.Create('function name too long');
- Result:=IndexOfAttrFunction(aName);
- if Result>0 then
- raise ECSSParser.Create('duplicate attribute function "'+aName+'"');
- if AttrFunctionCount=length(AttrFunctions) then
- begin
- if AttrFunctionCount<32 then
- SetLength(AttrFunctions,32)
- else
- SetLength(AttrFunctions,2*AttrFunctionCount);
- end;
- Result:=AttrFunctionCount;
- AttrFunctions[Result]:=aName;
- FHashLists[nikAttrFunction].Add(aName,{%H-}Pointer(Result));
- inc(FAttrFunctionCount);
- ChangeStamp;
- end;
- function TCSSRegistry.IndexOfAttrFunction(const aName: TCSSString
- ): TCSSNumericalID;
- var
- p: Pointer;
- begin
- p:=FHashLists[nikAttrFunction].Find(aName);
- if p=nil then
- exit(CSSIDNone)
- else
- Result:={%H-}TCSSNumericalID(p);
- end;
- { TCSSResolvedCallElement }
- destructor TCSSResolvedCallElement.Destroy;
- begin
- FreeAndNil(Params);
- inherited Destroy;
- end;
- { TCSSResCompValue }
- function TCSSResCompValue.AsString: TCSSString;
- var
- l: integer;
- begin
- if (StartP=nil) or (EndP=nil) or (EndP<=StartP) then
- exit('');
- l:=EndP-StartP;
- SetLength(Result,l);
- Move(StartP^,Result[1],l);
- end;
- function TCSSResCompValue.FloatAsString: TCSSString;
- begin
- Result:=FloatToCSSStr(Float)+CSSUnitNames[FloatUnit];
- end;
- { TCSSCheckAttrParams_Dimension }
- function TCSSCheckAttrParams_Dimension.Fits(const ResValue: TCSSResCompValue): boolean;
- var
- i: Integer;
- begin
- Result:=false;
- case ResValue.Kind of
- rvkFloat:
- if ResValue.FloatUnit in AllowedUnits then
- begin
- if not (ResValue.FloatUnit in AllowedUnits) then exit;
- if (not AllowNegative) and (ResValue.Float<0) then exit;
- if (not AllowFrac) and (Frac(ResValue.Float)>0) then exit;
- exit(true);
- end else if (ResValue.FloatUnit=cuNone) and (ResValue.Float=0) then
- exit(true);
- rvkKeyword:
- for i:=0 to length(AllowedKeywordIDs)-1 do
- if ResValue.KeywordID=AllowedKeywordIDs[i] then
- exit(true);
- end;
- end;
- { TCSSBaseResolver }
- procedure TCSSBaseResolver.SetCSSRegistry(const AValue: TCSSRegistry);
- begin
- if FCSSRegistry=AValue then Exit;
- FCSSRegistry:=AValue;
- end;
- function TCSSBaseResolver.InitParseAttr(Desc: TCSSAttributeDesc; AttrData: TCSSAttributeKeyData;
- const Value: TCSSString): boolean;
- var
- p: PCSSChar;
- begin
- Result:=false;
- CurAttrData:=AttrData;
- CurDesc:=Desc;
- CurValue:=Value;
- CurComp:=Default(TCSSResCompValue);
- CurComp.EndP:=PCSSChar(CurValue);
- if not ReadNext then
- begin
- if CurAttrData<>nil then
- CurAttrData.Invalid:=true;
- exit;
- end;
- if (CurAttrData<>nil) and (CurComp.Kind=rvkKeyword)
- and IsBaseKeyword(CurComp.KeywordID) then
- begin
- p:=CurComp.EndP;
- while (p^ in Whitespace) do inc(p);
- if p^>#0 then
- begin
- // "inherit" must be alone
- CurAttrData.Invalid:=true;
- exit;
- end;
- CurAttrData.Complete:=true;
- end;
- Result:=true;
- end;
- procedure TCSSBaseResolver.InitParseAttr(const Value: TCSSString);
- begin
- CurValue:=Value;
- CurComp:=Default(TCSSResCompValue);
- CurComp.EndP:=PCSSChar(CurValue);
- ReadNext;
- end;
- function TCSSBaseResolver.CheckAttribute_Keyword(const AllowedKeywordIDs: TCSSNumericalIDArray
- ): boolean;
- begin
- Result:=ReadAttribute_Keyword(CurAttrData.Invalid,AllowedKeywordIDs);
- end;
- function TCSSBaseResolver.CheckAttribute_CommaList_Keyword(
- const AllowedKeywordIDs: TCSSNumericalIDArray): boolean;
- var
- i: Integer;
- Fits: Boolean;
- begin
- CurAttrData.Invalid:=true;
- repeat
- Fits:=false;
- case CurComp.Kind of
- rvkKeyword:
- for i:=0 to length(AllowedKeywordIDs)-1 do
- if CurComp.KeywordID=AllowedKeywordIDs[i] then
- begin
- Fits:=true;
- break;
- end;
- rvkFunction:
- begin
- // todo: check for allowed functions
- Fits:=true;
- end;
- end;
- if not Fits then exit;
- if not ReadNext then
- begin
- // ok
- CurAttrData.Invalid:=false;
- exit(true);
- end;
- if (CurComp.Kind<>rvkSymbol) or (CurComp.Symbol=ctkCOMMA) then
- exit;
- until not ReadNext;
- Result:=false;
- end;
- function TCSSBaseResolver.CheckAttribute_Dimension(const Params: TCSSCheckAttrParams_Dimension
- ): boolean;
- begin
- Result:=ReadAttribute_Dimension(CurAttrData.Invalid,Params);
- end;
- function TCSSBaseResolver.CheckAttribute_Color(const AllowedKeywordIDs: TCSSNumericalIDArray
- ): boolean;
- begin
- Result:=ReadAttribute_Color(CurAttrData.Invalid,AllowedKeywordIDs);
- end;
- function TCSSBaseResolver.ReadNext: boolean;
- begin
- Result:=ReadComp(CurComp);
- end;
- function TCSSBaseResolver.ReadAttribute_Keyword(out Invalid: boolean;
- const AllowedKeywordIDs: TCSSNumericalIDArray): boolean;
- var
- i: Integer;
- begin
- Invalid:=false;
- repeat
- case CurComp.Kind of
- rvkKeyword:
- for i:=0 to length(AllowedKeywordIDs)-1 do
- if CurComp.KeywordID=AllowedKeywordIDs[i] then
- exit(true);
- end;
- // todo: warn if invalid
- until not ReadNext;
- Invalid:=true;
- Result:=false;
- end;
- function TCSSBaseResolver.ReadAttribute_Dimension(out Invalid: boolean;
- const Params: TCSSCheckAttrParams_Dimension): boolean;
- var
- i: Integer;
- begin
- Invalid:=true;
- repeat
- case CurComp.Kind of
- rvkFloat:
- if Params.Fits(CurComp) then
- exit(true);
- rvkKeyword:
- for i:=0 to length(Params.AllowedKeywordIDs)-1 do
- if CurComp.KeywordID=Params.AllowedKeywordIDs[i] then
- exit(true);
- end;
- // todo: warn if invalid
- until not ReadNext;
- Invalid:=true;
- Result:=false;
- end;
- function TCSSBaseResolver.ReadAttribute_Color(out Invalid: boolean;
- const AllowedKeywordIDs: TCSSNumericalIDArray): boolean;
- var
- i: Integer;
- begin
- Invalid:=false;
- repeat
- case CurComp.Kind of
- rvkKeyword:
- begin
- if (CurComp.KeywordID>=CSSRegistry.kwFirstColor)
- and (CurComp.KeywordID<=CSSRegistry.kwLastColor)
- then
- exit(true);
- for i:=0 to length(AllowedKeywordIDs)-1 do
- if CurComp.KeywordID=AllowedKeywordIDs[i] then
- exit(true);
- end;
- rvkFunction:
- begin
- // todo: check for allowed functions
- end;
- rvkHexColor:
- exit(true);
- end;
- // todo: warn if invalid
- until not ReadNext;
- Invalid:=true;
- Result:=false;
- end;
- function TCSSBaseResolver.ReadComp(var aComp: TCSSResCompValue): boolean;
- begin
- Result:=ReadValue(aComp);
- ReadWordID(aComp);
- end;
- procedure TCSSBaseResolver.ReadWordID(var aComp: TCSSResCompValue);
- var
- Identifier: TCSSString;
- begin
- case aComp.Kind of
- rvkFunctionUnknown:
- begin
- SetString(Identifier,aComp.StartP,aComp.EndP-aComp.StartP);
- aComp.FunctionID:=CSSRegistry.IndexOfAttrFunction(Identifier);
- if aComp.FunctionID>CSSIDNone then
- aComp.Kind:=rvkFunction;
- end;
- rvkKeywordUnknown:
- begin
- SetString(Identifier,aComp.StartP,aComp.EndP-aComp.StartP);
- aComp.KeywordID:=CSSRegistry.IndexOfKeyword(Identifier);
- if aComp.KeywordID>CSSIDNone then
- aComp.Kind:=rvkKeyword;
- end;
- end;
- end;
- class function TCSSBaseResolver.ReadValue(var aComp: TCSSResCompValue): boolean;
- var
- c: TCSSChar;
- p: PCSSChar;
- l: SizeInt;
- procedure SetSymbol(s: TCSSToken);
- begin
- aComp.Kind:=rvkSymbol;
- aComp.Symbol:=s;
- aComp.EndP:=p+1;
- end;
- begin
- Result:=true;
- aComp.Kind:=rvkNone;
- p:=aComp.EndP;
- // skip whitespace
- while (p^ in Whitespace) do inc(p);
- aComp.StartP:=p;
- aComp.EndP:=p;
- c:=p^;
- case c of
- #0: exit(false);
- '0'..'9':
- if ReadNumber(aComp) then exit;
- ',':
- begin
- SetSymbol(ctkCOMMA);
- exit;
- end;
- ')':
- begin
- SetSymbol(ctkRPARENTHESIS);
- exit;
- end;
- '+':
- case p[1] of
- '0'..'9','.':
- if ReadNumber(aComp) then exit;
- #0,#9,#10,#13,' ':
- begin
- SetSymbol(ctkPLUS);
- exit;
- end;
- end;
- '-':
- case p[1] of
- '0'..'9','.':
- if ReadNumber(aComp) then exit;
- 'a'..'z','A'..'Z','-':
- if ReadIdentifier(aComp) then exit;
- #0,#9,#10,#13,' ':
- begin
- SetSymbol(ctkMINUS);
- exit;
- end;
- end;
- '.':
- case p[1] of
- '0'..'9':
- if ReadNumber(aComp) then exit;
- else
- SetSymbol(ctkDOT);
- exit;
- end;
- '*':
- begin
- if p[1]='=' then
- begin
- inc(p);
- SetSymbol(ctkSTAREQUAL);
- end else
- SetSymbol(ctkSTAR);
- exit;
- end;
- '/':
- begin
- SetSymbol(ctkDIV);
- exit;
- end;
- ':':
- begin
- SetSymbol(ctkCOLON);
- exit;
- end;
- ';':
- begin
- SetSymbol(ctkSEMICOLON);
- exit;
- end;
- 'a'..'z','A'..'Z':
- if ReadIdentifier(aComp) then exit;
- '#':
- begin
- inc(p);
- while p^ in Hex do inc(p);
- l:=p-aComp.StartP;
- case l of
- 4,5,7,9:
- begin
- // #rgb, #rgba, #rrggbb, #rrggbbaa
- aComp.Kind:=rvkHexColor;
- aComp.EndP:=p;
- exit;
- end;
- end;
- end;
- end;
- // skip unknown aComp
- aComp.Kind:=rvkInvalid;
- repeat
- if p^ in ValEnd then break;
- case p^ of
- '(','[': SkipBrackets(p);
- '''','"': SkipString(p);
- else inc(p);
- end;
- until false;
- aComp.EndP:=p;
- end;
- class function TCSSBaseResolver.ReadNumber(var aComp: TCSSResCompValue): boolean;
- var
- Negative, HasNumber: Boolean;
- Divisor: double;
- Exponent: Integer;
- d: Float;
- U: TCSSUnit;
- StartP, p: PCSSChar;
- begin
- Result:=false;
- aComp.Kind:=rvkInvalid;
- p:=aComp.StartP;
- // number: 1, 0.2, .3, 4.01, 0.0, +0.0, -0.0, .50, 2e3, -6.7E-2
- if p^='-' then
- begin
- Negative:=true;
- inc(p);
- end else begin
- Negative:=false;
- if p^='+' then
- inc(p);
- end;
- HasNumber:=false;
- aComp.Float:=0;
- if p^ in Num then
- begin
- // read significand
- HasNumber:=true;
- repeat
- aComp.Float:=aComp.Float*10+ord(p^)-ord('0');
- if aComp.Float>CSSMaxSafeIntDouble then
- exit; // loosing precision
- inc(p);
- until not (p^ in Num);
- end;
- if p^='.' then
- begin
- // read fraction
- inc(p);
- if not (p^ in Num) then exit;
- Divisor:=1;
- repeat
- Divisor:=Divisor*10;
- aComp.Float:=aComp.Float*10+ord(p^)-ord('0');
- if (Divisor>CSSMaxSafeIntDouble)
- or (aComp.Float>CSSMaxSafeIntDouble) then
- exit; // loosing precision
- inc(p);
- until not (p^ in Num);
- aComp.Float:=aComp.Float/Divisor;
- end else if not HasNumber then
- exit;
- if Negative then
- aComp.Float:=-aComp.Float;
- if (p^ in ['e','E']) and not (p[1] in ['a'..'z']) then
- begin
- inc(p);
- if p^='-' then
- begin
- Negative:=true;
- inc(p);
- end else begin
- Negative:=false;
- if p^='+' then
- inc(p);
- end;
- if not (p^ in Num) then exit;
- Exponent:=0;
- repeat
- inc(p);
- Exponent:=Exponent*10+ord(p^)-ord('0');
- if Exponent>2047 then
- exit; // out of bounds
- until not (p^ in Num);
- if Exponent>0 then
- begin
- if Negative then
- Exponent:=-Exponent;
- try
- d:=Power(10,Exponent);
- aComp.Float:=aComp.Float*d;
- except
- exit;
- end;
- end;
- end;
- aComp.Kind:=rvkFloat;
- // parse unit
- U:=cuNone;
- case p^ of
- '%':
- begin
- inc(p);
- U:=cuPercent;
- end;
- 'a'..'z','A'..'Z':
- begin
- StartP:=p;
- while p^ in Alpha do inc(p);
- U:=high(TCSSUnit);
- while (U>cuNone) and not CompareMem(StartP,PChar(CSSUnitNames[U]),length(CSSUnitNames[U])) do
- U:=pred(U);
- if U=cuNone then
- exit; // unknown unit
- end;
- end;
- aComp.FloatUnit:=U;
- aComp.EndP:=p;
- Result:=true;
- //writeln('TCSSBaseResolver.ReadNumber "',p,'" Value=',FloatToCSSStr(aComp.Float),' U=',U,' Kind=',aComp.Kind,' Result=',Result);
- end;
- class function TCSSBaseResolver.ReadIdentifier(var aComp: TCSSResCompValue): boolean;
- var
- IsFunc: Boolean;
- p: PCSSChar;
- begin
- Result:=false;
- aComp.Kind:=rvkInvalid;
- p:=aComp.EndP;
- if not (p^ in AlIden) then exit;
- repeat
- inc(p);
- until not (p^ in AlNumIden);
- IsFunc:=p^='(';
- if IsFunc then
- begin
- // function call
- aComp.Kind:=rvkFunctionUnknown;
- aComp.BracketOpen:=p;
- if not SkipBrackets(p) then
- begin
- aComp.EndP:=p;
- exit;
- end;
- end else
- aComp.Kind:=rvkKeywordUnknown;
- aComp.EndP:=p;
- Result:=true;
- end;
- function TCSSBaseResolver.IsBaseKeyword(KeywordID: TCSSNumericalID): boolean;
- begin
- Result:=(KeywordID>=CSSKeywordInitial) and (KeywordID<=CSSKeywordRevertLayer);
- end;
- function TCSSBaseResolver.IsKeywordIn(aKeywordID: TCSSNumericalID;
- const KeywordIDs: TCSSNumericalIDArray): boolean;
- var
- i: Integer;
- begin
- for i:=0 to length(KeywordIDs)-1 do
- if KeywordIDs[i]=aKeywordID then
- exit(true);
- Result:=false;
- end;
- function TCSSBaseResolver.IsKeywordIn(const KeywordIDs: TCSSNumericalIDArray): boolean;
- var
- aKeywordID: TCSSNumericalID;
- i: Integer;
- begin
- Result:=false;
- if CurComp.Kind<>rvkKeyword then exit;
- aKeywordID:=CurComp.KeywordID;
- for i:=0 to length(KeywordIDs)-1 do
- if KeywordIDs[i]=aKeywordID then
- exit(true);
- end;
- function TCSSBaseResolver.IsLengthOrPercentage(AllowNegative: boolean): boolean;
- begin
- Result:=false;
- case CurComp.Kind of
- rvkFloat:
- if CurComp.FloatUnit in cuAllLengthsAndPercent then
- begin
- if (not AllowNegative) and (CurComp.Float<0) then exit;
- exit(true);
- end
- else if (CurComp.FloatUnit=cuNone) and (CurComp.Float=0) then
- exit(true); // 0 without unit is allowed
- end;
- end;
- function TCSSBaseResolver.IsLengthOrPercentage(const ResValue: TCSSResCompValue;
- AllowNegative: boolean): boolean;
- begin
- Result:=false;
- case ResValue.Kind of
- rvkFloat:
- if ResValue.FloatUnit in cuAllLengthsAndPercent then
- begin
- if (not AllowNegative) and (ResValue.Float<0) then exit;
- exit(true);
- end
- else if (ResValue.FloatUnit=cuNone) and (ResValue.Float=0) then
- exit(true);
- end;
- end;
- function TCSSBaseResolver.IsSymbol(Token: TCSSToken): boolean;
- begin
- Result:=(CurComp.Kind=rvkSymbol) and (CurComp.Symbol=Token);
- end;
- function TCSSBaseResolver.GetCompString: TCSSString;
- var
- StartP: PCSSChar;
- begin
- if CurComp.Kind=rvkKeyword then
- exit(CSSRegistry.Keywords[CurComp.KeywordID]);
- StartP:=CurComp.StartP;
- if (StartP=PCSSChar(CurValue)) and (CurComp.EndP-StartP = length(CurValue)) then
- Result:=CurValue
- else
- SetString(Result,StartP,CurComp.EndP-StartP);
- end;
- function TCSSBaseResolver.GetCompString(const aValue: string; const ResValue: TCSSResCompValue
- ): TCSSString;
- var
- Start: PCSSChar;
- begin
- if ResValue.Kind=rvkKeyword then
- exit(CSSRegistry.Keywords[ResValue.KeywordID]);
- Start:=ResValue.StartP;
- if (Start=PCSSChar(aValue)) and (ResValue.EndP-Start = length(aValue)) then
- Result:=aValue
- else
- SetString(Result,Start,ResValue.EndP-Start);
- end;
- class procedure TCSSBaseResolver.SkipToEndOfAttribute(var p: PCSSChar);
- begin
- repeat
- case p^ of
- #0,'{','}',';': exit;
- '''','"': SkipString(p);
- else inc(p);
- end;
- until false;
- end;
- class function TCSSBaseResolver.SkipString(var p: PCSSChar): boolean;
- var
- Delim, c: TCSSChar;
- begin
- Result:=false;
- Delim:=p^;
- repeat
- inc(p);
- c:=p^;
- if c=Delim then
- begin
- inc(p);
- exit(true);
- end else if c=#0 then
- exit
- else
- inc(p);
- until false;
- end;
- class function TCSSBaseResolver.SkipBrackets(var p: PCSSChar; Lvl: integer): boolean;
- const
- CSSMaxBracketLvl = 10;
- var
- CloseBracket: TCSSChar;
- begin
- Result:=false;
- if Lvl>CSSMaxBracketLvl then
- begin
- SkipToEndOfAttribute(p);
- exit;
- end;
- if p^='[' then
- CloseBracket:=']'
- else
- CloseBracket:=')';
- repeat
- inc(p);
- case p^ of
- #0,'{','}',';': exit;
- '''','"': SkipString(p);
- '(','[': SkipBrackets(p,Lvl+1);
- ')',']':
- if p^=CloseBracket then
- begin
- inc(p);
- exit(true);
- end else begin
- SkipToEndOfAttribute(p);
- exit;
- end;
- end;
- until false;
- end;
- function TCSSBaseResolver.GetAttributeID(const aName: TCSSString; AutoCreate: boolean
- ): TCSSNumericalID;
- begin
- Result:=CSSRegistry.IndexOfAttributeName(aName);
- if AutoCreate then ;
- end;
- function TCSSBaseResolver.GetAttributeDesc(AttrID: TCSSNumericalID): TCSSAttributeDesc;
- begin
- if (AttrID>0) and (AttrID<CSSRegistry.AttributeCount) then
- Result:=CSSRegistry.Attributes[AttrID]
- else
- Result:=nil;
- end;
- function TCSSBaseResolver.GetTypeID(const aName: TCSSString): TCSSNumericalID;
- begin
- Result:=CSSRegistry.IndexOfTypeName(aName);
- end;
- function TCSSBaseResolver.GetPseudoClassID(const aName: TCSSString): TCSSNumericalID;
- begin
- Result:=CSSRegistry.IndexOfPseudoClassName(aName);
- end;
- function TCSSBaseResolver.GetPseudoFunctionID(const aName: TCSSString): TCSSNumericalID;
- begin
- Result:=CSSRegistry.IndexOfPseudoFunction(aName);
- end;
- { TCSSResolverParser }
- function TCSSResolverParser.ResolveAttribute(El: TCSSResolvedIdentifierElement): TCSSNumericalID;
- var
- aName: TCSSString;
- begin
- if El.NumericalID<>CSSIDNone then
- raise Exception.Create('20240701143234');
- aName:=El.Name;
- El.Kind:=nikAttribute;
- Result:=Resolver.GetAttributeID(aName,true);
- if Result<=CSSIDNone then
- begin
- El.NumericalID:=-1;
- Log(etWarning,20240822172823,'unknown attribute "'+aName+'"',El);
- end else
- El.NumericalID:=Result;
- end;
- function TCSSResolverParser.ResolveType(El: TCSSResolvedIdentifierElement): TCSSNumericalID;
- var
- aName: TCSSString;
- begin
- if El.NumericalID<>CSSIDNone then
- raise Exception.Create('20240822133813');
- aName:=El.Name;
- El.Kind:=nikType;
- Result:=Resolver.GetTypeID(aName);
- if Result<=CSSIDNone then
- begin
- El.NumericalID:=-1;
- Log(etWarning,20240822133816,'unknown type "'+aName+'"',El);
- end else
- El.NumericalID:=Result;
- end;
- function TCSSResolverParser.ResolvePseudoClass(
- El: TCSSResolvedPseudoClassElement): TCSSNumericalID;
- var
- aName: TCSSString;
- begin
- aName:=El.Name;
- // pseudo classes are ASCII case insensitive
- System.Delete(aName,1,1);
- aName:=lowercase(aName);
- if El.NumericalID<>CSSIDNone then
- raise Exception.Create('20240701143234');
- El.Kind:=nikPseudoClass;
- Result:=Resolver.GetPseudoClassID(aName);
- //writeln('TCSSResolverParser.ResolvePseudoClass ',aName,' ID=',Result);
- if Result<=CSSIDNone then
- begin
- El.NumericalID:=-1;
- Log(etWarning,20240822172826,'unknown pseudo class "'+aName+'"',El);
- end else
- El.NumericalID:=Result;
- end;
- function TCSSResolverParser.ResolvePseudoFunction(El: TCSSResolvedCallElement
- ): TCSSNumericalID;
- var
- aName: TCSSString;
- begin
- if El.NameNumericalID<>CSSIDNone then
- raise Exception.Create('20240701143035');
- aName:=El.Name;
- if aName[1]<>':' then
- raise Exception.Create('20240701143650');
- // pseudo functions are ASCII case insensitive
- System.Delete(aName,1,1);
- aName:=lowercase(aName);
- El.Kind:=nikPseudoFunction;
- Result:=Resolver.GetPseudoFunctionID(aName);
- //writeln('TCSSResolverParser.ResolvePseudoFunction ',aName,' ID=',Result);
- if Result<=CSSIDNone then
- begin
- El.NameNumericalID:=-1;
- Log(etWarning,20240822172830,'unknown pseudo function "'+aName+'"',El);
- end else
- El.NameNumericalID:=Result;
- end;
- function TCSSResolverParser.ParseCall(aName: TCSSString; IsSelector: boolean
- ): TCSSCallElement;
- var
- CallID: TCSSNumericalID;
- begin
- Result:=inherited ParseCall(aName, IsSelector);
- if IsSelector then
- begin
- if Result.Name[1]=':' then
- begin
- CallID:=ResolvePseudoFunction(TCSSResolvedCallElement(Result));
- case CallID of
- CSSCallID_NthChild,CSSCallID_NthLastChild,
- CSSCallID_NthOfType,CSSCallID_NthLastOfType: CheckNthChildParams(TCSSResolvedCallElement(Result));
- end;
- end
- else
- Log(etWarning,20240701222744,'invalid selector function',Result);
- end;
- end;
- function TCSSResolverParser.ParseDeclaration(aIsAt: Boolean
- ): TCSSDeclarationElement;
- var
- aKey: TCSSElement;
- AttrId: TCSSNumericalID;
- Desc: TCSSAttributeDesc;
- AttrData: TCSSAttributeKeyData;
- i, ChildCnt: Integer;
- begin
- Result:=inherited ParseDeclaration(aIsAt);
- if Result.KeyCount<>1 then
- begin
- if Result.KeyCount<1 then
- Log(etWarning,20231112135955,'missing keys in declaration',Result);
- if Result.KeyCount>1 then
- Log(etWarning,20231112140722,'too many keys in declaration',Result);
- exit;
- end;
- aKey:=Result.Keys[0];
- if aKey is TCSSResolvedIdentifierElement then
- begin
- AttrId:=ResolveAttribute(TCSSResolvedIdentifierElement(aKey));
- if aKey.CustomData<>nil then
- raise Exception.Create('20240626113536');
- AttrData:=CSSAttributeKeyDataClass.Create;
- aKey.CustomData:=AttrData;
- ChildCnt:=Result.ChildCount;
- if ChildCnt=0 then
- begin
- AttrData.Invalid:=true;
- exit;
- end;
- for i:=0 to ChildCnt-1 do
- begin
- if (i>0) then
- AttrData.Value+=', ';
- AttrData.Value+=Result.Children[i].AsString;
- end;
- if AttrId>=CSSAttributeID_All then
- begin
- Desc:=Resolver.GetAttributeDesc(AttrId);
- if Pos('var(',AttrData.Value)>0 then
- begin
- // cannot be parsed yet
- end else if AttrID<Resolver.CSSRegistry.AttributeCount then
- begin
- if Resolver.InitParseAttr(Desc,AttrData,AttrData.Value) then
- begin
- if Assigned(Desc.OnCheck) then
- AttrData.Invalid:=not Desc.OnCheck(Resolver);
- end;
- end;
- {$IFDEF VerboseCSSResolver}
- if AttrData.Invalid then
- Log(etWarning,20240710162400,'Invalid CSS attribute value: '+Result.AsString,aKey);
- {$ENDIF}
- end;
- end else begin
- Log(etWarning,20220908230855,'Expected property name, but found '+aKey.ClassName,aKey);
- end;
- end;
- function TCSSResolverParser.ParseSelector: TCSSElement;
- begin
- Result:=inherited ParseSelector;
- CheckSelector(Result);
- end;
- procedure TCSSResolverParser.CheckSelector(El: TCSSElement);
- var
- C: TClass;
- begin
- C:=El.ClassType;
- if C=TCSSResolvedIdentifierElement then
- // e.g. div {}
- ResolveType(TCSSResolvedIdentifierElement(El))
- else if C=TCSSHashIdentifierElement then
- // e.g. #id {}
- else if C=TCSSClassNameElement then
- // e.g. .classname {}
- else if C=TCSSResolvedPseudoClassElement then
- // e.g. :pseudoclass {}
- ResolvePseudoClass(TCSSResolvedPseudoClassElement(El))
- else if C=TCSSBinaryElement then
- CheckSelectorBinary(TCSSBinaryElement(El))
- else if C=TCSSArrayElement then
- CheckSelectorArray(TCSSArrayElement(El))
- else if C=TCSSListElement then
- CheckSelectorList(TCSSListElement(El))
- else if C=TCSSResolvedCallElement then
- // checked via ParseCall
- else
- Log(etWarning,20240625131810,'Unknown CSS selector element',El);
- end;
- procedure TCSSResolverParser.CheckSelectorArray(anArray: TCSSArrayElement);
- var
- {$IFDEF VerboseCSSResolver}
- i: integer;
- {$ENDIF}
- El: TCSSElement;
- C: TClass;
- aValue: TCSSString;
- begin
- if anArray.Prefix<>nil then
- begin
- Log(etWarning,20240625134737,'Invalid CSS attribute selector prefix',anArray.Prefix);
- exit;
- end;
- {$IFDEF VerboseCSSResolver}
- writeln('TCSSResolverParser.CheckSelectorArray Prefix=',GetCSSObj(anArray.Prefix),' ChildCount=',anArray.ChildCount);
- for i:=0 to anArray.ChildCount-1 do
- writeln('TCSSResolverParser.CheckSelectorArray ',i,' ',GetCSSObj(anArray.Children[i]));
- {$ENDIF}
- if anArray.ChildCount<1 then
- begin
- Log(etWarning,20220910154033,'Invalid CSS attribute selector',anArray);
- exit;
- end;
- if anArray.ChildCount>1 then
- begin
- El:=anArray.Children[1];
- C:=El.ClassType;
- if C=TCSSResolvedIdentifierElement then
- begin
- aValue:=TCSSResolvedIdentifierElement(El).Value;
- case aValue of
- 'i','s': ;
- else
- Log(etWarning,20240625134931,'Invalid attribute modifier "'+aValue+'"',El);
- exit;
- end;
- end else begin
- Log(etWarning,20240625134940,'Invalid CSS attribute modifier',El);
- exit;
- end;
- end;
- if (anArray.ChildCount>2) then
- Log(etWarning,20240625134951,'Invalid CSS attribute modifier',anArray.Children[2]);
- El:=anArray.Children[0];
- C:=El.ClassType;
- if C=TCSSResolvedIdentifierElement then
- begin
- // [name] -> has explicit attribute
- ResolveAttribute(TCSSResolvedIdentifierElement(El));
- end else if C=TCSSBinaryElement then
- CheckSelectorArrayBinary(TCSSBinaryElement(El))
- else begin
- Log(etWarning,20240625135119,'Invalid CSS array selector',El);
- end;
- end;
- procedure TCSSResolverParser.CheckSelectorArrayBinary(aBinary: TCSSBinaryElement
- );
- var
- Left, Right: TCSSElement;
- C: TClass;
- begin
- Left:=aBinary.Left;
- if Left.ClassType<>TCSSResolvedIdentifierElement then
- begin
- Log(etWarning,20240625154314,'Invalid CSS array selector, expected attribute',Left);
- exit;
- end;
- ResolveAttribute(TCSSResolvedIdentifierElement(Left));
- Right:=aBinary.Right;
- C:=Right.ClassType;
- if (C=TCSSStringElement) or (C=TCSSIntegerElement) or (C=TCSSFloatElement)
- or (C=TCSSResolvedIdentifierElement) then
- // ok
- else begin
- Log(etWarning,20240625154455,'Invalid CSS array selector, expected string',Right);
- exit;
- end;
- ComputeValue(Right);
- case aBinary.Operation of
- boEquals,
- boSquaredEqual,
- boDollarEqual,
- boPipeEqual,
- boStarEqual,
- boTildeEqual: ;
- else
- Log(etWarning,20240625154617,'Invalid CSS array selector operator',aBinary);
- end;
- end;
- procedure TCSSResolverParser.CheckSelectorBinary(aBinary: TCSSBinaryElement);
- begin
- case aBinary.Operation of
- boGT,
- boPlus,
- boTilde,
- boWhiteSpace: ;
- else
- Log(etWarning,20240625153307,'Invalid CSS binary selector '+BinaryOperators[aBinary.Operation],aBinary);
- end;
- CheckSelector(aBinary.Left);
- CheckSelector(aBinary.Right);
- end;
- procedure TCSSResolverParser.CheckSelectorList(aList: TCSSListElement);
- var
- i: Integer;
- El: TCSSElement;
- begin
- for i:=0 to aList.ChildCount-1 do
- begin
- El:=aList.Children[i];
- {$IFDEF VerboseCSSResolver}
- writeln('TCSSResolverParser.CheckSelectorList ',i,' ',GetCSSObj(El),' AsString=',El.AsString);
- {$ENDIF}
- CheckSelector(El);
- end;
- end;
- procedure TCSSResolverParser.CheckNthChildParams(aCall: TCSSResolvedCallElement);
- procedure NthWarn(const ID: TCSSMsgID; const Msg: string; PosEl: TCSSElement);
- begin
- Log(etWarning,ID,CSSSelectorCallNames[aCall.NameNumericalID]+' '+Msg,PosEl);
- end;
- var
- i, ArgCount, aModulo, aStart: Integer;
- Arg, OffsetEl: TCSSElement;
- Str: TCSSString;
- UnaryEl, anUnary: TCSSUnaryElement;
- Params: TCSSNthChildParams;
- begin
- if aCall.Params<>nil then
- raise Exception.Create('20240625150639');
- ArgCount:=aCall.ArgCount;
- {$IFDEF VerboseCSSResolver}
- writeln('TCSSResolverParser.CheckSelectorCall_NthChild ArgCount=',aCall.ArgCount);
- for i:=0 to aCall.ArgCount-1 do
- writeln('TCSSResolverParser.CheckSelectorCall_NthChild Arg[',i,'] ',GetCSSObj(aCall.Args[i]),' AsString=',aCall.Args[i].AsString);
- {$ENDIF}
- // An, An+B, An+B of S, odd, even
- i:=0;
- aModulo:=0;
- aStart:=1;
- // check step
- if ArgCount<=i then
- begin
- NthWarn(20220915143843,'missing arguments',aCall);
- exit;
- end;
- Arg:=aCall.Args[i];
- if Arg.ClassType=TCSSIntegerElement then
- begin
- aModulo:=TCSSIntegerElement(Arg).Value;
- inc(i);
- // check n
- if ArgCount<=i then
- begin
- NthWarn(20220915143843,'missing arguments',aCall);
- exit;
- end;
- Arg:=aCall.Args[i];
- if Arg.ClassType<>TCSSResolvedIdentifierElement then
- begin
- NthWarn(20220915144312,'expected n',Arg);
- exit;
- end;
- if TCSSResolvedIdentifierElement(Arg).Value<>'n' then
- begin
- NthWarn(20220915144359,'expected n',Arg);
- exit;
- end;
- end
- else if Arg.ClassType=TCSSResolvedIdentifierElement then
- begin
- Str:=TCSSResolvedIdentifierElement(Arg).Value;
- case lowercase(Str) of
- 'even':
- begin
- aModulo:=2;
- aStart:=2;
- end;
- 'odd':
- begin
- aModulo:=2;
- end;
- 'n':
- begin
- aModulo:=1;
- end;
- else
- NthWarn(20220915150332,'expected multiplier',Arg);
- exit;
- end
- end else if Arg.ClassType=TCSSUnaryElement then
- begin
- anUnary:=TCSSUnaryElement(Arg);
- case anUnary.Operation of
- uoMinus: aModulo:=-1;
- uoPlus: aModulo:=1;
- else
- NthWarn(20220917080309,'expected multiplier',Arg);
- exit;
- end;
- if (anUnary.Right.ClassType=TCSSResolvedIdentifierElement)
- and (SameText(TCSSResolvedIdentifierElement(anUnary.Right).Value,'n')) then
- begin
- // ok
- end else begin
- NthWarn(20220917080154,'expected multiplier',Arg);
- exit;
- end;
- end else
- begin
- NthWarn(20220915144056,'expected multiplier',Arg);
- exit;
- end;
- inc(i);
- if ArgCount>i then
- begin
- Arg:=aCall.Args[i];
- if Arg.ClassType=TCSSUnaryElement then
- begin
- UnaryEl:=TCSSUnaryElement(Arg);
- if not (UnaryEl.Operation in [uoMinus,uoPlus]) then
- begin
- NthWarn(20220915151422,'unexpected offset',UnaryEl);
- exit;
- end;
- OffsetEl:=UnaryEl.Right;
- if OffsetEl=nil then
- begin
- NthWarn(20220915151511,'unexpected offset',UnaryEl);
- exit;
- end;
- if OffsetEl.ClassType<>TCSSIntegerElement then
- begin
- NthWarn(20220915151718,'unexpected offset',OffsetEl);
- exit;
- end;
- aStart:=TCSSIntegerElement(OffsetEl).Value;
- if UnaryEl.Operation=uoMinus then
- aStart:=-aStart;
- end else
- begin
- NthWarn(20220915150851,'expected offset',Arg);
- exit;
- end;
- end;
- Params:=CSSNthChildParamsClass.Create;
- aCall.Params:=Params;
- Params.Modulo:=aModulo;
- Params.Start:=aStart;
- inc(i);
- if (i<ArgCount) then
- begin
- Arg:=aCall.Args[i];
- if (Arg.ClassType=TCSSResolvedIdentifierElement)
- and (SameText(TCSSResolvedIdentifierElement(Arg).Value,'of')) then
- begin
- // An+B of Selector
- inc(i);
- if i=ArgCount then
- begin
- NthWarn(20240711154813,'expected selector',Arg);
- exit;
- end;
- Arg:=aCall.Args[i];
- Params.HasOf:=true;
- Params.OfSelector:=Arg;
- end;
- end;
- if (aCall.NameNumericalID in [CSSCallID_NthOfType,CSSCallID_NthLastOfType]) then
- Params.HasOf:=true;
- end;
- function TCSSResolverParser.ComputeValue(El: TCSSElement): TCSSString;
- var
- ElData: TObject;
- C: TClass;
- StrEl: TCSSStringElement;
- IntEl: TCSSIntegerElement;
- FloatEl: TCSSFloatElement;
- begin
- C:=El.ClassType;
- if C=TCSSResolvedIdentifierElement then
- Result:=TCSSResolvedIdentifierElement(El).Value
- else if (C=TCSSStringElement)
- or (C=TCSSIntegerElement)
- or (C=TCSSFloatElement) then
- begin
- ElData:=El.CustomData;
- if ElData is TCSSValueData then
- exit(TCSSValueData(ElData).NormValue);
- if C=TCSSStringElement then
- begin
- StrEl:=TCSSStringElement(El);
- Result:=StrEl.Value;
- end
- else if C=TCSSIntegerElement then
- begin
- IntEl:=TCSSIntegerElement(El);
- Result:=IntEl.AsString;
- end else if C=TCSSFloatElement then
- begin
- FloatEl:=TCSSFloatElement(El);
- Result:=FloatEl.AsString;
- end;
- ElData:=TCSSValueData.Create;
- TCSSValueData(ElData).NormValue:=Result;
- El.CustomData:=ElData;
- end else begin
- Log(etWarning,20240625162632,'TCSSResolverParser.ComputeValue not supported',El);
- end;
- end;
- constructor TCSSResolverParser.Create(AScanner: TCSSScanner);
- begin
- inherited Create(AScanner);
- CSSIdentifierElementClass:=TCSSResolvedIdentifierElement;
- CSSPseudoClassElementClass:=TCSSResolvedPseudoClassElement;
- CSSCallElementClass:=TCSSResolvedCallElement;
- CSSNthChildParamsClass:=TCSSNthChildParams;
- CSSAttributeKeyDataClass:=TCSSAttributeKeyData;
- end;
- destructor TCSSResolverParser.Destroy;
- begin
- inherited Destroy;
- end;
- procedure TCSSResolverParser.Log(MsgType: TEventType; const ID: TCSSMsgID;
- const Msg: TCSSString; PosEl: TCSSElement);
- begin
- if Assigned(OnLog) then
- OnLog(MsgType,ID,Msg,PosEl);
- end;
- class function TCSSResolverParser.IsWhiteSpace(const s: TCSSString): boolean;
- var
- i: Integer;
- begin
- for i:=1 to length(s) do
- if not (s[i] in [' ',#10,#13]) then
- exit(false);
- Result:=true;
- end;
- end.
|