fpcssresparser.pas 81 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694
  1. {
  2. This file is part of the Free Pascal Run time library.
  3. Copyright (c) 2023 by Michael Van Canneyt ([email protected])
  4. This file contains CSS utility class
  5. See the File COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************
  11. }
  12. {$IFNDEF FPC_DOTTEDUNITS}
  13. unit fpCSSResParser;
  14. {$ENDIF FPC_DOTTEDUNITS}
  15. {$mode ObjFPC}{$H+}
  16. {$Interfaces CORBA}
  17. {$ModeSwitch AdvancedRecords}
  18. {$IF FPC_FULLVERSION>30300}
  19. {$WARN 6060 off} // Case statement does not handle all possible cases
  20. {$ENDIF}
  21. {$WARN 6058 off : Call to subroutine "$1" marked as inline is not inlined}
  22. interface
  23. {$IFDEF FPC_DOTTEDUNITS}
  24. uses
  25. System.Classes, System.SysUtils, System.Math, System.Contnrs, System.StrUtils,
  26. Fcl.AVLTree, FpCss.Tree, FpCss.Scanner, FpCss.Parser;
  27. {$ELSE FPC_DOTTEDUNITS}
  28. uses
  29. Classes, SysUtils, Math, Contnrs, AVL_Tree, fpCSSTree, fpCSSScanner,
  30. fpCSSParser;
  31. {$ENDIF FPC_DOTTEDUNITS}
  32. const
  33. CSSIDNone = 0;
  34. // built-in attribute IDs
  35. CSSAttributeID_ID = 1; // id of attribute key 'id'
  36. CSSAttributeID_Class = 2; // id of attribute key 'class'
  37. CSSAttributeID_All = 3; // id of attribute key 'all'
  38. CSSAttributeID_LastResolver = CSSAttributeID_All;
  39. // built-in type IDs
  40. CSSTypeID_Universal = 1; // id of type '*'
  41. CSSTypeID_LastResolver = CSSTypeID_Universal;
  42. // built-in pseudo class IDs
  43. CSSPseudoID_Root = 1; // :root
  44. CSSPseudoID_Empty = CSSPseudoID_Root+1; // :empty
  45. CSSPseudoID_FirstChild = CSSPseudoID_Empty+1; // :first-child
  46. CSSPseudoID_LastChild = CSSPseudoID_FirstChild+1; // :last-child
  47. CSSPseudoID_OnlyChild = CSSPseudoID_LastChild+1; // :only-child
  48. CSSPseudoID_FirstOfType = CSSPseudoID_OnlyChild+1; // :first-of-type
  49. CSSPseudoID_LastOfType = CSSPseudoID_FirstOfType+1; // :last-of-type
  50. CSSPseudoID_OnlyOfType = CSSPseudoID_LastOfType+1; // :only-of-type
  51. CSSPseudoID_LastResolver = CSSPseudoID_OnlyOfType;
  52. CSSPseudoClassNames: array[0..CSSPseudoID_LastResolver] of TCSSString = (
  53. '?',
  54. ':root',
  55. ':empty',
  56. ':first-child',
  57. ':last-child',
  58. ':only-child',
  59. ':first-of-type',
  60. ':last-of-type',
  61. ':only-of-type'
  62. );
  63. // built-in pseudo function IDs
  64. CSSCallID_Not = 1; // :not()
  65. CSSCallID_Is = CSSCallID_Not+1; // :is()
  66. CSSCallID_Where = CSSCallID_Is+1; // :where()
  67. CSSCallID_Has = CSSCallID_Where+1; // :has()
  68. CSSCallID_NthChild = CSSCallID_Has+1; // :nth-child(n)
  69. CSSCallID_NthLastChild = CSSCallID_NthChild+1; // :nth-last-child(n)
  70. CSSCallID_NthOfType = CSSCallID_NthLastChild+1; // :nth-of-type(n)
  71. CSSCallID_NthLastOfType = CSSCallID_NthOfType+1; // :nth-last-of-type(n)
  72. CSSCallID_LastResolver = CSSCallID_NthLastOfType;
  73. CSSSelectorCallNames: array[0..CSSCallID_LastResolver] of TCSSString = (
  74. '?',
  75. ':not()',
  76. ':is()',
  77. ':where()',
  78. ':has()',
  79. ':nth-child(n)',
  80. ':nth-last-child(n)',
  81. ':nth-of-type(n)',
  82. ':nth-last-of-type(n)'
  83. );
  84. // keywords
  85. CSSKeywordNone = 1;
  86. CSSKeywordInitial = CSSKeywordNone+1;
  87. CSSKeywordInherit = CSSKeywordInitial+1;
  88. CSSKeywordUnset = CSSKeywordInherit+1;
  89. CSSKeywordRevert = CSSKeywordUnset+1;
  90. CSSKeywordRevertLayer = CSSKeywordRevert+1;
  91. CSSKeywordAuto = CSSKeywordRevertLayer+1;
  92. CSSKeyword_LastResolver = CSSKeywordAuto;
  93. // attribute functions
  94. CSSAttrFuncVar = 1;
  95. CSSMinSafeIntDouble = -$1fffffffffffff; // -9007199254740991 54 bits (52 plus signed bit plus implicit highest bit)
  96. CSSMaxSafeIntDouble = $1fffffffffffff; // 9007199254740991
  97. type
  98. TCSSMsgID = int64; // used for message numbers, e.g. error codes
  99. TCSSNumericalID = integer; // used for IDs of each type and attribute
  100. TCSSNumericalIDArray = array of TCSSNumericalID;
  101. TCSSNumericalIDKind = (
  102. nikAttribute,
  103. nikPseudoClass, // e.g. "hover" of ":hover"
  104. nikPseudoFunction, // e.g. "is" of ":is()"
  105. nikType,
  106. nikKeyword,
  107. nikAttrFunction // e.g. "calc" of "calc()"
  108. );
  109. TCSSNumericalIDs = set of TCSSNumericalIDKind;
  110. const
  111. nikAllDescriptors = [nikAttribute,nikPseudoClass,nikType]; // all items having a descriptor
  112. CSSNumericalIDKindNames: array[TCSSNumericalIDKind] of TCSSString = (
  113. 'Type',
  114. 'PseudoClass',
  115. 'PseudoFunction',
  116. 'Attribute',
  117. 'Keyword',
  118. 'AttributeFunction'
  119. );
  120. type
  121. TCSSAlphaColor = DWord;
  122. TCSSNamedColor = record
  123. Name: TCSSString;
  124. Color: TCSSAlphaColor;
  125. end;
  126. const
  127. CSSNamedColors: array[0..148] of TCSSNamedColor = (
  128. (Name: 'aliceblue'; Color: TCSSAlphaColor($fff0f8ff)),
  129. (Name: 'antiquewhite'; Color: TCSSAlphaColor($fffaebd7)),
  130. (Name: 'aqua'; Color: TCSSAlphaColor($ff00ffff)),
  131. (Name: 'aquamarine'; Color: TCSSAlphaColor($ff7fffd4)),
  132. (Name: 'azure'; Color: TCSSAlphaColor($fff0ffff)),
  133. (Name: 'beige'; Color: TCSSAlphaColor($fff5f5dc)),
  134. (Name: 'bisque'; Color: TCSSAlphaColor($ffffe4c4)),
  135. (Name: 'black'; Color: TCSSAlphaColor($ff000000)),
  136. (Name: 'blanchedalmond'; Color: TCSSAlphaColor($ffffebcd)),
  137. (Name: 'blue'; Color: TCSSAlphaColor($ff0000ff)),
  138. (Name: 'blueviolet'; Color: TCSSAlphaColor($ff8a2be2)),
  139. (Name: 'brown'; Color: TCSSAlphaColor($ffa52a2a)),
  140. (Name: 'burlywood'; Color: TCSSAlphaColor($ffdeb887)),
  141. (Name: 'cadetblue'; Color: TCSSAlphaColor($ff5f9ea0)),
  142. (Name: 'chartreuse'; Color: TCSSAlphaColor($ff7fff00)),
  143. (Name: 'chocolate'; Color: TCSSAlphaColor($ffd2691e)),
  144. (Name: 'coral'; Color: TCSSAlphaColor($ffff7f50)),
  145. (Name: 'cornflowerblue'; Color: TCSSAlphaColor($ff6495ed)),
  146. (Name: 'cornsilk'; Color: TCSSAlphaColor($fffff8dc)),
  147. (Name: 'crimson'; Color: TCSSAlphaColor($ffdc143c)),
  148. (Name: 'cyan'; Color: TCSSAlphaColor($ff00ffff)),
  149. (Name: 'darkblue'; Color: TCSSAlphaColor($ff00008b)),
  150. (Name: 'darkcyan'; Color: TCSSAlphaColor($ff008b8b)),
  151. (Name: 'darkgoldenrod'; Color: TCSSAlphaColor($ffb8860b)),
  152. (Name: 'darkgray'; Color: TCSSAlphaColor($ffa9a9a9)),
  153. (Name: 'darkgreen'; Color: TCSSAlphaColor($ff006400)),
  154. (Name: 'darkgrey'; Color: TCSSAlphaColor($ffa9a9a9)),
  155. (Name: 'darkkhaki'; Color: TCSSAlphaColor($ffbdb76b)),
  156. (Name: 'darkmagenta'; Color: TCSSAlphaColor($ff8b008b)),
  157. (Name: 'darkolivegreen'; Color: TCSSAlphaColor($ff556b2f)),
  158. (Name: 'darkorange'; Color: TCSSAlphaColor($ffff8c00)),
  159. (Name: 'darkorchid'; Color: TCSSAlphaColor($ff9932cc)),
  160. (Name: 'darkred'; Color: TCSSAlphaColor($ff8b0000)),
  161. (Name: 'darksalmon'; Color: TCSSAlphaColor($ffe9967a)),
  162. (Name: 'darkseagreen'; Color: TCSSAlphaColor($ff8fbc8f)),
  163. (Name: 'darkslateblue'; Color: TCSSAlphaColor($ff483d8b)),
  164. (Name: 'darkslategray'; Color: TCSSAlphaColor($ff2f4f4f)),
  165. (Name: 'darkslategrey'; Color: TCSSAlphaColor($ff2f4f4f)),
  166. (Name: 'darkturquoise'; Color: TCSSAlphaColor($ff00ced1)),
  167. (Name: 'darkviolet'; Color: TCSSAlphaColor($ff9400d3)),
  168. (Name: 'deeppink'; Color: TCSSAlphaColor($ffff1493)),
  169. (Name: 'deepskyblue'; Color: TCSSAlphaColor($ff00bfff)),
  170. (Name: 'dimgray'; Color: TCSSAlphaColor($ff696969)),
  171. (Name: 'dimgrey'; Color: TCSSAlphaColor($ff696969)),
  172. (Name: 'dodgerblue'; Color: TCSSAlphaColor($ff1e90ff)),
  173. (Name: 'firebrick'; Color: TCSSAlphaColor($ffb22222)),
  174. (Name: 'floralwhite'; Color: TCSSAlphaColor($fffffaf0)),
  175. (Name: 'forestgreen'; Color: TCSSAlphaColor($ff228b22)),
  176. (Name: 'fuchsia'; Color: TCSSAlphaColor($ffff00ff)),
  177. (Name: 'gainsboro'; Color: TCSSAlphaColor($ffdcdcdc)),
  178. (Name: 'ghostwhite'; Color: TCSSAlphaColor($fff8f8ff)),
  179. (Name: 'gold'; Color: TCSSAlphaColor($ffffd700)),
  180. (Name: 'goldenrod'; Color: TCSSAlphaColor($ffdaa520)),
  181. (Name: 'gray'; Color: TCSSAlphaColor($ff808080)),
  182. (Name: 'green'; Color: TCSSAlphaColor($ff008000)),
  183. (Name: 'greenyellow'; Color: TCSSAlphaColor($ffadff2f)),
  184. (Name: 'grey'; Color: TCSSAlphaColor($ff808080)),
  185. (Name: 'honeydew'; Color: TCSSAlphaColor($fff0fff0)),
  186. (Name: 'hotpink'; Color: TCSSAlphaColor($ffff69b4)),
  187. (Name: 'indianred'; Color: TCSSAlphaColor($ffcd5c5c)),
  188. (Name: 'indigo'; Color: TCSSAlphaColor($ff4b0082)),
  189. (Name: 'ivory'; Color: TCSSAlphaColor($fffffff0)),
  190. (Name: 'khaki'; Color: TCSSAlphaColor($fff0e68c)),
  191. (Name: 'lavender'; Color: TCSSAlphaColor($ffe6e6fa)),
  192. (Name: 'lavenderblush'; Color: TCSSAlphaColor($fffff0f5)),
  193. (Name: 'lawngreen'; Color: TCSSAlphaColor($ff7cfc00)),
  194. (Name: 'lemonchiffon'; Color: TCSSAlphaColor($fffffacd)),
  195. (Name: 'lightblue'; Color: TCSSAlphaColor($ffadd8e6)),
  196. (Name: 'lightcoral'; Color: TCSSAlphaColor($fff08080)),
  197. (Name: 'lightcyan'; Color: TCSSAlphaColor($ffe0ffff)),
  198. (Name: 'lightgoldenrodyellow'; Color: TCSSAlphaColor($fffafad2)),
  199. (Name: 'lightgray'; Color: TCSSAlphaColor($ffd3d3d3)),
  200. (Name: 'lightgreen'; Color: TCSSAlphaColor($ff90ee90)),
  201. (Name: 'lightgrey'; Color: TCSSAlphaColor($ffd3d3d3)),
  202. (Name: 'lightpink'; Color: TCSSAlphaColor($ffffb6c1)),
  203. (Name: 'lightsalmon'; Color: TCSSAlphaColor($ffffa07a)),
  204. (Name: 'lightseagreen'; Color: TCSSAlphaColor($ff20b2aa)),
  205. (Name: 'lightskyblue'; Color: TCSSAlphaColor($ff87cefa)),
  206. (Name: 'lightslategray'; Color: TCSSAlphaColor($ff778899)),
  207. (Name: 'lightslategrey'; Color: TCSSAlphaColor($ff778899)),
  208. (Name: 'lightsteelblue'; Color: TCSSAlphaColor($ffb0c4de)),
  209. (Name: 'lightyellow'; Color: TCSSAlphaColor($ffffffe0)),
  210. (Name: 'lime'; Color: TCSSAlphaColor($ff00ff00)),
  211. (Name: 'limegreen'; Color: TCSSAlphaColor($ff32cd32)),
  212. (Name: 'linen'; Color: TCSSAlphaColor($fffaf0e6)),
  213. (Name: 'magenta'; Color: TCSSAlphaColor($ffff00ff)),
  214. (Name: 'maroon'; Color: TCSSAlphaColor($ff800000)),
  215. (Name: 'mediumaquamarine'; Color: TCSSAlphaColor($ff66cdaa)),
  216. (Name: 'mediumblue'; Color: TCSSAlphaColor($ff0000cd)),
  217. (Name: 'mediumorchid'; Color: TCSSAlphaColor($ffba55d3)),
  218. (Name: 'mediumpurple'; Color: TCSSAlphaColor($ff9370db)),
  219. (Name: 'mediumseagreen'; Color: TCSSAlphaColor($ff3cb371)),
  220. (Name: 'mediumslateblue'; Color: TCSSAlphaColor($ff7b68ee)),
  221. (Name: 'mediumspringgreen'; Color: TCSSAlphaColor($ff00fa9a)),
  222. (Name: 'mediumturquoise'; Color: TCSSAlphaColor($ff48d1cc)),
  223. (Name: 'mediumvioletred'; Color: TCSSAlphaColor($ffc71585)),
  224. (Name: 'midnightblue'; Color: TCSSAlphaColor($ff191970)),
  225. (Name: 'mintcream'; Color: TCSSAlphaColor($fff5fffa)),
  226. (Name: 'mistyrose'; Color: TCSSAlphaColor($ffffe4e1)),
  227. (Name: 'moccasin'; Color: TCSSAlphaColor($ffffe4b5)),
  228. (Name: 'navajowhite'; Color: TCSSAlphaColor($ffffdead)),
  229. (Name: 'navy'; Color: TCSSAlphaColor($ff000080)),
  230. (Name: 'oldlace'; Color: TCSSAlphaColor($fffdf5e6)),
  231. (Name: 'olive'; Color: TCSSAlphaColor($ff808000)),
  232. (Name: 'olivedrab'; Color: TCSSAlphaColor($ff6b8e23)),
  233. (Name: 'orange'; Color: TCSSAlphaColor($ffffa500)),
  234. (Name: 'orangered'; Color: TCSSAlphaColor($ffff4500)),
  235. (Name: 'orchid'; Color: TCSSAlphaColor($ffda70d6)),
  236. (Name: 'palegoldenrod'; Color: TCSSAlphaColor($ffeee8aa)),
  237. (Name: 'palegreen'; Color: TCSSAlphaColor($ff98fb98)),
  238. (Name: 'paleturquoise'; Color: TCSSAlphaColor($ffafeeee)),
  239. (Name: 'palevioletred'; Color: TCSSAlphaColor($ffdb7093)),
  240. (Name: 'papayawhip'; Color: TCSSAlphaColor($ffffefd5)),
  241. (Name: 'peachpuff'; Color: TCSSAlphaColor($ffffdab9)),
  242. (Name: 'peru'; Color: TCSSAlphaColor($ffcd853f)),
  243. (Name: 'pink'; Color: TCSSAlphaColor($ffffc0cb)),
  244. (Name: 'plum'; Color: TCSSAlphaColor($ffdda0dd)),
  245. (Name: 'powderblue'; Color: TCSSAlphaColor($ffb0e0e6)),
  246. (Name: 'purple'; Color: TCSSAlphaColor($ff800080)),
  247. (Name: 'rebeccapurple'; Color: TCSSAlphaColor($ff663399)),
  248. (Name: 'red'; Color: TCSSAlphaColor($ffff0000)),
  249. (Name: 'rosybrown'; Color: TCSSAlphaColor($ffbc8f8f)),
  250. (Name: 'royalblue'; Color: TCSSAlphaColor($ff4169e1)),
  251. (Name: 'saddlebrown'; Color: TCSSAlphaColor($ff8b4513)),
  252. (Name: 'salmon'; Color: TCSSAlphaColor($fffa8072)),
  253. (Name: 'sandybrown'; Color: TCSSAlphaColor($fff4a460)),
  254. (Name: 'seagreen'; Color: TCSSAlphaColor($ff2e8b57)),
  255. (Name: 'seashell'; Color: TCSSAlphaColor($fffff5ee)),
  256. (Name: 'sienna'; Color: TCSSAlphaColor($ffa0522d)),
  257. (Name: 'silver'; Color: TCSSAlphaColor($ffc0c0c0)),
  258. (Name: 'skyblue'; Color: TCSSAlphaColor($ff87ceeb)),
  259. (Name: 'slateblue'; Color: TCSSAlphaColor($ff6a5acd)),
  260. (Name: 'slategray'; Color: TCSSAlphaColor($ff708090)),
  261. (Name: 'slategrey'; Color: TCSSAlphaColor($ff708090)),
  262. (Name: 'snow'; Color: TCSSAlphaColor($fffffafa)),
  263. (Name: 'springgreen'; Color: TCSSAlphaColor($ff00ff7f)),
  264. (Name: 'steelblue'; Color: TCSSAlphaColor($ff4682b4)),
  265. (Name: 'tan'; Color: TCSSAlphaColor($ffd2b48c)),
  266. (Name: 'teal'; Color: TCSSAlphaColor($ff008080)),
  267. (Name: 'thistle'; Color: TCSSAlphaColor($ffd8bfd8)),
  268. (Name: 'tomato'; Color: TCSSAlphaColor($ffff6347)),
  269. (Name: 'transparent'; Color: TCSSAlphaColor($ff0)),
  270. (Name: 'turquoise'; Color: TCSSAlphaColor($ff40e0d0)),
  271. (Name: 'violet'; Color: TCSSAlphaColor($ffee82ee)),
  272. (Name: 'wheat'; Color: TCSSAlphaColor($fff5deb3)),
  273. (Name: 'white'; Color: TCSSAlphaColor($ffffffff)),
  274. (Name: 'whitesmoke'; Color: TCSSAlphaColor($fff5f5f5)),
  275. (Name: 'yellow'; Color: TCSSAlphaColor($ffffff00)),
  276. (Name: 'yellowgreen'; Color: TCSSAlphaColor($ff9acd32))
  277. );
  278. type
  279. { TCSSRegistryNamedItem }
  280. TCSSRegistryNamedItem = class
  281. public
  282. Name: TCSSString; // case sensitive
  283. Index: TCSSNumericalID;
  284. end;
  285. { TCSSPseudoClassDesc }
  286. TCSSPseudoClassDesc = class(TCSSRegistryNamedItem)
  287. public
  288. end;
  289. TCSSPseudoClassDescClass = class of TCSSPseudoClassDesc;
  290. TCSSPseudoClassDescArray = array of TCSSPseudoClassDesc;
  291. { TCSSTypeDesc }
  292. TCSSTypeDesc = class(TCSSRegistryNamedItem)
  293. public
  294. end;
  295. TCSSTypeDescClass = class of TCSSTypeDesc;
  296. TCSSTypeDescArray = array of TCSSTypeDesc;
  297. { TCSSAttributeKeyData }
  298. TCSSAttributeKeyData = class(TCSSElementOwnedData)
  299. public
  300. Invalid: boolean;
  301. Complete: boolean;
  302. Value: TCSSString;
  303. end;
  304. TCSSAttributeKeyDataClass = class of TCSSAttributeKeyData;
  305. TCSSBaseResolver = class;
  306. TCSSResolverParser = class;
  307. { TCSSAttributeDesc - general properties of a CSS attribute }
  308. TCSSAttributeDesc = class(TCSSRegistryNamedItem)
  309. public
  310. type
  311. TCheckEvent = function(Resolver: TCSSBaseResolver): boolean of object;
  312. TSplitShorthandEvent = procedure(Resolver: TCSSBaseResolver;
  313. var AttrIDs: TCSSNumericalIDArray; var Values: TCSSStringArray) of object;
  314. public
  315. Inherits: boolean; // true = the default value is the parent's value
  316. All: boolean; // true = can be changed by the 'all' attribute
  317. InitialValue: TCSSString;
  318. CompProps: array of TCSSAttributeDesc; // if this attribute is a shorthand,
  319. // these are the component properties (longhands + sub-shorthands like border-width)
  320. // used by the cascade algorithm to delete all overwritten properties
  321. OnCheck: TCheckEvent; // called by the parser after reading a declaration and there is no var()
  322. // return false if invalid, so the resolver skips this declaration
  323. OnSplitShorthand: TSplitShorthandEvent; // called by resolver after resolving var(), if any value is empty, the initialvalue is used
  324. end;
  325. TCSSAttributeDescClass = class of TCSSAttributeDesc;
  326. TCSSAttributeDescArray = array of TCSSAttributeDesc;
  327. { TCSSRegistry }
  328. TCSSRegistry = class
  329. private
  330. FAttrFunctionCount: TCSSNumericalID;
  331. FAttributeCount: TCSSNumericalID;
  332. FHashLists: array[TCSSNumericalIDKind] of TFPHashList; // name to TCSSRegistryNamedItem
  333. FKeywordCount: TCSSNumericalID;
  334. FPseudoClassCount: TCSSNumericalID;
  335. FPseudoFunctionCount: TCSSNumericalID;
  336. FStamp, FModifiedStamp: TCSSNumericalID;
  337. FTypeCount: TCSSNumericalID;
  338. function GetModified: boolean;
  339. procedure SetModified(const AValue: boolean);
  340. public
  341. constructor Create;
  342. procedure Init; virtual; // add basic items
  343. destructor Destroy; override;
  344. function FindNamedItem(Kind: TCSSNumericalIDKind; const aName: TCSSString): TCSSRegistryNamedItem; overload;
  345. function IndexOfNamedItem(Kind: TCSSNumericalIDKind; const aName: TCSSString): TCSSNumericalID; overload;
  346. procedure ConsistencyCheck; virtual;
  347. procedure ChangeStamp;
  348. property Stamp: TCSSNumericalID read FStamp; // always >0
  349. property Modified: boolean read GetModified write SetModified;
  350. public
  351. // attributes
  352. Attributes: TCSSAttributeDescArray; // Note: Attributes[0] is nil to spot bugs easily
  353. Attribute_ClassOf: TCSSAttributeDescClass;
  354. NotAllAttributes: TCSSAttributeDescArray;
  355. function AddAttribute(Attr: TCSSAttributeDesc): TCSSAttributeDesc; overload;
  356. function AddAttribute(const aName: TCSSString; const aInitialValue: TCSSString = '';
  357. aInherits: boolean = false; aAll: boolean = true; aClass: TCSSAttributeDescClass = nil): TCSSAttributeDesc; overload;
  358. function FindAttribute(const aName: TCSSString): TCSSAttributeDesc; overload;
  359. function IndexOfAttributeName(const aName: TCSSString): TCSSNumericalID; overload;
  360. procedure AddSplitLonghand(var AttrIDs: TCSSNumericalIDArray; var Values: TCSSStringArray; AttrID: TCSSNumericalID; const Value: TCSSString); overload;
  361. procedure AddSplitLonghandSides(var AttrIDs: TCSSNumericalIDArray; var Values: TCSSStringArray;
  362. TopID, RightID, BottomID, LeftID: TCSSNumericalID; const Found: TCSSStringArray); overload;
  363. procedure AddSplitLonghandCorners(var AttrIDs: TCSSNumericalIDArray; var Values: TCSSStringArray;
  364. TopLeftID, TopRightID, BottomLeftID, BottomRightID: TCSSNumericalID; const Found: TCSSStringArray); overload;
  365. property AttributeCount: TCSSNumericalID read FAttributeCount;
  366. public
  367. // pseudo classes
  368. PseudoClasses: TCSSPseudoClassDescArray; // Note: PseudoClasses[0] is nil to spot bugs easily
  369. PseudoClass_ClassOf: TCSSPseudoClassDescClass;
  370. function AddPseudoClass(aPseudo: TCSSPseudoClassDesc): TCSSPseudoClassDesc; overload;
  371. function AddPseudoClass(const aName: TCSSString; aClass: TCSSPseudoClassDescClass = nil): TCSSPseudoClassDesc; overload;
  372. function FindPseudoClass(const aName: TCSSString): TCSSPseudoClassDesc; overload;
  373. function IndexOfPseudoClassName(const aName: TCSSString): TCSSNumericalID; overload;
  374. property PseudoClassCount: TCSSNumericalID read FPseudoClassCount;
  375. public
  376. // pseudo functions lowercase (they are parsed case insensitive)
  377. PseudoFunctions: TCSSStringArray;
  378. function AddPseudoFunction(const aName: TCSSString): TCSSNumericalID; overload;
  379. function IndexOfPseudoFunction(const aName: TCSSString): TCSSNumericalID; overload;
  380. property PseudoFunctionCount: TCSSNumericalID read FPseudoFunctionCount;
  381. public
  382. // types
  383. Types: TCSSTypeDescArray; // Note: Types[0] is nil to spot bugs easily
  384. Type_ClassOf: TCSSTypeDescClass;
  385. function AddType(aType: TCSSTypeDesc): TCSSTypeDesc; overload;
  386. function AddType(const aName: TCSSString; aClass: TCSSTypeDescClass = nil): TCSSTypeDesc; overload;
  387. function FindType(const aName: TCSSString): TCSSTypeDesc; overload;
  388. function IndexOfTypeName(const aName: TCSSString): TCSSNumericalID; overload;
  389. property TypeCount: TCSSNumericalID read FTypeCount;
  390. public
  391. // keywords
  392. Keywords: TCSSStringArray;
  393. kwFirstColor, kwLastColor, kwTransparent: TCSSNumericalID;
  394. function AddKeyword(const aName: TCSSString): TCSSNumericalID; overload;
  395. procedure AddKeywords(const Names: TCSSStringArray; out First, Last: TCSSNumericalID); overload;
  396. function IndexOfKeyword(const aName: TCSSString): TCSSNumericalID; overload;
  397. procedure AddColorKeywords; virtual;
  398. function GetNamedColor(const aName: TCSSString): TCSSAlphaColor; virtual; overload;
  399. function GetKeywordColor(KeywordID: TCSSNumericalID): TCSSAlphaColor; virtual; overload;
  400. property KeywordCount: TCSSNumericalID read FKeywordCount;
  401. public
  402. // attribute functions
  403. AttrFunctions: TCSSStringArray;
  404. const afVar = CSSAttrFuncVar;
  405. function AddAttrFunction(const aName: TCSSString): TCSSNumericalID; overload;
  406. function IndexOfAttrFunction(const aName: TCSSString): TCSSNumericalID; overload;
  407. property AttrFunctionCount: TCSSNumericalID read FAttrFunctionCount;
  408. end;
  409. TCSSValueParserLogEvent = procedure(MsgType: TEventType; const ID: TCSSMsgID;
  410. const Msg: TCSSString; PosEl: TCSSElement) of object;
  411. { TCSSResolvedIdentifierElement }
  412. TCSSResolvedIdentifierElement = class(TCSSIdentifierElement)
  413. public
  414. NumericalID: TCSSNumericalID;
  415. Kind: TCSSNumericalIDKind;
  416. end;
  417. { TCSSResolvedPseudoClassElement }
  418. TCSSResolvedPseudoClassElement = class(TCSSPseudoClassElement)
  419. public
  420. NumericalID: TCSSNumericalID;
  421. Kind: TCSSNumericalIDKind;
  422. end;
  423. { TCSSNthChildParams }
  424. TCSSNthChildParams = class
  425. public
  426. Modulo: integer;
  427. Start: integer;
  428. HasOf: boolean; // for nth-of-type() HasOf=true and OfSelector=nil
  429. OfSelector: TCSSElement;
  430. end;
  431. TCSSNthChildParamsClass = class of TCSSNthChildParams;
  432. { TCSSResolvedCallElement }
  433. TCSSResolvedCallElement = class(TCSSCallElement)
  434. public
  435. NameNumericalID: TCSSNumericalID;
  436. Kind: TCSSNumericalIDKind;
  437. Params: TObject; // e.g. TCSSNthChildParams
  438. destructor Destroy; override;
  439. end;
  440. { TCSSValueData }
  441. TCSSValueData = class(TCSSElementOwnedData)
  442. public
  443. NormValue: TCSSString; // normalized value, stripped of comments and e.g. '0.1' instead of '000.100' or '.1'
  444. end;
  445. TCSSResValueKind = (
  446. rvkNone,
  447. rvkInvalid,
  448. rvkSymbol,
  449. rvkFloat,
  450. rvkCalcFloat,
  451. rvkKeyword,
  452. rvkKeywordUnknown,
  453. rvkFunction,
  454. rvkFunctionUnknown,
  455. rvkString,
  456. rvkHexColor
  457. );
  458. { TCSSResCompValue }
  459. TCSSResCompValue = record
  460. Kind: TCSSResValueKind;
  461. StartP, EndP: PCSSChar;
  462. function AsString: TCSSString;
  463. function FloatAsString: TCSSString;
  464. case longint of
  465. 1: (Float: Double; FloatUnit: TCSSUnit);
  466. 2: (KeywordID: TCSSNumericalID);
  467. 3: (FunctionID: TCSSNumericalID; BracketOpen: PCSSChar);
  468. 4: (Symbol: TCSSToken);
  469. end;
  470. { TCSSCheckAttrParams_Dimension }
  471. TCSSCheckAttrParams_Dimension = record
  472. public
  473. AllowedUnits: TCSSUnits;
  474. AllowNegative, AllowFrac: boolean;
  475. AllowedKeywordIDs: TCSSNumericalIDArray;
  476. function Fits(const ResValue: TCSSResCompValue): boolean; overload;
  477. end;
  478. { TCSSBaseResolver }
  479. TCSSBaseResolver = class(TComponent)
  480. private
  481. FCSSRegistry: TCSSRegistry;
  482. protected
  483. procedure SetCSSRegistry(const AValue: TCSSRegistry); virtual;
  484. public
  485. CurAttrData: TCSSAttributeKeyData;
  486. CurDesc: TCSSAttributeDesc;
  487. CurValue: TCSSString;
  488. CurComp: TCSSResCompValue;
  489. function InitParseAttr(Desc: TCSSAttributeDesc; AttrData: TCSSAttributeKeyData; const Value: TCSSString): boolean; virtual; // true if parsing can start
  490. procedure InitParseAttr(const Value: TCSSString); virtual;
  491. // check whole attribute, skipping invalid values, emit warnings:
  492. function CheckAttribute_Keyword(const AllowedKeywordIDs: TCSSNumericalIDArray): boolean; virtual;
  493. function CheckAttribute_CommaList_Keyword(const AllowedKeywordIDs: TCSSNumericalIDArray): boolean; virtual;
  494. function CheckAttribute_Dimension(const Params: TCSSCheckAttrParams_Dimension): boolean; virtual;
  495. function CheckAttribute_Color(const AllowedKeywordIDs: TCSSNumericalIDArray): boolean; virtual;
  496. // parse whole attribute, skipping invalid values:
  497. function ReadNext: boolean;
  498. function ReadAttribute_Keyword(out Invalid: boolean; const AllowedKeywordIDs: TCSSNumericalIDArray): boolean; virtual;
  499. function ReadAttribute_Dimension(out Invalid: boolean; const Params: TCSSCheckAttrParams_Dimension): boolean; virtual;
  500. function ReadAttribute_Color(out Invalid: boolean; const AllowedKeywordIDs: TCSSNumericalIDArray): boolean; virtual;
  501. function IsBaseKeyword(KeywordID: TCSSNumericalID): boolean;
  502. function IsKeywordIn(aKeywordID: TCSSNumericalID; const KeywordIDs: TCSSNumericalIDArray): boolean; overload;
  503. function IsKeywordIn(const KeywordIDs: TCSSNumericalIDArray): boolean; overload;
  504. function IsLengthOrPercentage(AllowNegative: boolean): boolean; overload;
  505. function IsLengthOrPercentage(const ResValue: TCSSResCompValue; AllowNegative: boolean): boolean; overload;
  506. function IsSymbol(Token: TCSSToken): boolean; overload;
  507. function GetCompString: TCSSString; overload;
  508. function GetCompString(const aValue: string; const ResValue: TCSSResCompValue): TCSSString; overload;
  509. // low level functions to parse attribute components
  510. function ReadComp(var aComp: TCSSResCompValue): boolean; // true if parsing attribute can continue
  511. procedure ReadWordID(var aComp: TCSSResCompValue);
  512. class function ReadValue(var aComp: TCSSResCompValue): boolean; // true if parsing attribute can continue, not using CSSRegistry
  513. class function ReadNumber(var aComp: TCSSResCompValue): boolean;
  514. class function ReadIdentifier(var aComp: TCSSResCompValue): boolean;
  515. class procedure SkipToEndOfAttribute(var p: PCSSChar);
  516. class function SkipString(var p: PCSSChar): boolean;
  517. class function SkipBrackets(var p: PCSSChar; Lvl: integer = 1): boolean;
  518. // registry
  519. function GetAttributeID(const aName: TCSSString; AutoCreate: boolean = false): TCSSNumericalID; virtual;
  520. function GetAttributeDesc(AttrID: TCSSNumericalID): TCSSAttributeDesc; virtual;
  521. function GetTypeID(const aName: TCSSString): TCSSNumericalID; virtual;
  522. function GetPseudoClassID(const aName: TCSSString): TCSSNumericalID; virtual;
  523. function GetPseudoFunctionID(const aName: TCSSString): TCSSNumericalID; virtual;
  524. property CSSRegistry: TCSSRegistry read FCSSRegistry write SetCSSRegistry;
  525. end;
  526. { TCSSResolverParser
  527. - resolves identifiers to IDs
  528. - warns about constructs unsupported by resolver }
  529. TCSSResolverParser = class(TCSSParser)
  530. private
  531. FOnLog: TCSSValueParserLogEvent;
  532. FResolver: TCSSBaseResolver;
  533. protected
  534. function ResolveAttribute(El: TCSSResolvedIdentifierElement): TCSSNumericalID; virtual;
  535. function ResolveType(El: TCSSResolvedIdentifierElement): TCSSNumericalID; virtual;
  536. function ResolvePseudoClass(El: TCSSResolvedPseudoClassElement): TCSSNumericalID; virtual;
  537. function ResolvePseudoFunction(El: TCSSResolvedCallElement): TCSSNumericalID; virtual;
  538. function ParseCall(aName: TCSSString; IsSelector: boolean): TCSSCallElement; override;
  539. function ParseDeclaration(aIsAt: Boolean): TCSSDeclarationElement; override;
  540. function ParseSelector: TCSSElement; override;
  541. procedure CheckSelector(El: TCSSElement); virtual;
  542. procedure CheckSelectorArray(anArray: TCSSArrayElement); virtual;
  543. procedure CheckSelectorArrayBinary(aBinary: TCSSBinaryElement); virtual;
  544. procedure CheckSelectorBinary(aBinary: TCSSBinaryElement); virtual;
  545. procedure CheckSelectorList(aList: TCSSListElement); virtual;
  546. procedure CheckNthChildParams(aCall: TCSSResolvedCallElement); virtual;
  547. function ComputeValue(El: TCSSElement): TCSSString; virtual;
  548. public
  549. CSSNthChildParamsClass: TCSSNthChildParamsClass;
  550. CSSAttributeKeyDataClass: TCSSAttributeKeyDataClass;
  551. constructor Create(AScanner: TCSSScanner); override; overload;
  552. destructor Destroy; override;
  553. procedure Log(MsgType: TEventType; const ID: TCSSMsgID; const Msg: TCSSString; PosEl: TCSSElement); virtual;
  554. class function IsWhiteSpace(const s: TCSSString): boolean; virtual; overload;
  555. property Resolver: TCSSBaseResolver read FResolver write FResolver;
  556. property OnLog: TCSSValueParserLogEvent read FOnLog write FOnLog;
  557. end;
  558. implementation
  559. Const
  560. Alpha = ['A'..'Z','a'..'z'];
  561. Num = ['0'..'9'];
  562. AlNum = Alpha+Num;
  563. AlIden = Alpha+['-'];
  564. AlNumIden = AlNum+['-'];
  565. Whitespace = [#9,#10,#13,' '];
  566. //WhitespaceZ = Whitespace+[#0];
  567. Hex = ['0'..'9','a'..'z','A'..'Z'];
  568. ValEnd = [#0,#10,#13,#9,' ',';',',','}',')',']']; // used for skipping a component value
  569. { TCSSRegistry }
  570. procedure TCSSRegistry.SetModified(const AValue: boolean);
  571. begin
  572. if AValue then
  573. ChangeStamp
  574. else
  575. FModifiedStamp:=FStamp;
  576. end;
  577. function TCSSRegistry.GetModified: boolean;
  578. begin
  579. Result:=FStamp=FModifiedStamp;
  580. end;
  581. constructor TCSSRegistry.Create;
  582. var
  583. Kind: TCSSNumericalIDKind;
  584. i: Integer;
  585. begin
  586. for Kind in TCSSNumericalIDKind do
  587. FHashLists[Kind]:=TFPHashList.Create;
  588. if Attribute_ClassOf=nil then
  589. Attribute_ClassOf:=TCSSAttributeDesc;
  590. if PseudoClass_ClassOf=nil then
  591. PseudoClass_ClassOf:=TCSSPseudoClassDesc;
  592. if Type_ClassOf=nil then
  593. Type_ClassOf:=TCSSTypeDesc;
  594. // init attributes
  595. SetLength(Attributes,32);
  596. for i:=0 to length(Attributes)-1 do Attributes[i]:=nil;
  597. FAttributeCount:=1; // index 0 is CSSIDNone
  598. // init pseudo classes
  599. SetLength(PseudoClasses,32);
  600. for i:=0 to length(PseudoClasses)-1 do PseudoClasses[i]:=nil;
  601. FPseudoClassCount:=1; // index 0 is CSSIDNone
  602. // init pseudo functions
  603. SetLength(PseudoFunctions,16);
  604. FPseudoFunctionCount:=1; // index 0 is CSSIDNone
  605. // init types
  606. SetLength(Types,32);
  607. for i:=0 to length(Types)-1 do Types[i]:=nil;
  608. FTypeCount:=1; // index 0 is CSSIDNone
  609. // init keywords
  610. SetLength(Keywords,32);
  611. FKeywordCount:=1; // index 0 is CSSIDNone
  612. // init keywords
  613. SetLength(AttrFunctions,32);
  614. FAttrFunctionCount:=1; // index 0 is CSSIDNone
  615. end;
  616. procedure TCSSRegistry.Init;
  617. begin
  618. // init attributes
  619. if AddAttribute('id').Index<>CSSAttributeID_ID then
  620. raise Exception.Create('20240617191749');
  621. if AddAttribute('class').Index<>CSSAttributeID_Class then
  622. raise Exception.Create('20240617191801');
  623. if AddAttribute('all').Index<>CSSAttributeID_All then
  624. raise Exception.Create('20240617191816');
  625. // init pseudo classes
  626. if AddPseudoClass('root').Index<>CSSPseudoID_Root then
  627. raise Exception.Create('20240623165848');
  628. if AddPseudoClass('empty').Index<>CSSPseudoID_Empty then
  629. raise Exception.Create('20240623170450');
  630. if AddPseudoClass('first-child').Index<>CSSPseudoID_FirstChild then
  631. raise Exception.Create('20240623170508');
  632. if AddPseudoClass('last-child').Index<>CSSPseudoID_LastChild then
  633. raise Exception.Create('20240623170521');
  634. if AddPseudoClass('only-child').Index<>CSSPseudoID_OnlyChild then
  635. raise Exception.Create('20240623170534');
  636. if AddPseudoClass('first-of-type').Index<>CSSPseudoID_FirstOfType then
  637. raise Exception.Create('20240623170547');
  638. if AddPseudoClass('last-of-type').Index<>CSSPseudoID_LastOfType then
  639. raise Exception.Create('20240623170558');
  640. if AddPseudoClass('only-of-type').Index<>CSSPseudoID_OnlyOfType then
  641. raise Exception.Create('20240623170609');
  642. // init pseudo functions
  643. if AddPseudoFunction('not')<>CSSCallID_Not then
  644. raise Exception.Create('20240625183757');
  645. if AddPseudoFunction('is')<>CSSCallID_Is then
  646. raise Exception.Create('20240625142038');
  647. if AddPseudoFunction('where')<>CSSCallID_Where then
  648. raise Exception.Create('20240625142049');
  649. if AddPseudoFunction('has')<>CSSCallID_Has then
  650. raise Exception.Create('20240625142104');
  651. if AddPseudoFunction('nth-child')<>CSSCallID_NthChild then
  652. raise Exception.Create('20240625142124');
  653. if AddPseudoFunction('nth-last-child')<>CSSCallID_NthLastChild then
  654. raise Exception.Create('20240625142136');
  655. if AddPseudoFunction('nth-of-type')<>CSSCallID_NthOfType then
  656. raise Exception.Create('20240625142156');
  657. if AddPseudoFunction('nth-last-of-type')<>CSSCallID_NthLastOfType then
  658. raise Exception.Create('20240625142212');
  659. // init types
  660. if AddType('*').Index<>CSSTypeID_Universal then
  661. raise Exception.Create('20240617190914');
  662. // init keywords
  663. if AddKeyword('none')<>CSSKeywordNone then
  664. raise Exception.Create('20240623184021');
  665. if AddKeyword('initial')<>CSSKeywordInitial then
  666. raise Exception.Create('20240623184030');
  667. if AddKeyword('inherit')<>CSSKeywordInherit then
  668. raise Exception.Create('20240623184042');
  669. if AddKeyword('unset')<>CSSKeywordUnset then
  670. raise Exception.Create('20240623184053');
  671. if AddKeyword('revert')<>CSSKeywordRevert then
  672. raise Exception.Create('20240623184104');
  673. if AddKeyword('revert-layer')<>CSSKeywordRevertLayer then
  674. raise Exception.Create('20240623184114');
  675. if AddKeyword('auto')<>CSSKeywordAuto then
  676. raise Exception.Create('20240625182731');
  677. // init attribute functions
  678. if AddAttrFunction('var')<>CSSAttrFuncVar then
  679. raise Exception.Create('20240716124054');
  680. end;
  681. destructor TCSSRegistry.Destroy;
  682. var
  683. i: Integer;
  684. Kind: TCSSNumericalIDKind;
  685. begin
  686. for Kind in TCSSNumericalIDKind do
  687. FreeAndNil(FHashLists[Kind]);
  688. // attributes
  689. NotAllAttributes:=nil;
  690. for i:=1 to AttributeCount-1 do
  691. FreeAndNil(Attributes[i]);
  692. Attributes:=nil;
  693. FAttributeCount:=0;
  694. // pseudo classes
  695. for i:=1 to PseudoClassCount-1 do
  696. FreeAndNil(PseudoClasses[i]);
  697. PseudoClasses:=nil;
  698. FPseudoClassCount:=0;
  699. // types
  700. for i:=1 to TypeCount-1 do
  701. FreeAndNil(Types[i]);
  702. Types:=nil;
  703. FTypeCount:=0;
  704. // keywords
  705. FKeywordCount:=0;
  706. inherited Destroy;
  707. end;
  708. function TCSSRegistry.FindNamedItem(Kind: TCSSNumericalIDKind;
  709. const aName: TCSSString): TCSSRegistryNamedItem;
  710. begin
  711. if Kind in nikAllDescriptors then
  712. Result:=TCSSRegistryNamedItem(FHashLists[Kind].Find(aName))
  713. else
  714. raise Exception.Create('20240625141820');
  715. end;
  716. function TCSSRegistry.IndexOfNamedItem(Kind: TCSSNumericalIDKind;
  717. const aName: TCSSString): TCSSNumericalID;
  718. var
  719. Item: TCSSRegistryNamedItem;
  720. p: Pointer;
  721. begin
  722. if Kind in nikAllDescriptors then
  723. begin
  724. Item:=TCSSRegistryNamedItem(FHashLists[Kind].Find(aName));
  725. if Item<>nil then
  726. Result:=Item.Index
  727. else
  728. Result:=-1;
  729. end else begin
  730. p:=FHashLists[Kind].Find(aName);
  731. if p=nil then
  732. exit(CSSIDNone)
  733. else
  734. Result:={%H-}TCSSNumericalID(p);
  735. end;
  736. end;
  737. procedure TCSSRegistry.ConsistencyCheck;
  738. var
  739. ID, ID2: TCSSNumericalID;
  740. AttrDesc, SubAttrDesc: TCSSAttributeDesc;
  741. PseudoClassDesc: TCSSPseudoClassDesc;
  742. TypeDesc: TCSSTypeDesc;
  743. i: Integer;
  744. aName: TCSSString;
  745. begin
  746. if AttributeCount>length(Attributes) then
  747. raise Exception.Create('20240629102438');
  748. for ID:=1 to AttributeCount-1 do
  749. begin
  750. AttrDesc:=Attributes[ID];
  751. if AttrDesc=nil then
  752. raise Exception.Create('20240629102530 attr ID='+IntToStr(ID)+' Desc=nil');
  753. aName:=AttrDesc.Name;
  754. if aName='' then
  755. raise Exception.Create('20240629100056 attr ID='+IntToStr(ID)+' missing name');
  756. if length(aName)>255 then
  757. raise Exception.Create('20240629100143 attr ID='+IntToStr(ID)+' name too long "'+aName+'"');
  758. if aName[1]=':' then
  759. raise Exception.Create('20240701231211 attr ID='+IntToStr(ID)+' invalid name "'+aName+'"');
  760. if AttrDesc.Index<>ID then
  761. raise Exception.Create('20240629095849 attr ID='+IntToStr(ID)+' Desc.Index='+IntToStr(AttrDesc.Index)+' "'+aName+'"');
  762. ID2:=IndexOfAttributeName(aName);
  763. if ID2<>ID then
  764. raise Exception.Create('20240629101227 attr ID='+IntToStr(ID)+' "'+aName+'" IndexOf failed: '+IntToStr(ID2));
  765. if length(AttrDesc.CompProps)>0 then
  766. begin
  767. // check shorthand
  768. for i:=0 to length(AttrDesc.CompProps)-1 do
  769. begin
  770. SubAttrDesc:=AttrDesc.CompProps[i];
  771. if SubAttrDesc=nil then
  772. raise Exception.Create('20240629102701 attr ID='+IntToStr(ID)+' Shorthand="'+aName+'" CompDesc=nil '+IntToStr(i));
  773. if (SubAttrDesc.Index<=0) then
  774. raise Exception.Create('20240629100345 attr ID='+IntToStr(ID)+' Shorthand="'+aName+'" invalid CompAttr '+IntToStr(SubAttrDesc.Index));
  775. if (SubAttrDesc.Index>=ID) then
  776. raise Exception.Create('20240629100345 attr ID='+IntToStr(ID)+' Shorthand="'+aName+'" CompAttr after Shorthand: SubID='+IntToStr(SubAttrDesc.Index)+' SubName='+SubAttrDesc.Name);
  777. end;
  778. end;
  779. end;
  780. if PseudoClassCount>length(PseudoClasses) then
  781. raise Exception.Create('20240629102438');
  782. for ID:=1 to PseudoClassCount-1 do
  783. begin
  784. PseudoClassDesc:=PseudoClasses[ID];
  785. if PseudoClassDesc=nil then
  786. raise Exception.Create('20240629102605 pseudo class ID='+IntToStr(ID)+' Desc=nil');
  787. aName:=PseudoClassDesc.Name;
  788. if aName='' then
  789. raise Exception.Create('20240629100652 pseudo class ID='+IntToStr(ID)+' missing name');
  790. if length(aName)>255 then
  791. raise Exception.Create('20240629100657 pseudo class ID='+IntToStr(ID)+' name too long "'+aName+'"');
  792. if aName[1]=':' then
  793. raise Exception.Create('20240701231235 pseudo class ID='+IntToStr(ID)+' invalid name "'+aName+'"');
  794. if PseudoClassDesc.Index<>ID then
  795. raise Exception.Create('20240629100659 pseudo class ID='+IntToStr(ID)+' Desc.Index='+IntToStr(PseudoClassDesc.Index)+' "'+aName+'"');
  796. ID2:=IndexOfPseudoClassName(PseudoClassDesc.Name);
  797. if ID2<>ID then
  798. raise Exception.Create('20240629101227 pseudo class ID='+IntToStr(ID)+' "'+aName+'" IndexOf failed: '+IntToStr(ID2));
  799. end;
  800. if PseudoFunctionCount>length(PseudoFunctions) then
  801. raise Exception.Create('20240629103430');
  802. for ID:=1 to PseudoFunctionCount-1 do
  803. begin
  804. aName:=PseudoFunctions[ID];
  805. if aName='' then
  806. raise Exception.Create('20240629103431 pseudo function ID='+IntToStr(ID)+' missing name');
  807. if length(aName)>255 then
  808. raise Exception.Create('20240629103433 pseudo function ID='+IntToStr(ID)+' name too long "'+aName+'"');
  809. if aName[1]=':' then
  810. raise Exception.Create('20240701231235 pseudo function ID='+IntToStr(ID)+' invalid name "'+aName+'"');
  811. ID2:=IndexOfPseudoFunction(aName);
  812. if ID2<>ID then
  813. raise Exception.Create('20240629103434 pseudo function ID='+IntToStr(ID)+' "'+aName+'" IndexOf failed: '+IntToStr(ID2));
  814. end;
  815. if TypeCount>length(Types) then
  816. raise Exception.Create('20240629102438');
  817. for ID:=1 to TypeCount-1 do
  818. begin
  819. TypeDesc:=Types[ID];
  820. if TypeDesc=nil then
  821. raise Exception.Create('20240629102620 type ID='+IntToStr(ID)+' Desc=nil');
  822. aName:=TypeDesc.Name;
  823. if aName='' then
  824. raise Exception.Create('20240629100812 type ID='+IntToStr(ID)+' missing name');
  825. if length(aName)>255 then
  826. raise Exception.Create('20240629100825 type ID='+IntToStr(ID)+' name too long "'+aName+'"');
  827. if aName[1]=':' then
  828. raise Exception.Create('20240701231645 type ID='+IntToStr(ID)+' invalid name "'+aName+'"');
  829. if TypeDesc.Index<>ID then
  830. raise Exception.Create('20240629101013 type ID='+IntToStr(ID)+' Desc.Index='+IntToStr(TypeDesc.Index)+' "'+aName+'"');
  831. ID2:=IndexOfTypeName(aName);
  832. if ID2<>ID then
  833. raise Exception.Create('20240629101529 type ID='+IntToStr(ID)+' "'+aName+'" IndexOf failed: '+IntToStr(ID2));
  834. end;
  835. if KeywordCount>length(Keywords) then
  836. raise Exception.Create('20240629103200');
  837. for ID:=1 to KeywordCount-1 do
  838. begin
  839. aName:=Keywords[ID];
  840. if aName='' then
  841. raise Exception.Create('20240629103223 keyword ID='+IntToStr(ID)+' missing name');
  842. if length(aName)>255 then
  843. raise Exception.Create('20240629103242 keyword ID='+IntToStr(ID)+' name too long "'+aName+'"');
  844. if aName[1]=':' then
  845. raise Exception.Create('20240701231656 keyword ID='+IntToStr(ID)+' invalid name "'+aName+'"');
  846. ID2:=IndexOfKeyword(aName);
  847. if ID2<>ID then
  848. raise Exception.Create('20240629103303 keyword ID='+IntToStr(ID)+' "'+aName+'" IndexOf failed: '+IntToStr(ID2));
  849. end;
  850. end;
  851. procedure TCSSRegistry.ChangeStamp;
  852. begin
  853. if FStamp<high(FStamp) then
  854. inc(FStamp)
  855. else
  856. FStamp:=1;
  857. end;
  858. function TCSSRegistry.AddAttribute(Attr: TCSSAttributeDesc
  859. ): TCSSAttributeDesc;
  860. begin
  861. Result:=Attr;
  862. if Attr.Name='' then
  863. raise ECSSParser.Create('missing name');
  864. if FindAttribute(Attr.Name)<>nil then
  865. raise ECSSParser.Create('duplicate attribute "'+Attr.Name+'"');
  866. if AttributeCount=length(Attributes) then
  867. begin
  868. if AttributeCount<32 then
  869. SetLength(Attributes,32)
  870. else
  871. SetLength(Attributes,2*AttributeCount);
  872. FillByte(Attributes[AttributeCount],SizeOf(Pointer)*(length(Attributes)-AttributeCount),0);
  873. end;
  874. Attributes[AttributeCount]:=Attr;
  875. Attr.Index:=AttributeCount;
  876. FHashLists[nikAttribute].Add(Attr.Name,Attr);
  877. inc(FAttributeCount);
  878. ChangeStamp;
  879. end;
  880. function TCSSRegistry.AddAttribute(const aName: TCSSString;
  881. const aInitialValue: TCSSString; aInherits: boolean; aAll: boolean;
  882. aClass: TCSSAttributeDescClass): TCSSAttributeDesc;
  883. begin
  884. if aName='' then
  885. raise ECSSParser.Create('missing name');
  886. if FindAttribute(aName)<>nil then
  887. raise ECSSParser.Create('duplicate attribute "'+aName+'"');
  888. if aClass=nil then
  889. aClass:=Attribute_ClassOf;
  890. Result:=aClass.Create;
  891. Result.Name:=aName;
  892. Result.InitialValue:=aInitialValue;
  893. Result.Inherits:=aInherits;
  894. Result.All:=aAll;
  895. AddAttribute(Result);
  896. if not aAll then
  897. Insert(Result,NotAllAttributes,length(NotAllAttributes));
  898. end;
  899. function TCSSRegistry.FindAttribute(const aName: TCSSString
  900. ): TCSSAttributeDesc;
  901. begin
  902. Result:=TCSSAttributeDesc(FHashLists[nikAttribute].Find(aName));
  903. end;
  904. function TCSSRegistry.IndexOfAttributeName(const aName: TCSSString
  905. ): TCSSNumericalID;
  906. var
  907. Attr: TCSSAttributeDesc;
  908. begin
  909. Attr:=TCSSAttributeDesc(FHashLists[nikAttribute].Find(aName));
  910. if Attr<>nil then
  911. Result:=Attr.Index
  912. else
  913. Result:=-1;
  914. end;
  915. procedure TCSSRegistry.AddSplitLonghand(var AttrIDs: TCSSNumericalIDArray;
  916. var Values: TCSSStringArray; AttrID: TCSSNumericalID; const Value: TCSSString);
  917. begin
  918. System.Insert(AttrID,AttrIDs,length(AttrIDs));
  919. System.Insert(Value,Values,length(Values));
  920. end;
  921. procedure TCSSRegistry.AddSplitLonghandSides(var AttrIDs: TCSSNumericalIDArray;
  922. var Values: TCSSStringArray; TopID, RightID, BottomID, LeftID: TCSSNumericalID;
  923. const Found: TCSSStringArray);
  924. begin
  925. if length(Found)=0 then
  926. exit; // invalid
  927. Setlength(AttrIDs,4);
  928. Setlength(Values,4);
  929. AttrIDs[0]:=TopID;
  930. AttrIDs[1]:=RightID;
  931. AttrIDs[2]:=BottomID;
  932. AttrIDs[3]:=LeftID;
  933. // sets sides depending on how many values were passed:
  934. // 1: all four the same
  935. // 2: top and bottom | left and right
  936. // 3: top | left and right | bottom
  937. // 4: top | right | bottom | left
  938. case length(Found) of
  939. 1:
  940. begin
  941. Values[0]:=Found[0];
  942. Values[1]:=Found[0];
  943. Values[2]:=Found[0];
  944. Values[3]:=Found[0];
  945. end;
  946. 2:
  947. begin
  948. Values[0]:=Found[0];
  949. Values[1]:=Found[1];
  950. Values[2]:=Found[0];
  951. Values[3]:=Found[1];
  952. end;
  953. 3:
  954. begin
  955. Values[0]:=Found[0];
  956. Values[1]:=Found[1];
  957. Values[2]:=Found[2];
  958. Values[3]:=Found[1];
  959. end;
  960. 4:
  961. begin
  962. Values[0]:=Found[0];
  963. Values[1]:=Found[1];
  964. Values[2]:=Found[2];
  965. Values[3]:=Found[3];
  966. end;
  967. end;
  968. end;
  969. procedure TCSSRegistry.AddSplitLonghandCorners(var AttrIDs: TCSSNumericalIDArray;
  970. var Values: TCSSStringArray; TopLeftID, TopRightID, BottomLeftID, BottomRightID: TCSSNumericalID;
  971. const Found: TCSSStringArray);
  972. begin
  973. if length(Found)=0 then
  974. exit; // invalid
  975. Setlength(AttrIDs,4);
  976. Setlength(Values,4);
  977. AttrIDs[0]:=TopLeftID;
  978. AttrIDs[1]:=TopRightID;
  979. AttrIDs[2]:=BottomRightID;
  980. AttrIDs[3]:=BottomLeftID;
  981. // sets corners depending on how many values were passed:
  982. // 1: all four the same
  983. // 2: top-left-and-bottom-right | top-right-and-bottom-left
  984. // 3: top-left | top-right-and-bottom-left | bottom-right
  985. // 4: top-left | top-right | bottom-right | bottom-left
  986. case length(Found) of
  987. 1:
  988. begin
  989. Values[0]:=Found[0];
  990. Values[1]:=Found[0];
  991. Values[2]:=Found[0];
  992. Values[3]:=Found[0];
  993. end;
  994. 2:
  995. begin
  996. Values[0]:=Found[0];
  997. Values[1]:=Found[1];
  998. Values[2]:=Found[0];
  999. Values[3]:=Found[1];
  1000. end;
  1001. 3:
  1002. begin
  1003. Values[0]:=Found[0];
  1004. Values[1]:=Found[1];
  1005. Values[2]:=Found[2];
  1006. Values[3]:=Found[1];
  1007. end;
  1008. 4:
  1009. begin
  1010. Values[0]:=Found[0];
  1011. Values[1]:=Found[1];
  1012. Values[2]:=Found[2];
  1013. Values[3]:=Found[3];
  1014. end;
  1015. end;
  1016. end;
  1017. function TCSSRegistry.AddPseudoClass(aPseudo: TCSSPseudoClassDesc
  1018. ): TCSSPseudoClassDesc;
  1019. begin
  1020. Result:=aPseudo;
  1021. if aPseudo.Name='' then
  1022. raise ECSSParser.Create('missing name');
  1023. if FindPseudoClass(aPseudo.Name)<>nil then
  1024. raise ECSSParser.Create('duplicate pseudo class "'+aPseudo.Name+'"');
  1025. if PseudoClassCount=length(PseudoClasses) then
  1026. begin
  1027. if PseudoClassCount<32 then
  1028. SetLength(PseudoClasses,32)
  1029. else
  1030. SetLength(PseudoClasses,2*PseudoClassCount);
  1031. FillByte(PseudoClasses[PseudoClassCount],SizeOf(Pointer)*(length(PseudoClasses)-PseudoClassCount),0);
  1032. end;
  1033. PseudoClasses[PseudoClassCount]:=aPseudo;
  1034. aPseudo.Index:=PseudoClassCount;
  1035. FHashLists[nikPseudoClass].Add(aPseudo.Name,aPseudo);
  1036. inc(FPseudoClassCount);
  1037. ChangeStamp;
  1038. end;
  1039. function TCSSRegistry.AddPseudoClass(const aName: TCSSString;
  1040. aClass: TCSSPseudoClassDescClass): TCSSPseudoClassDesc;
  1041. begin
  1042. if aName='' then
  1043. raise ECSSParser.Create('missing name');
  1044. if FindPseudoClass(aName)<>nil then
  1045. raise ECSSParser.Create('duplicate pseudo class "'+aName+'"');
  1046. if aClass=nil then
  1047. aClass:=PseudoClass_ClassOf;
  1048. Result:=aClass.Create;
  1049. Result.Name:=aName;
  1050. AddPseudoClass(Result);
  1051. end;
  1052. function TCSSRegistry.FindPseudoClass(const aName: TCSSString
  1053. ): TCSSPseudoClassDesc;
  1054. begin
  1055. Result:=TCSSPseudoClassDesc(FHashLists[nikPseudoClass].Find(aName));
  1056. end;
  1057. function TCSSRegistry.IndexOfPseudoClassName(const aName: TCSSString
  1058. ): TCSSNumericalID;
  1059. var
  1060. aPseudo: TCSSPseudoClassDesc;
  1061. begin
  1062. aPseudo:=TCSSPseudoClassDesc(FHashLists[nikPseudoClass].Find(aName));
  1063. if aPseudo<>nil then
  1064. Result:=aPseudo.Index
  1065. else
  1066. Result:=-1;
  1067. end;
  1068. function TCSSRegistry.AddPseudoFunction(const aName: TCSSString
  1069. ): TCSSNumericalID;
  1070. begin
  1071. if aName='' then
  1072. raise ECSSParser.Create('missing name');
  1073. if length(aName)>255 then
  1074. raise ECSSParser.Create('pseudo function name too long');
  1075. if aName<>LowerCase(aName) then
  1076. raise ECSSParser.Create('pseudo function name not lowercase');
  1077. Result:=IndexOfKeyword(aName);
  1078. if Result>0 then
  1079. raise ECSSParser.Create('duplicate pseudo function "'+aName+'"');
  1080. if PseudoFunctionCount=length(PseudoFunctions) then
  1081. begin
  1082. if PseudoFunctionCount<32 then
  1083. SetLength(PseudoFunctions,32)
  1084. else
  1085. SetLength(PseudoFunctions,2*PseudoFunctionCount);
  1086. end;
  1087. Result:=PseudoFunctionCount;
  1088. PseudoFunctions[Result]:=aName;
  1089. FHashLists[nikPseudoFunction].Add(aName,{%H-}Pointer(Result));
  1090. inc(FPseudoFunctionCount);
  1091. ChangeStamp;
  1092. end;
  1093. function TCSSRegistry.IndexOfPseudoFunction(const aName: TCSSString
  1094. ): TCSSNumericalID;
  1095. var
  1096. p: Pointer;
  1097. begin
  1098. p:=FHashLists[nikPseudoFunction].Find(aName);
  1099. if p=nil then
  1100. exit(CSSIDNone)
  1101. else
  1102. Result:={%H-}TCSSNumericalID(p);
  1103. end;
  1104. function TCSSRegistry.AddType(aType: TCSSTypeDesc): TCSSTypeDesc;
  1105. begin
  1106. Result:=aType;
  1107. if aType.Name='' then
  1108. raise ECSSParser.Create('missing name');
  1109. if FindType(aType.Name)<>nil then
  1110. raise ECSSParser.Create('duplicate type "'+aType.Name+'"');
  1111. if TypeCount=length(Types) then
  1112. begin
  1113. if TypeCount<32 then
  1114. SetLength(Types,32)
  1115. else
  1116. SetLength(Types,2*TypeCount);
  1117. FillByte(Types[TypeCount],SizeOf(Pointer)*(length(Types)-TypeCount),0);
  1118. end;
  1119. Types[TypeCount]:=aType;
  1120. aType.Index:=TypeCount;
  1121. FHashLists[nikType].Add(aType.Name,aType);
  1122. inc(FTypeCount);
  1123. ChangeStamp;
  1124. end;
  1125. function TCSSRegistry.AddType(const aName: TCSSString; aClass: TCSSTypeDescClass
  1126. ): TCSSTypeDesc;
  1127. begin
  1128. if aName='' then
  1129. raise ECSSParser.Create('missing name');
  1130. if FindType(aName)<>nil then
  1131. raise ECSSParser.Create('duplicate type "'+aName+'"');
  1132. if aClass=nil then
  1133. aClass:=Type_ClassOf;
  1134. Result:=aClass.Create;
  1135. Result.Name:=aName;
  1136. AddType(Result);
  1137. end;
  1138. function TCSSRegistry.FindType(const aName: TCSSString): TCSSTypeDesc;
  1139. begin
  1140. Result:=TCSSTypeDesc(FHashLists[nikType].Find(aName));
  1141. end;
  1142. function TCSSRegistry.IndexOfTypeName(const aName: TCSSString): TCSSNumericalID;
  1143. var
  1144. aType: TCSSTypeDesc;
  1145. begin
  1146. aType:=TCSSTypeDesc(FHashLists[nikType].Find(aName));
  1147. if aType<>nil then
  1148. Result:=aType.Index
  1149. else
  1150. Result:=-1;
  1151. end;
  1152. function TCSSRegistry.AddKeyword(const aName: TCSSString): TCSSNumericalID;
  1153. begin
  1154. if aName='' then
  1155. raise ECSSParser.Create('missing name');
  1156. if length(aName)>255 then
  1157. raise ECSSParser.Create('keyword too long');
  1158. Result:=IndexOfKeyword(aName);
  1159. if Result>0 then
  1160. raise ECSSParser.Create('duplicate keyword "'+aName+'"');
  1161. if KeywordCount=length(Keywords) then
  1162. begin
  1163. if KeywordCount<32 then
  1164. SetLength(Keywords,32)
  1165. else
  1166. SetLength(Keywords,2*KeywordCount);
  1167. end;
  1168. Result:=KeywordCount;
  1169. Keywords[Result]:=aName;
  1170. FHashLists[nikKeyword].Add(aName,{%H-}Pointer(Result));
  1171. inc(FKeywordCount);
  1172. ChangeStamp;
  1173. end;
  1174. procedure TCSSRegistry.AddKeywords(const Names: TCSSStringArray; out First, Last: TCSSNumericalID);
  1175. var
  1176. i, NewCnt: integer;
  1177. begin
  1178. if Names=nil then begin
  1179. First:=CSSIDNone;
  1180. Last:=CSSIDNone;
  1181. exit;
  1182. end;
  1183. for i:=0 to length(Names)-1 do
  1184. if IndexOfKeyword(Names[i])>CSSIDNone then
  1185. raise Exception.Create('20240712215853');
  1186. NewCnt:=KeywordCount+length(Names);
  1187. if NewCnt>length(Keywords) then
  1188. begin
  1189. NewCnt:=(NewCnt div 32 +1) *32;
  1190. SetLength(Keywords,NewCnt);
  1191. end;
  1192. First:=KeywordCount;
  1193. for i:=0 to length(Names)-1 do
  1194. begin
  1195. Last:=KeywordCount;
  1196. Keywords[Last]:=Names[i];
  1197. FHashLists[nikKeyword].Add(Names[i],{%H-}Pointer(Last));
  1198. inc(FKeywordCount);
  1199. end;
  1200. ChangeStamp;
  1201. end;
  1202. function TCSSRegistry.IndexOfKeyword(const aName: TCSSString): TCSSNumericalID;
  1203. var
  1204. p: Pointer;
  1205. begin
  1206. p:=FHashLists[nikKeyword].Find(aName);
  1207. if p=nil then
  1208. exit(CSSIDNone)
  1209. else
  1210. Result:={%H-}TCSSNumericalID(p);
  1211. end;
  1212. procedure TCSSRegistry.AddColorKeywords;
  1213. var
  1214. Names: TCSSStringArray;
  1215. i: Integer;
  1216. begin
  1217. SetLength(Names{%H-},length(CSSNamedColors));
  1218. for i:=0 to High(CSSNamedColors) do
  1219. Names[i]:=CSSNamedColors[i].Name;
  1220. AddKeywords(Names,kwFirstColor,kwLastColor);
  1221. kwTransparent:=IndexOfKeyword('transparent');
  1222. end;
  1223. function TCSSRegistry.GetNamedColor(const aName: TCSSString): TCSSAlphaColor;
  1224. begin
  1225. Result:=GetKeywordColor(IndexOfKeyword(aName));
  1226. end;
  1227. function TCSSRegistry.GetKeywordColor(KeywordID: TCSSNumericalID): TCSSAlphaColor;
  1228. begin
  1229. if (KeywordID<kwFirstColor) or (KeywordID>kwLastColor) then
  1230. Result:=$ff000000
  1231. else
  1232. Result:=CSSNamedColors[KeywordID-kwFirstColor].Color;
  1233. end;
  1234. function TCSSRegistry.AddAttrFunction(const aName: TCSSString): TCSSNumericalID;
  1235. begin
  1236. if aName='' then
  1237. raise ECSSParser.Create('missing name');
  1238. if length(aName)>255 then
  1239. raise ECSSParser.Create('function name too long');
  1240. Result:=IndexOfAttrFunction(aName);
  1241. if Result>0 then
  1242. raise ECSSParser.Create('duplicate attribute function "'+aName+'"');
  1243. if AttrFunctionCount=length(AttrFunctions) then
  1244. begin
  1245. if AttrFunctionCount<32 then
  1246. SetLength(AttrFunctions,32)
  1247. else
  1248. SetLength(AttrFunctions,2*AttrFunctionCount);
  1249. end;
  1250. Result:=AttrFunctionCount;
  1251. AttrFunctions[Result]:=aName;
  1252. FHashLists[nikAttrFunction].Add(aName,{%H-}Pointer(Result));
  1253. inc(FAttrFunctionCount);
  1254. ChangeStamp;
  1255. end;
  1256. function TCSSRegistry.IndexOfAttrFunction(const aName: TCSSString
  1257. ): TCSSNumericalID;
  1258. var
  1259. p: Pointer;
  1260. begin
  1261. p:=FHashLists[nikAttrFunction].Find(aName);
  1262. if p=nil then
  1263. exit(CSSIDNone)
  1264. else
  1265. Result:={%H-}TCSSNumericalID(p);
  1266. end;
  1267. { TCSSResolvedCallElement }
  1268. destructor TCSSResolvedCallElement.Destroy;
  1269. begin
  1270. FreeAndNil(Params);
  1271. inherited Destroy;
  1272. end;
  1273. { TCSSResCompValue }
  1274. function TCSSResCompValue.AsString: TCSSString;
  1275. var
  1276. l: integer;
  1277. begin
  1278. if (StartP=nil) or (EndP=nil) or (EndP<=StartP) then
  1279. exit('');
  1280. l:=EndP-StartP;
  1281. SetLength(Result,l);
  1282. Move(StartP^,Result[1],l);
  1283. end;
  1284. function TCSSResCompValue.FloatAsString: TCSSString;
  1285. begin
  1286. Result:=FloatToCSSStr(Float)+CSSUnitNames[FloatUnit];
  1287. end;
  1288. { TCSSCheckAttrParams_Dimension }
  1289. function TCSSCheckAttrParams_Dimension.Fits(const ResValue: TCSSResCompValue): boolean;
  1290. var
  1291. i: Integer;
  1292. begin
  1293. Result:=false;
  1294. case ResValue.Kind of
  1295. rvkFloat:
  1296. if ResValue.FloatUnit in AllowedUnits then
  1297. begin
  1298. if not (ResValue.FloatUnit in AllowedUnits) then exit;
  1299. if (not AllowNegative) and (ResValue.Float<0) then exit;
  1300. if (not AllowFrac) and (Frac(ResValue.Float)>0) then exit;
  1301. exit(true);
  1302. end else if (ResValue.FloatUnit=cuNone) and (ResValue.Float=0) then
  1303. exit(true);
  1304. rvkKeyword:
  1305. for i:=0 to length(AllowedKeywordIDs)-1 do
  1306. if ResValue.KeywordID=AllowedKeywordIDs[i] then
  1307. exit(true);
  1308. end;
  1309. end;
  1310. { TCSSBaseResolver }
  1311. procedure TCSSBaseResolver.SetCSSRegistry(const AValue: TCSSRegistry);
  1312. begin
  1313. if FCSSRegistry=AValue then Exit;
  1314. FCSSRegistry:=AValue;
  1315. end;
  1316. function TCSSBaseResolver.InitParseAttr(Desc: TCSSAttributeDesc; AttrData: TCSSAttributeKeyData;
  1317. const Value: TCSSString): boolean;
  1318. var
  1319. p: PCSSChar;
  1320. begin
  1321. Result:=false;
  1322. CurAttrData:=AttrData;
  1323. CurDesc:=Desc;
  1324. CurValue:=Value;
  1325. CurComp:=Default(TCSSResCompValue);
  1326. CurComp.EndP:=PCSSChar(CurValue);
  1327. if not ReadNext then
  1328. begin
  1329. if CurAttrData<>nil then
  1330. CurAttrData.Invalid:=true;
  1331. exit;
  1332. end;
  1333. if (CurAttrData<>nil) and (CurComp.Kind=rvkKeyword)
  1334. and IsBaseKeyword(CurComp.KeywordID) then
  1335. begin
  1336. p:=CurComp.EndP;
  1337. while (p^ in Whitespace) do inc(p);
  1338. if p^>#0 then
  1339. begin
  1340. // "inherit" must be alone
  1341. CurAttrData.Invalid:=true;
  1342. exit;
  1343. end;
  1344. CurAttrData.Complete:=true;
  1345. end;
  1346. Result:=true;
  1347. end;
  1348. procedure TCSSBaseResolver.InitParseAttr(const Value: TCSSString);
  1349. begin
  1350. CurValue:=Value;
  1351. CurComp:=Default(TCSSResCompValue);
  1352. CurComp.EndP:=PCSSChar(CurValue);
  1353. ReadNext;
  1354. end;
  1355. function TCSSBaseResolver.CheckAttribute_Keyword(const AllowedKeywordIDs: TCSSNumericalIDArray
  1356. ): boolean;
  1357. begin
  1358. Result:=ReadAttribute_Keyword(CurAttrData.Invalid,AllowedKeywordIDs);
  1359. end;
  1360. function TCSSBaseResolver.CheckAttribute_CommaList_Keyword(
  1361. const AllowedKeywordIDs: TCSSNumericalIDArray): boolean;
  1362. var
  1363. i: Integer;
  1364. Fits: Boolean;
  1365. begin
  1366. CurAttrData.Invalid:=true;
  1367. repeat
  1368. Fits:=false;
  1369. case CurComp.Kind of
  1370. rvkKeyword:
  1371. for i:=0 to length(AllowedKeywordIDs)-1 do
  1372. if CurComp.KeywordID=AllowedKeywordIDs[i] then
  1373. begin
  1374. Fits:=true;
  1375. break;
  1376. end;
  1377. rvkFunction:
  1378. begin
  1379. // todo: check for allowed functions
  1380. Fits:=true;
  1381. end;
  1382. end;
  1383. if not Fits then exit;
  1384. if not ReadNext then
  1385. begin
  1386. // ok
  1387. CurAttrData.Invalid:=false;
  1388. exit(true);
  1389. end;
  1390. if (CurComp.Kind<>rvkSymbol) or (CurComp.Symbol=ctkCOMMA) then
  1391. exit;
  1392. until not ReadNext;
  1393. Result:=false;
  1394. end;
  1395. function TCSSBaseResolver.CheckAttribute_Dimension(const Params: TCSSCheckAttrParams_Dimension
  1396. ): boolean;
  1397. begin
  1398. Result:=ReadAttribute_Dimension(CurAttrData.Invalid,Params);
  1399. end;
  1400. function TCSSBaseResolver.CheckAttribute_Color(const AllowedKeywordIDs: TCSSNumericalIDArray
  1401. ): boolean;
  1402. begin
  1403. Result:=ReadAttribute_Color(CurAttrData.Invalid,AllowedKeywordIDs);
  1404. end;
  1405. function TCSSBaseResolver.ReadNext: boolean;
  1406. begin
  1407. Result:=ReadComp(CurComp);
  1408. end;
  1409. function TCSSBaseResolver.ReadAttribute_Keyword(out Invalid: boolean;
  1410. const AllowedKeywordIDs: TCSSNumericalIDArray): boolean;
  1411. var
  1412. i: Integer;
  1413. begin
  1414. Invalid:=false;
  1415. repeat
  1416. case CurComp.Kind of
  1417. rvkKeyword:
  1418. for i:=0 to length(AllowedKeywordIDs)-1 do
  1419. if CurComp.KeywordID=AllowedKeywordIDs[i] then
  1420. exit(true);
  1421. end;
  1422. // todo: warn if invalid
  1423. until not ReadNext;
  1424. Invalid:=true;
  1425. Result:=false;
  1426. end;
  1427. function TCSSBaseResolver.ReadAttribute_Dimension(out Invalid: boolean;
  1428. const Params: TCSSCheckAttrParams_Dimension): boolean;
  1429. var
  1430. i: Integer;
  1431. begin
  1432. Invalid:=true;
  1433. repeat
  1434. case CurComp.Kind of
  1435. rvkFloat:
  1436. if Params.Fits(CurComp) then
  1437. exit(true);
  1438. rvkKeyword:
  1439. for i:=0 to length(Params.AllowedKeywordIDs)-1 do
  1440. if CurComp.KeywordID=Params.AllowedKeywordIDs[i] then
  1441. exit(true);
  1442. end;
  1443. // todo: warn if invalid
  1444. until not ReadNext;
  1445. Invalid:=true;
  1446. Result:=false;
  1447. end;
  1448. function TCSSBaseResolver.ReadAttribute_Color(out Invalid: boolean;
  1449. const AllowedKeywordIDs: TCSSNumericalIDArray): boolean;
  1450. var
  1451. i: Integer;
  1452. begin
  1453. Invalid:=false;
  1454. repeat
  1455. case CurComp.Kind of
  1456. rvkKeyword:
  1457. begin
  1458. if (CurComp.KeywordID>=CSSRegistry.kwFirstColor)
  1459. and (CurComp.KeywordID<=CSSRegistry.kwLastColor)
  1460. then
  1461. exit(true);
  1462. for i:=0 to length(AllowedKeywordIDs)-1 do
  1463. if CurComp.KeywordID=AllowedKeywordIDs[i] then
  1464. exit(true);
  1465. end;
  1466. rvkFunction:
  1467. begin
  1468. // todo: check for allowed functions
  1469. end;
  1470. rvkHexColor:
  1471. exit(true);
  1472. end;
  1473. // todo: warn if invalid
  1474. until not ReadNext;
  1475. Invalid:=true;
  1476. Result:=false;
  1477. end;
  1478. function TCSSBaseResolver.ReadComp(var aComp: TCSSResCompValue): boolean;
  1479. begin
  1480. Result:=ReadValue(aComp);
  1481. ReadWordID(aComp);
  1482. end;
  1483. procedure TCSSBaseResolver.ReadWordID(var aComp: TCSSResCompValue);
  1484. var
  1485. Identifier: TCSSString;
  1486. begin
  1487. case aComp.Kind of
  1488. rvkFunctionUnknown:
  1489. begin
  1490. SetString(Identifier,aComp.StartP,aComp.EndP-aComp.StartP);
  1491. aComp.FunctionID:=CSSRegistry.IndexOfAttrFunction(Identifier);
  1492. if aComp.FunctionID>CSSIDNone then
  1493. aComp.Kind:=rvkFunction;
  1494. end;
  1495. rvkKeywordUnknown:
  1496. begin
  1497. SetString(Identifier,aComp.StartP,aComp.EndP-aComp.StartP);
  1498. aComp.KeywordID:=CSSRegistry.IndexOfKeyword(Identifier);
  1499. if aComp.KeywordID>CSSIDNone then
  1500. aComp.Kind:=rvkKeyword;
  1501. end;
  1502. end;
  1503. end;
  1504. class function TCSSBaseResolver.ReadValue(var aComp: TCSSResCompValue): boolean;
  1505. var
  1506. c: TCSSChar;
  1507. p: PCSSChar;
  1508. l: SizeInt;
  1509. procedure SetSymbol(s: TCSSToken);
  1510. begin
  1511. aComp.Kind:=rvkSymbol;
  1512. aComp.Symbol:=s;
  1513. aComp.EndP:=p+1;
  1514. end;
  1515. begin
  1516. Result:=true;
  1517. aComp.Kind:=rvkNone;
  1518. p:=aComp.EndP;
  1519. // skip whitespace
  1520. while (p^ in Whitespace) do inc(p);
  1521. aComp.StartP:=p;
  1522. aComp.EndP:=p;
  1523. c:=p^;
  1524. case c of
  1525. #0: exit(false);
  1526. '0'..'9':
  1527. if ReadNumber(aComp) then exit;
  1528. ',':
  1529. begin
  1530. SetSymbol(ctkCOMMA);
  1531. exit;
  1532. end;
  1533. ')':
  1534. begin
  1535. SetSymbol(ctkRPARENTHESIS);
  1536. exit;
  1537. end;
  1538. '+':
  1539. case p[1] of
  1540. '0'..'9','.':
  1541. if ReadNumber(aComp) then exit;
  1542. #0,#9,#10,#13,' ':
  1543. begin
  1544. SetSymbol(ctkPLUS);
  1545. exit;
  1546. end;
  1547. end;
  1548. '-':
  1549. case p[1] of
  1550. '0'..'9','.':
  1551. if ReadNumber(aComp) then exit;
  1552. 'a'..'z','A'..'Z','-':
  1553. if ReadIdentifier(aComp) then exit;
  1554. #0,#9,#10,#13,' ':
  1555. begin
  1556. SetSymbol(ctkMINUS);
  1557. exit;
  1558. end;
  1559. end;
  1560. '.':
  1561. case p[1] of
  1562. '0'..'9':
  1563. if ReadNumber(aComp) then exit;
  1564. else
  1565. SetSymbol(ctkDOT);
  1566. exit;
  1567. end;
  1568. '*':
  1569. begin
  1570. if p[1]='=' then
  1571. begin
  1572. inc(p);
  1573. SetSymbol(ctkSTAREQUAL);
  1574. end else
  1575. SetSymbol(ctkSTAR);
  1576. exit;
  1577. end;
  1578. '/':
  1579. begin
  1580. SetSymbol(ctkDIV);
  1581. exit;
  1582. end;
  1583. ':':
  1584. begin
  1585. SetSymbol(ctkCOLON);
  1586. exit;
  1587. end;
  1588. ';':
  1589. begin
  1590. SetSymbol(ctkSEMICOLON);
  1591. exit;
  1592. end;
  1593. 'a'..'z','A'..'Z':
  1594. if ReadIdentifier(aComp) then exit;
  1595. '#':
  1596. begin
  1597. inc(p);
  1598. while p^ in Hex do inc(p);
  1599. l:=p-aComp.StartP;
  1600. case l of
  1601. 4,5,7,9:
  1602. begin
  1603. // #rgb, #rgba, #rrggbb, #rrggbbaa
  1604. aComp.Kind:=rvkHexColor;
  1605. aComp.EndP:=p;
  1606. exit;
  1607. end;
  1608. end;
  1609. end;
  1610. end;
  1611. // skip unknown aComp
  1612. aComp.Kind:=rvkInvalid;
  1613. repeat
  1614. if p^ in ValEnd then break;
  1615. case p^ of
  1616. '(','[': SkipBrackets(p);
  1617. '''','"': SkipString(p);
  1618. else inc(p);
  1619. end;
  1620. until false;
  1621. aComp.EndP:=p;
  1622. end;
  1623. class function TCSSBaseResolver.ReadNumber(var aComp: TCSSResCompValue): boolean;
  1624. var
  1625. Negative, HasNumber: Boolean;
  1626. Divisor: double;
  1627. Exponent: Integer;
  1628. d: Float;
  1629. U: TCSSUnit;
  1630. StartP, p: PCSSChar;
  1631. begin
  1632. Result:=false;
  1633. aComp.Kind:=rvkInvalid;
  1634. p:=aComp.StartP;
  1635. // number: 1, 0.2, .3, 4.01, 0.0, +0.0, -0.0, .50, 2e3, -6.7E-2
  1636. if p^='-' then
  1637. begin
  1638. Negative:=true;
  1639. inc(p);
  1640. end else begin
  1641. Negative:=false;
  1642. if p^='+' then
  1643. inc(p);
  1644. end;
  1645. HasNumber:=false;
  1646. aComp.Float:=0;
  1647. if p^ in Num then
  1648. begin
  1649. // read significand
  1650. HasNumber:=true;
  1651. repeat
  1652. aComp.Float:=aComp.Float*10+ord(p^)-ord('0');
  1653. if aComp.Float>CSSMaxSafeIntDouble then
  1654. exit; // loosing precision
  1655. inc(p);
  1656. until not (p^ in Num);
  1657. end;
  1658. if p^='.' then
  1659. begin
  1660. // read fraction
  1661. inc(p);
  1662. if not (p^ in Num) then exit;
  1663. Divisor:=1;
  1664. repeat
  1665. Divisor:=Divisor*10;
  1666. aComp.Float:=aComp.Float*10+ord(p^)-ord('0');
  1667. if (Divisor>CSSMaxSafeIntDouble)
  1668. or (aComp.Float>CSSMaxSafeIntDouble) then
  1669. exit; // loosing precision
  1670. inc(p);
  1671. until not (p^ in Num);
  1672. aComp.Float:=aComp.Float/Divisor;
  1673. end else if not HasNumber then
  1674. exit;
  1675. if Negative then
  1676. aComp.Float:=-aComp.Float;
  1677. if (p^ in ['e','E']) and not (p[1] in ['a'..'z']) then
  1678. begin
  1679. inc(p);
  1680. if p^='-' then
  1681. begin
  1682. Negative:=true;
  1683. inc(p);
  1684. end else begin
  1685. Negative:=false;
  1686. if p^='+' then
  1687. inc(p);
  1688. end;
  1689. if not (p^ in Num) then exit;
  1690. Exponent:=0;
  1691. repeat
  1692. inc(p);
  1693. Exponent:=Exponent*10+ord(p^)-ord('0');
  1694. if Exponent>2047 then
  1695. exit; // out of bounds
  1696. until not (p^ in Num);
  1697. if Exponent>0 then
  1698. begin
  1699. if Negative then
  1700. Exponent:=-Exponent;
  1701. try
  1702. d:=Power(10,Exponent);
  1703. aComp.Float:=aComp.Float*d;
  1704. except
  1705. exit;
  1706. end;
  1707. end;
  1708. end;
  1709. aComp.Kind:=rvkFloat;
  1710. // parse unit
  1711. U:=cuNone;
  1712. case p^ of
  1713. '%':
  1714. begin
  1715. inc(p);
  1716. U:=cuPercent;
  1717. end;
  1718. 'a'..'z','A'..'Z':
  1719. begin
  1720. StartP:=p;
  1721. while p^ in Alpha do inc(p);
  1722. U:=high(TCSSUnit);
  1723. while (U>cuNone) and not CompareMem(StartP,PChar(CSSUnitNames[U]),length(CSSUnitNames[U])) do
  1724. U:=pred(U);
  1725. if U=cuNone then
  1726. exit; // unknown unit
  1727. end;
  1728. end;
  1729. aComp.FloatUnit:=U;
  1730. aComp.EndP:=p;
  1731. Result:=true;
  1732. //writeln('TCSSBaseResolver.ReadNumber "',p,'" Value=',FloatToCSSStr(aComp.Float),' U=',U,' Kind=',aComp.Kind,' Result=',Result);
  1733. end;
  1734. class function TCSSBaseResolver.ReadIdentifier(var aComp: TCSSResCompValue): boolean;
  1735. var
  1736. IsFunc: Boolean;
  1737. p: PCSSChar;
  1738. begin
  1739. Result:=false;
  1740. aComp.Kind:=rvkInvalid;
  1741. p:=aComp.EndP;
  1742. if not (p^ in AlIden) then exit;
  1743. repeat
  1744. inc(p);
  1745. until not (p^ in AlNumIden);
  1746. IsFunc:=p^='(';
  1747. if IsFunc then
  1748. begin
  1749. // function call
  1750. aComp.Kind:=rvkFunctionUnknown;
  1751. aComp.BracketOpen:=p;
  1752. if not SkipBrackets(p) then
  1753. begin
  1754. aComp.EndP:=p;
  1755. exit;
  1756. end;
  1757. end else
  1758. aComp.Kind:=rvkKeywordUnknown;
  1759. aComp.EndP:=p;
  1760. Result:=true;
  1761. end;
  1762. function TCSSBaseResolver.IsBaseKeyword(KeywordID: TCSSNumericalID): boolean;
  1763. begin
  1764. Result:=(KeywordID>=CSSKeywordInitial) and (KeywordID<=CSSKeywordRevertLayer);
  1765. end;
  1766. function TCSSBaseResolver.IsKeywordIn(aKeywordID: TCSSNumericalID;
  1767. const KeywordIDs: TCSSNumericalIDArray): boolean;
  1768. var
  1769. i: Integer;
  1770. begin
  1771. for i:=0 to length(KeywordIDs)-1 do
  1772. if KeywordIDs[i]=aKeywordID then
  1773. exit(true);
  1774. Result:=false;
  1775. end;
  1776. function TCSSBaseResolver.IsKeywordIn(const KeywordIDs: TCSSNumericalIDArray): boolean;
  1777. var
  1778. aKeywordID: TCSSNumericalID;
  1779. i: Integer;
  1780. begin
  1781. Result:=false;
  1782. if CurComp.Kind<>rvkKeyword then exit;
  1783. aKeywordID:=CurComp.KeywordID;
  1784. for i:=0 to length(KeywordIDs)-1 do
  1785. if KeywordIDs[i]=aKeywordID then
  1786. exit(true);
  1787. end;
  1788. function TCSSBaseResolver.IsLengthOrPercentage(AllowNegative: boolean): boolean;
  1789. begin
  1790. Result:=false;
  1791. case CurComp.Kind of
  1792. rvkFloat:
  1793. if CurComp.FloatUnit in cuAllLengthsAndPercent then
  1794. begin
  1795. if (not AllowNegative) and (CurComp.Float<0) then exit;
  1796. exit(true);
  1797. end
  1798. else if (CurComp.FloatUnit=cuNone) and (CurComp.Float=0) then
  1799. exit(true); // 0 without unit is allowed
  1800. end;
  1801. end;
  1802. function TCSSBaseResolver.IsLengthOrPercentage(const ResValue: TCSSResCompValue;
  1803. AllowNegative: boolean): boolean;
  1804. begin
  1805. Result:=false;
  1806. case ResValue.Kind of
  1807. rvkFloat:
  1808. if ResValue.FloatUnit in cuAllLengthsAndPercent then
  1809. begin
  1810. if (not AllowNegative) and (ResValue.Float<0) then exit;
  1811. exit(true);
  1812. end
  1813. else if (ResValue.FloatUnit=cuNone) and (ResValue.Float=0) then
  1814. exit(true);
  1815. end;
  1816. end;
  1817. function TCSSBaseResolver.IsSymbol(Token: TCSSToken): boolean;
  1818. begin
  1819. Result:=(CurComp.Kind=rvkSymbol) and (CurComp.Symbol=Token);
  1820. end;
  1821. function TCSSBaseResolver.GetCompString: TCSSString;
  1822. var
  1823. StartP: PCSSChar;
  1824. begin
  1825. if CurComp.Kind=rvkKeyword then
  1826. exit(CSSRegistry.Keywords[CurComp.KeywordID]);
  1827. StartP:=CurComp.StartP;
  1828. if (StartP=PCSSChar(CurValue)) and (CurComp.EndP-StartP = length(CurValue)) then
  1829. Result:=CurValue
  1830. else
  1831. SetString(Result,StartP,CurComp.EndP-StartP);
  1832. end;
  1833. function TCSSBaseResolver.GetCompString(const aValue: string; const ResValue: TCSSResCompValue
  1834. ): TCSSString;
  1835. var
  1836. Start: PCSSChar;
  1837. begin
  1838. if ResValue.Kind=rvkKeyword then
  1839. exit(CSSRegistry.Keywords[ResValue.KeywordID]);
  1840. Start:=ResValue.StartP;
  1841. if (Start=PCSSChar(aValue)) and (ResValue.EndP-Start = length(aValue)) then
  1842. Result:=aValue
  1843. else
  1844. SetString(Result,Start,ResValue.EndP-Start);
  1845. end;
  1846. class procedure TCSSBaseResolver.SkipToEndOfAttribute(var p: PCSSChar);
  1847. begin
  1848. repeat
  1849. case p^ of
  1850. #0,'{','}',';': exit;
  1851. '''','"': SkipString(p);
  1852. else inc(p);
  1853. end;
  1854. until false;
  1855. end;
  1856. class function TCSSBaseResolver.SkipString(var p: PCSSChar): boolean;
  1857. var
  1858. Delim, c: TCSSChar;
  1859. begin
  1860. Result:=false;
  1861. Delim:=p^;
  1862. repeat
  1863. inc(p);
  1864. c:=p^;
  1865. if c=Delim then
  1866. begin
  1867. inc(p);
  1868. exit(true);
  1869. end else if c=#0 then
  1870. exit
  1871. else
  1872. inc(p);
  1873. until false;
  1874. end;
  1875. class function TCSSBaseResolver.SkipBrackets(var p: PCSSChar; Lvl: integer): boolean;
  1876. const
  1877. CSSMaxBracketLvl = 10;
  1878. var
  1879. CloseBracket: TCSSChar;
  1880. begin
  1881. Result:=false;
  1882. if Lvl>CSSMaxBracketLvl then
  1883. begin
  1884. SkipToEndOfAttribute(p);
  1885. exit;
  1886. end;
  1887. if p^='[' then
  1888. CloseBracket:=']'
  1889. else
  1890. CloseBracket:=')';
  1891. repeat
  1892. inc(p);
  1893. case p^ of
  1894. #0,'{','}',';': exit;
  1895. '''','"': SkipString(p);
  1896. '(','[': SkipBrackets(p,Lvl+1);
  1897. ')',']':
  1898. if p^=CloseBracket then
  1899. begin
  1900. inc(p);
  1901. exit(true);
  1902. end else begin
  1903. SkipToEndOfAttribute(p);
  1904. exit;
  1905. end;
  1906. end;
  1907. until false;
  1908. end;
  1909. function TCSSBaseResolver.GetAttributeID(const aName: TCSSString; AutoCreate: boolean
  1910. ): TCSSNumericalID;
  1911. begin
  1912. Result:=CSSRegistry.IndexOfAttributeName(aName);
  1913. if AutoCreate then ;
  1914. end;
  1915. function TCSSBaseResolver.GetAttributeDesc(AttrID: TCSSNumericalID): TCSSAttributeDesc;
  1916. begin
  1917. if (AttrID>0) and (AttrID<CSSRegistry.AttributeCount) then
  1918. Result:=CSSRegistry.Attributes[AttrID]
  1919. else
  1920. Result:=nil;
  1921. end;
  1922. function TCSSBaseResolver.GetTypeID(const aName: TCSSString): TCSSNumericalID;
  1923. begin
  1924. Result:=CSSRegistry.IndexOfTypeName(aName);
  1925. end;
  1926. function TCSSBaseResolver.GetPseudoClassID(const aName: TCSSString): TCSSNumericalID;
  1927. begin
  1928. Result:=CSSRegistry.IndexOfPseudoClassName(aName);
  1929. end;
  1930. function TCSSBaseResolver.GetPseudoFunctionID(const aName: TCSSString): TCSSNumericalID;
  1931. begin
  1932. Result:=CSSRegistry.IndexOfPseudoFunction(aName);
  1933. end;
  1934. { TCSSResolverParser }
  1935. function TCSSResolverParser.ResolveAttribute(El: TCSSResolvedIdentifierElement): TCSSNumericalID;
  1936. var
  1937. aName: TCSSString;
  1938. begin
  1939. if El.NumericalID<>CSSIDNone then
  1940. raise Exception.Create('20240701143234');
  1941. aName:=El.Name;
  1942. El.Kind:=nikAttribute;
  1943. Result:=Resolver.GetAttributeID(aName,true);
  1944. if Result<=CSSIDNone then
  1945. begin
  1946. El.NumericalID:=-1;
  1947. Log(etWarning,20240822172823,'unknown attribute "'+aName+'"',El);
  1948. end else
  1949. El.NumericalID:=Result;
  1950. end;
  1951. function TCSSResolverParser.ResolveType(El: TCSSResolvedIdentifierElement): TCSSNumericalID;
  1952. var
  1953. aName: TCSSString;
  1954. begin
  1955. if El.NumericalID<>CSSIDNone then
  1956. raise Exception.Create('20240822133813');
  1957. aName:=El.Name;
  1958. El.Kind:=nikType;
  1959. Result:=Resolver.GetTypeID(aName);
  1960. if Result<=CSSIDNone then
  1961. begin
  1962. El.NumericalID:=-1;
  1963. Log(etWarning,20240822133816,'unknown type "'+aName+'"',El);
  1964. end else
  1965. El.NumericalID:=Result;
  1966. end;
  1967. function TCSSResolverParser.ResolvePseudoClass(
  1968. El: TCSSResolvedPseudoClassElement): TCSSNumericalID;
  1969. var
  1970. aName: TCSSString;
  1971. begin
  1972. aName:=El.Name;
  1973. // pseudo classes are ASCII case insensitive
  1974. System.Delete(aName,1,1);
  1975. aName:=lowercase(aName);
  1976. if El.NumericalID<>CSSIDNone then
  1977. raise Exception.Create('20240701143234');
  1978. El.Kind:=nikPseudoClass;
  1979. Result:=Resolver.GetPseudoClassID(aName);
  1980. //writeln('TCSSResolverParser.ResolvePseudoClass ',aName,' ID=',Result);
  1981. if Result<=CSSIDNone then
  1982. begin
  1983. El.NumericalID:=-1;
  1984. Log(etWarning,20240822172826,'unknown pseudo class "'+aName+'"',El);
  1985. end else
  1986. El.NumericalID:=Result;
  1987. end;
  1988. function TCSSResolverParser.ResolvePseudoFunction(El: TCSSResolvedCallElement
  1989. ): TCSSNumericalID;
  1990. var
  1991. aName: TCSSString;
  1992. begin
  1993. if El.NameNumericalID<>CSSIDNone then
  1994. raise Exception.Create('20240701143035');
  1995. aName:=El.Name;
  1996. if aName[1]<>':' then
  1997. raise Exception.Create('20240701143650');
  1998. // pseudo functions are ASCII case insensitive
  1999. System.Delete(aName,1,1);
  2000. aName:=lowercase(aName);
  2001. El.Kind:=nikPseudoFunction;
  2002. Result:=Resolver.GetPseudoFunctionID(aName);
  2003. //writeln('TCSSResolverParser.ResolvePseudoFunction ',aName,' ID=',Result);
  2004. if Result<=CSSIDNone then
  2005. begin
  2006. El.NameNumericalID:=-1;
  2007. Log(etWarning,20240822172830,'unknown pseudo function "'+aName+'"',El);
  2008. end else
  2009. El.NameNumericalID:=Result;
  2010. end;
  2011. function TCSSResolverParser.ParseCall(aName: TCSSString; IsSelector: boolean
  2012. ): TCSSCallElement;
  2013. var
  2014. CallID: TCSSNumericalID;
  2015. begin
  2016. Result:=inherited ParseCall(aName, IsSelector);
  2017. if IsSelector then
  2018. begin
  2019. if Result.Name[1]=':' then
  2020. begin
  2021. CallID:=ResolvePseudoFunction(TCSSResolvedCallElement(Result));
  2022. case CallID of
  2023. CSSCallID_NthChild,CSSCallID_NthLastChild,
  2024. CSSCallID_NthOfType,CSSCallID_NthLastOfType: CheckNthChildParams(TCSSResolvedCallElement(Result));
  2025. end;
  2026. end
  2027. else
  2028. Log(etWarning,20240701222744,'invalid selector function',Result);
  2029. end;
  2030. end;
  2031. function TCSSResolverParser.ParseDeclaration(aIsAt: Boolean
  2032. ): TCSSDeclarationElement;
  2033. var
  2034. aKey: TCSSElement;
  2035. AttrId: TCSSNumericalID;
  2036. Desc: TCSSAttributeDesc;
  2037. AttrData: TCSSAttributeKeyData;
  2038. i, ChildCnt: Integer;
  2039. begin
  2040. Result:=inherited ParseDeclaration(aIsAt);
  2041. if Result.KeyCount<>1 then
  2042. begin
  2043. if Result.KeyCount<1 then
  2044. Log(etWarning,20231112135955,'missing keys in declaration',Result);
  2045. if Result.KeyCount>1 then
  2046. Log(etWarning,20231112140722,'too many keys in declaration',Result);
  2047. exit;
  2048. end;
  2049. aKey:=Result.Keys[0];
  2050. if aKey is TCSSResolvedIdentifierElement then
  2051. begin
  2052. AttrId:=ResolveAttribute(TCSSResolvedIdentifierElement(aKey));
  2053. if aKey.CustomData<>nil then
  2054. raise Exception.Create('20240626113536');
  2055. AttrData:=CSSAttributeKeyDataClass.Create;
  2056. aKey.CustomData:=AttrData;
  2057. ChildCnt:=Result.ChildCount;
  2058. if ChildCnt=0 then
  2059. begin
  2060. AttrData.Invalid:=true;
  2061. exit;
  2062. end;
  2063. for i:=0 to ChildCnt-1 do
  2064. begin
  2065. if (i>0) then
  2066. AttrData.Value+=', ';
  2067. AttrData.Value+=Result.Children[i].AsString;
  2068. end;
  2069. if AttrId>=CSSAttributeID_All then
  2070. begin
  2071. Desc:=Resolver.GetAttributeDesc(AttrId);
  2072. if Pos('var(',AttrData.Value)>0 then
  2073. begin
  2074. // cannot be parsed yet
  2075. end else if AttrID<Resolver.CSSRegistry.AttributeCount then
  2076. begin
  2077. if Resolver.InitParseAttr(Desc,AttrData,AttrData.Value) then
  2078. begin
  2079. if Assigned(Desc.OnCheck) then
  2080. AttrData.Invalid:=not Desc.OnCheck(Resolver);
  2081. end;
  2082. end;
  2083. {$IFDEF VerboseCSSResolver}
  2084. if AttrData.Invalid then
  2085. Log(etWarning,20240710162400,'Invalid CSS attribute value: '+Result.AsString,aKey);
  2086. {$ENDIF}
  2087. end;
  2088. end else begin
  2089. Log(etWarning,20220908230855,'Expected property name, but found '+aKey.ClassName,aKey);
  2090. end;
  2091. end;
  2092. function TCSSResolverParser.ParseSelector: TCSSElement;
  2093. begin
  2094. Result:=inherited ParseSelector;
  2095. CheckSelector(Result);
  2096. end;
  2097. procedure TCSSResolverParser.CheckSelector(El: TCSSElement);
  2098. var
  2099. C: TClass;
  2100. begin
  2101. C:=El.ClassType;
  2102. if C=TCSSResolvedIdentifierElement then
  2103. // e.g. div {}
  2104. ResolveType(TCSSResolvedIdentifierElement(El))
  2105. else if C=TCSSHashIdentifierElement then
  2106. // e.g. #id {}
  2107. else if C=TCSSClassNameElement then
  2108. // e.g. .classname {}
  2109. else if C=TCSSResolvedPseudoClassElement then
  2110. // e.g. :pseudoclass {}
  2111. ResolvePseudoClass(TCSSResolvedPseudoClassElement(El))
  2112. else if C=TCSSBinaryElement then
  2113. CheckSelectorBinary(TCSSBinaryElement(El))
  2114. else if C=TCSSArrayElement then
  2115. CheckSelectorArray(TCSSArrayElement(El))
  2116. else if C=TCSSListElement then
  2117. CheckSelectorList(TCSSListElement(El))
  2118. else if C=TCSSResolvedCallElement then
  2119. // checked via ParseCall
  2120. else
  2121. Log(etWarning,20240625131810,'Unknown CSS selector element',El);
  2122. end;
  2123. procedure TCSSResolverParser.CheckSelectorArray(anArray: TCSSArrayElement);
  2124. var
  2125. {$IFDEF VerboseCSSResolver}
  2126. i: integer;
  2127. {$ENDIF}
  2128. El: TCSSElement;
  2129. C: TClass;
  2130. aValue: TCSSString;
  2131. begin
  2132. if anArray.Prefix<>nil then
  2133. begin
  2134. Log(etWarning,20240625134737,'Invalid CSS attribute selector prefix',anArray.Prefix);
  2135. exit;
  2136. end;
  2137. {$IFDEF VerboseCSSResolver}
  2138. writeln('TCSSResolverParser.CheckSelectorArray Prefix=',GetCSSObj(anArray.Prefix),' ChildCount=',anArray.ChildCount);
  2139. for i:=0 to anArray.ChildCount-1 do
  2140. writeln('TCSSResolverParser.CheckSelectorArray ',i,' ',GetCSSObj(anArray.Children[i]));
  2141. {$ENDIF}
  2142. if anArray.ChildCount<1 then
  2143. begin
  2144. Log(etWarning,20220910154033,'Invalid CSS attribute selector',anArray);
  2145. exit;
  2146. end;
  2147. if anArray.ChildCount>1 then
  2148. begin
  2149. El:=anArray.Children[1];
  2150. C:=El.ClassType;
  2151. if C=TCSSResolvedIdentifierElement then
  2152. begin
  2153. aValue:=TCSSResolvedIdentifierElement(El).Value;
  2154. case aValue of
  2155. 'i','s': ;
  2156. else
  2157. Log(etWarning,20240625134931,'Invalid attribute modifier "'+aValue+'"',El);
  2158. exit;
  2159. end;
  2160. end else begin
  2161. Log(etWarning,20240625134940,'Invalid CSS attribute modifier',El);
  2162. exit;
  2163. end;
  2164. end;
  2165. if (anArray.ChildCount>2) then
  2166. Log(etWarning,20240625134951,'Invalid CSS attribute modifier',anArray.Children[2]);
  2167. El:=anArray.Children[0];
  2168. C:=El.ClassType;
  2169. if C=TCSSResolvedIdentifierElement then
  2170. begin
  2171. // [name] -> has explicit attribute
  2172. ResolveAttribute(TCSSResolvedIdentifierElement(El));
  2173. end else if C=TCSSBinaryElement then
  2174. CheckSelectorArrayBinary(TCSSBinaryElement(El))
  2175. else begin
  2176. Log(etWarning,20240625135119,'Invalid CSS array selector',El);
  2177. end;
  2178. end;
  2179. procedure TCSSResolverParser.CheckSelectorArrayBinary(aBinary: TCSSBinaryElement
  2180. );
  2181. var
  2182. Left, Right: TCSSElement;
  2183. C: TClass;
  2184. begin
  2185. Left:=aBinary.Left;
  2186. if Left.ClassType<>TCSSResolvedIdentifierElement then
  2187. begin
  2188. Log(etWarning,20240625154314,'Invalid CSS array selector, expected attribute',Left);
  2189. exit;
  2190. end;
  2191. ResolveAttribute(TCSSResolvedIdentifierElement(Left));
  2192. Right:=aBinary.Right;
  2193. C:=Right.ClassType;
  2194. if (C=TCSSStringElement) or (C=TCSSIntegerElement) or (C=TCSSFloatElement)
  2195. or (C=TCSSResolvedIdentifierElement) then
  2196. // ok
  2197. else begin
  2198. Log(etWarning,20240625154455,'Invalid CSS array selector, expected string',Right);
  2199. exit;
  2200. end;
  2201. ComputeValue(Right);
  2202. case aBinary.Operation of
  2203. boEquals,
  2204. boSquaredEqual,
  2205. boDollarEqual,
  2206. boPipeEqual,
  2207. boStarEqual,
  2208. boTildeEqual: ;
  2209. else
  2210. Log(etWarning,20240625154617,'Invalid CSS array selector operator',aBinary);
  2211. end;
  2212. end;
  2213. procedure TCSSResolverParser.CheckSelectorBinary(aBinary: TCSSBinaryElement);
  2214. begin
  2215. case aBinary.Operation of
  2216. boGT,
  2217. boPlus,
  2218. boTilde,
  2219. boWhiteSpace: ;
  2220. else
  2221. Log(etWarning,20240625153307,'Invalid CSS binary selector '+BinaryOperators[aBinary.Operation],aBinary);
  2222. end;
  2223. CheckSelector(aBinary.Left);
  2224. CheckSelector(aBinary.Right);
  2225. end;
  2226. procedure TCSSResolverParser.CheckSelectorList(aList: TCSSListElement);
  2227. var
  2228. i: Integer;
  2229. El: TCSSElement;
  2230. begin
  2231. for i:=0 to aList.ChildCount-1 do
  2232. begin
  2233. El:=aList.Children[i];
  2234. {$IFDEF VerboseCSSResolver}
  2235. writeln('TCSSResolverParser.CheckSelectorList ',i,' ',GetCSSObj(El),' AsString=',El.AsString);
  2236. {$ENDIF}
  2237. CheckSelector(El);
  2238. end;
  2239. end;
  2240. procedure TCSSResolverParser.CheckNthChildParams(aCall: TCSSResolvedCallElement);
  2241. procedure NthWarn(const ID: TCSSMsgID; const Msg: string; PosEl: TCSSElement);
  2242. begin
  2243. Log(etWarning,ID,CSSSelectorCallNames[aCall.NameNumericalID]+' '+Msg,PosEl);
  2244. end;
  2245. var
  2246. i, ArgCount, aModulo, aStart: Integer;
  2247. Arg, OffsetEl: TCSSElement;
  2248. Str: TCSSString;
  2249. UnaryEl, anUnary: TCSSUnaryElement;
  2250. Params: TCSSNthChildParams;
  2251. begin
  2252. if aCall.Params<>nil then
  2253. raise Exception.Create('20240625150639');
  2254. ArgCount:=aCall.ArgCount;
  2255. {$IFDEF VerboseCSSResolver}
  2256. writeln('TCSSResolverParser.CheckSelectorCall_NthChild ArgCount=',aCall.ArgCount);
  2257. for i:=0 to aCall.ArgCount-1 do
  2258. writeln('TCSSResolverParser.CheckSelectorCall_NthChild Arg[',i,'] ',GetCSSObj(aCall.Args[i]),' AsString=',aCall.Args[i].AsString);
  2259. {$ENDIF}
  2260. // An, An+B, An+B of S, odd, even
  2261. i:=0;
  2262. aModulo:=0;
  2263. aStart:=1;
  2264. // check step
  2265. if ArgCount<=i then
  2266. begin
  2267. NthWarn(20220915143843,'missing arguments',aCall);
  2268. exit;
  2269. end;
  2270. Arg:=aCall.Args[i];
  2271. if Arg.ClassType=TCSSIntegerElement then
  2272. begin
  2273. aModulo:=TCSSIntegerElement(Arg).Value;
  2274. inc(i);
  2275. // check n
  2276. if ArgCount<=i then
  2277. begin
  2278. NthWarn(20220915143843,'missing arguments',aCall);
  2279. exit;
  2280. end;
  2281. Arg:=aCall.Args[i];
  2282. if Arg.ClassType<>TCSSResolvedIdentifierElement then
  2283. begin
  2284. NthWarn(20220915144312,'expected n',Arg);
  2285. exit;
  2286. end;
  2287. if TCSSResolvedIdentifierElement(Arg).Value<>'n' then
  2288. begin
  2289. NthWarn(20220915144359,'expected n',Arg);
  2290. exit;
  2291. end;
  2292. end
  2293. else if Arg.ClassType=TCSSResolvedIdentifierElement then
  2294. begin
  2295. Str:=TCSSResolvedIdentifierElement(Arg).Value;
  2296. case lowercase(Str) of
  2297. 'even':
  2298. begin
  2299. aModulo:=2;
  2300. aStart:=2;
  2301. end;
  2302. 'odd':
  2303. begin
  2304. aModulo:=2;
  2305. end;
  2306. 'n':
  2307. begin
  2308. aModulo:=1;
  2309. end;
  2310. else
  2311. NthWarn(20220915150332,'expected multiplier',Arg);
  2312. exit;
  2313. end
  2314. end else if Arg.ClassType=TCSSUnaryElement then
  2315. begin
  2316. anUnary:=TCSSUnaryElement(Arg);
  2317. case anUnary.Operation of
  2318. uoMinus: aModulo:=-1;
  2319. uoPlus: aModulo:=1;
  2320. else
  2321. NthWarn(20220917080309,'expected multiplier',Arg);
  2322. exit;
  2323. end;
  2324. if (anUnary.Right.ClassType=TCSSResolvedIdentifierElement)
  2325. and (SameText(TCSSResolvedIdentifierElement(anUnary.Right).Value,'n')) then
  2326. begin
  2327. // ok
  2328. end else begin
  2329. NthWarn(20220917080154,'expected multiplier',Arg);
  2330. exit;
  2331. end;
  2332. end else
  2333. begin
  2334. NthWarn(20220915144056,'expected multiplier',Arg);
  2335. exit;
  2336. end;
  2337. inc(i);
  2338. if ArgCount>i then
  2339. begin
  2340. Arg:=aCall.Args[i];
  2341. if Arg.ClassType=TCSSUnaryElement then
  2342. begin
  2343. UnaryEl:=TCSSUnaryElement(Arg);
  2344. if not (UnaryEl.Operation in [uoMinus,uoPlus]) then
  2345. begin
  2346. NthWarn(20220915151422,'unexpected offset',UnaryEl);
  2347. exit;
  2348. end;
  2349. OffsetEl:=UnaryEl.Right;
  2350. if OffsetEl=nil then
  2351. begin
  2352. NthWarn(20220915151511,'unexpected offset',UnaryEl);
  2353. exit;
  2354. end;
  2355. if OffsetEl.ClassType<>TCSSIntegerElement then
  2356. begin
  2357. NthWarn(20220915151718,'unexpected offset',OffsetEl);
  2358. exit;
  2359. end;
  2360. aStart:=TCSSIntegerElement(OffsetEl).Value;
  2361. if UnaryEl.Operation=uoMinus then
  2362. aStart:=-aStart;
  2363. end else
  2364. begin
  2365. NthWarn(20220915150851,'expected offset',Arg);
  2366. exit;
  2367. end;
  2368. end;
  2369. Params:=CSSNthChildParamsClass.Create;
  2370. aCall.Params:=Params;
  2371. Params.Modulo:=aModulo;
  2372. Params.Start:=aStart;
  2373. inc(i);
  2374. if (i<ArgCount) then
  2375. begin
  2376. Arg:=aCall.Args[i];
  2377. if (Arg.ClassType=TCSSResolvedIdentifierElement)
  2378. and (SameText(TCSSResolvedIdentifierElement(Arg).Value,'of')) then
  2379. begin
  2380. // An+B of Selector
  2381. inc(i);
  2382. if i=ArgCount then
  2383. begin
  2384. NthWarn(20240711154813,'expected selector',Arg);
  2385. exit;
  2386. end;
  2387. Arg:=aCall.Args[i];
  2388. Params.HasOf:=true;
  2389. Params.OfSelector:=Arg;
  2390. end;
  2391. end;
  2392. if (aCall.NameNumericalID in [CSSCallID_NthOfType,CSSCallID_NthLastOfType]) then
  2393. Params.HasOf:=true;
  2394. end;
  2395. function TCSSResolverParser.ComputeValue(El: TCSSElement): TCSSString;
  2396. var
  2397. ElData: TObject;
  2398. C: TClass;
  2399. StrEl: TCSSStringElement;
  2400. IntEl: TCSSIntegerElement;
  2401. FloatEl: TCSSFloatElement;
  2402. begin
  2403. C:=El.ClassType;
  2404. if C=TCSSResolvedIdentifierElement then
  2405. Result:=TCSSResolvedIdentifierElement(El).Value
  2406. else if (C=TCSSStringElement)
  2407. or (C=TCSSIntegerElement)
  2408. or (C=TCSSFloatElement) then
  2409. begin
  2410. ElData:=El.CustomData;
  2411. if ElData is TCSSValueData then
  2412. exit(TCSSValueData(ElData).NormValue);
  2413. if C=TCSSStringElement then
  2414. begin
  2415. StrEl:=TCSSStringElement(El);
  2416. Result:=StrEl.Value;
  2417. end
  2418. else if C=TCSSIntegerElement then
  2419. begin
  2420. IntEl:=TCSSIntegerElement(El);
  2421. Result:=IntEl.AsString;
  2422. end else if C=TCSSFloatElement then
  2423. begin
  2424. FloatEl:=TCSSFloatElement(El);
  2425. Result:=FloatEl.AsString;
  2426. end;
  2427. ElData:=TCSSValueData.Create;
  2428. TCSSValueData(ElData).NormValue:=Result;
  2429. El.CustomData:=ElData;
  2430. end else begin
  2431. Log(etWarning,20240625162632,'TCSSResolverParser.ComputeValue not supported',El);
  2432. end;
  2433. end;
  2434. constructor TCSSResolverParser.Create(AScanner: TCSSScanner);
  2435. begin
  2436. inherited Create(AScanner);
  2437. CSSIdentifierElementClass:=TCSSResolvedIdentifierElement;
  2438. CSSPseudoClassElementClass:=TCSSResolvedPseudoClassElement;
  2439. CSSCallElementClass:=TCSSResolvedCallElement;
  2440. CSSNthChildParamsClass:=TCSSNthChildParams;
  2441. CSSAttributeKeyDataClass:=TCSSAttributeKeyData;
  2442. end;
  2443. destructor TCSSResolverParser.Destroy;
  2444. begin
  2445. inherited Destroy;
  2446. end;
  2447. procedure TCSSResolverParser.Log(MsgType: TEventType; const ID: TCSSMsgID;
  2448. const Msg: TCSSString; PosEl: TCSSElement);
  2449. begin
  2450. if Assigned(OnLog) then
  2451. OnLog(MsgType,ID,Msg,PosEl);
  2452. end;
  2453. class function TCSSResolverParser.IsWhiteSpace(const s: TCSSString): boolean;
  2454. var
  2455. i: Integer;
  2456. begin
  2457. for i:=1 to length(s) do
  2458. if not (s[i] in [' ',#10,#13]) then
  2459. exit(false);
  2460. Result:=true;
  2461. end;
  2462. end.