tccssresolver.pp 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752
  1. {
  2. This file is part of the Free Pascal Run time library.
  3. Copyright (c) 2022 by Michael Van Canneyt ([email protected])
  4. This file contains the tests for the CSS parser
  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. unit tcCSSResolver;
  12. {$mode ObjFPC}{$H+}
  13. interface
  14. uses
  15. Classes, SysUtils, Contnrs, fpcunit, testregistry, fpCSSParser, fpCSSTree,
  16. fpCSSResolver;
  17. type
  18. TDemoNodeAttribute = (
  19. naLeft,
  20. naTop,
  21. naWidth,
  22. naHeight,
  23. naBorder,
  24. naDisplay,
  25. naColor
  26. );
  27. TDemoNodeAttributes = set of TDemoNodeAttribute;
  28. const
  29. DemoAttributeNames: array[TDemoNodeAttribute] of string = (
  30. // case sensitive!
  31. 'left',
  32. 'top',
  33. 'width',
  34. 'height',
  35. 'border',
  36. 'display',
  37. 'color'
  38. );
  39. DemoAttrIDBase = 100;
  40. type
  41. TDemoPseudoClass = (
  42. pcActive,
  43. pcHover
  44. );
  45. TDemoPseudoClasses = set of TDemoPseudoClass;
  46. type
  47. { TDemoNode }
  48. TDemoNode = class(TComponent,ICSSNode)
  49. private
  50. class var FAttributeInitialValues: array[TDemoNodeAttribute] of string;
  51. private
  52. FAttributeValues: array[TDemoNodeAttribute] of string;
  53. FNodes: TFPObjectList; // list of TDemoNode
  54. FCSSClasses: TStrings;
  55. FParent: TDemoNode;
  56. FStyleElements: TCSSElement;
  57. FStyle: string;
  58. function GetAttribute(AIndex: TDemoNodeAttribute): string;
  59. function GetNodeCount: integer;
  60. function GetNodes(Index: integer): TDemoNode;
  61. procedure SetAttribute(AIndex: TDemoNodeAttribute; const AValue: string);
  62. procedure SetParent(const AValue: TDemoNode);
  63. procedure SetStyleElements(const AValue: TCSSElement);
  64. procedure SetStyle(const AValue: string);
  65. protected
  66. procedure Notification(AComponent: TComponent; Operation: TOperation);
  67. override;
  68. public
  69. constructor Create(AOwner: TComponent); override;
  70. destructor Destroy; override;
  71. procedure Clear;
  72. function GetCSSID: TCSSString; virtual;
  73. class function CSSTypeName: TCSSString; virtual;
  74. function GetCSSTypeName: TCSSString;
  75. class function CSSTypeID: TCSSNumericalID; virtual;
  76. function GetCSSTypeID: TCSSNumericalID;
  77. class function GetAttributeInitialValue(Attr: TDemoNodeAttribute): string; virtual;
  78. function HasCSSClass(const aClassName: TCSSString): boolean; virtual;
  79. function CheckCSSValue(AttrID: TCSSNumericalID; Value: TCSSElement
  80. ): boolean; virtual;
  81. procedure SetCSSValue(AttrID: TCSSNumericalID; Value: TCSSElement); virtual;
  82. function GetCSSParent: ICSSNode; virtual;
  83. function GetCSSIndex: integer; virtual;
  84. function GetCSSNextSibling: ICSSNode; virtual;
  85. function GetCSSPreviousSibling: ICSSNode; virtual;
  86. function GetCSSChildCount: integer; virtual;
  87. function GetCSSChild(const anIndex: integer): ICSSNode; virtual;
  88. function GetCSSNextOfType: ICSSNode; virtual;
  89. function GetCSSPreviousOfType: ICSSNode; virtual;
  90. function GetCSSAttributeClass: TCSSString; virtual;
  91. function HasCSSAttribute(const AttrID: TCSSNumericalID): boolean; virtual;
  92. function GetCSSAttribute(const AttrID: TCSSNumericalID): TCSSString; virtual;
  93. function HasCSSPseudo(const AttrID: TCSSNumericalID): boolean; virtual;
  94. function GetCSSPseudo(const AttrID: TCSSNumericalID): TCSSString; virtual;
  95. function GetCSSEmpty: boolean; virtual;
  96. function GetCSSDepth: integer; virtual;
  97. property Parent: TDemoNode read FParent write SetParent;
  98. property NodeCount: integer read GetNodeCount;
  99. property Nodes[Index: integer]: TDemoNode read GetNodes; default;
  100. property CSSClasses: TStrings read FCSSClasses;
  101. property StyleElements: TCSSElement read FStyleElements write SetStyleElements;
  102. property Style: string read FStyle write SetStyle;
  103. // CSS attributes
  104. property Left: string index naLeft read GetAttribute write SetAttribute;
  105. property Top: string index naTop read GetAttribute write SetAttribute;
  106. property Width: string index naWidth read GetAttribute write SetAttribute;
  107. property Height: string index naHeight read GetAttribute write SetAttribute;
  108. property Border: string index naBorder read GetAttribute write SetAttribute;
  109. property Display: string index naDisplay read GetAttribute write SetAttribute;
  110. property Color: string index naColor read GetAttribute write SetAttribute;
  111. property Attribute[Attr: TDemoNodeAttribute]: string read GetAttribute write SetAttribute;
  112. end;
  113. TDemoNodeClass = class of TDemoNode;
  114. { TDemoDiv }
  115. TDemoDiv = class(TDemoNode)
  116. public
  117. class function CSSTypeName: TCSSString; override;
  118. class function CSSTypeID: TCSSNumericalID; override;
  119. end;
  120. { TDemoSpan }
  121. TDemoSpan = class(TDemoNode)
  122. public
  123. class function CSSTypeName: TCSSString; override;
  124. class function CSSTypeID: TCSSNumericalID; override;
  125. end;
  126. { TDemoButton }
  127. TDemoButton = class(TDemoNode)
  128. private
  129. FCaption: string;
  130. procedure SetCaption(const AValue: string);
  131. public
  132. class var CSSCaptionID: TCSSNumericalID;
  133. class function CSSTypeName: TCSSString; override;
  134. class function CSSTypeID: TCSSNumericalID; override;
  135. function HasCSSAttribute(const AttrID: TCSSNumericalID): boolean; override;
  136. function GetCSSAttribute(const AttrID: TCSSNumericalID): TCSSString;
  137. override;
  138. procedure SetCSSValue(AttrID: TCSSNumericalID; Value: TCSSElement); override;
  139. property Caption: string read FCaption write SetCaption;
  140. end;
  141. { TDemoDocument }
  142. TDemoDocument = class(TComponent)
  143. private
  144. FNumericalIDs: array[TCSSNumericalIDKind] of TCSSNumericalIDs;
  145. FCSSResolver: TCSSResolver;
  146. FStyle: string;
  147. FStyleElements: TCSSElement;
  148. function GetNumericalIDs(Kind: TCSSNumericalIDKind): TCSSNumericalIDs;
  149. procedure SetNumericalIDs(Kind: TCSSNumericalIDKind;
  150. const AValue: TCSSNumericalIDs);
  151. procedure SetStyle(const AValue: string);
  152. procedure SetStyleElements(const AValue: TCSSElement);
  153. public
  154. Root: TDemoNode;
  155. constructor Create(AOwner: TComponent); override;
  156. destructor Destroy; override;
  157. procedure ApplyStyle; virtual;
  158. procedure ApplyStyleToNode(Node: TDemoNode); virtual;
  159. property NumericalIDs[Kind: TCSSNumericalIDKind]: TCSSNumericalIDs read GetNumericalIDs write SetNumericalIDs;
  160. property StyleElements: TCSSElement read FStyleElements write SetStyleElements;
  161. property Style: string read FStyle write SetStyle;
  162. property CSSResolver: TCSSResolver read FCSSResolver;
  163. end;
  164. { TCustomTestCSSResolver }
  165. TCustomTestCSSResolver = class(TTestCase)
  166. Private
  167. FDoc: TDemoDocument;
  168. protected
  169. procedure SetUp; override;
  170. procedure TearDown; override;
  171. public
  172. property Doc: TDemoDocument read FDoc;
  173. end;
  174. { TTestCSSResolver }
  175. TTestCSSResolver = class(TCustomTestCSSResolver)
  176. published
  177. procedure Test_Selector_Universal;
  178. procedure Test_Selector_Type;
  179. // Test list spaces "div, button ,span {}"
  180. procedure Test_Selector_Id;
  181. procedure Test_Selector_Class;
  182. procedure Test_Selector_ClassClass; // ToDo and combinator
  183. procedure Test_Selector_ClassSpaceClass; // ToDo descendant combinator
  184. procedure Test_Selector_TypeCommaType; // or combinator
  185. procedure Test_Selector_ClassGTClass; // child combinator
  186. procedure Test_Selector_TypePlusType; // adjacent sibling combinator
  187. procedure Test_Selector_TypeTildeType; // general sibling combinator
  188. procedure Test_Selector_HasAttribute;
  189. procedure Test_Selector_AttributeEquals;
  190. procedure Test_Selector_AttributeEqualsI;
  191. procedure Test_Selector_AttributeBeginsWith;
  192. procedure Test_Selector_AttributeEndsWith;
  193. procedure Test_Selector_AttributeBeginsWithHyphen;
  194. procedure Test_Selector_AttributeContainsWord;
  195. procedure Test_Selector_AttributeContainsSubstring;
  196. // ToDo: "all"
  197. // pseudo attributes
  198. procedure Test_Selector_Root;
  199. procedure Test_Selector_Empty;
  200. procedure Test_Selector_FirstChild;
  201. procedure Test_Selector_LastChild;
  202. procedure Test_Selector_OnlyChild;
  203. procedure Test_Selector_Not;
  204. procedure Test_Selector_NthChild;
  205. procedure Test_Selector_NthLastChild;
  206. procedure Test_Selector_NthChildOf;
  207. procedure Test_Selector_FirstOfType;
  208. procedure Test_Selector_LastOfType;
  209. procedure Test_Selector_OnlyOfType;
  210. procedure Test_Selector_NthOfType;
  211. procedure Test_Selector_NthLastOfType;
  212. procedure Test_Selector_Is;
  213. procedure Test_Selector_Where;
  214. // ToDo: div:has(>img)
  215. // ToDo: div:has(+img)
  216. // ToDo: :lang()
  217. // inline style
  218. procedure Test_InlineStyle;
  219. // ToDo: specifity
  220. // pseudo elements
  221. // skipping for forward compatibility
  222. // ToDo: invalid token in selector makes selector invalid
  223. end;
  224. function LinesToStr(const Args: array of const): string;
  225. implementation
  226. function LinesToStr(const Args: array of const): string;
  227. var
  228. s: String;
  229. i: Integer;
  230. begin
  231. s:='';
  232. for i:=Low(Args) to High(Args) do
  233. case Args[i].VType of
  234. vtChar: s += Args[i].VChar+LineEnding;
  235. vtString: s += Args[i].VString^+LineEnding;
  236. vtPChar: s += Args[i].VPChar+LineEnding;
  237. vtWideChar: s += AnsiString(Args[i].VWideChar)+LineEnding;
  238. vtPWideChar: s += AnsiString(Args[i].VPWideChar)+LineEnding;
  239. vtAnsiString: s += AnsiString(Args[i].VAnsiString)+LineEnding;
  240. vtWidestring: s += AnsiString(WideString(Args[i].VWideString))+LineEnding;
  241. vtUnicodeString:s += AnsiString(UnicodeString(Args[i].VUnicodeString))+LineEnding;
  242. end;
  243. Result:=s;
  244. end;
  245. { TCustomTestCSSResolver }
  246. procedure TCustomTestCSSResolver.SetUp;
  247. begin
  248. inherited SetUp;
  249. FDoc:=TDemoDocument.Create(nil);
  250. end;
  251. procedure TCustomTestCSSResolver.TearDown;
  252. begin
  253. FreeAndNil(FDoc);
  254. inherited TearDown;
  255. end;
  256. { TTestCSSResolver }
  257. procedure TTestCSSResolver.Test_Selector_Universal;
  258. begin
  259. Doc.Root:=TDemoNode.Create(nil);
  260. Doc.Style:='* { left: 10px; }';
  261. Doc.ApplyStyle;
  262. AssertEquals('Root.left','10px',Doc.Root.Left);
  263. end;
  264. procedure TTestCSSResolver.Test_Selector_Type;
  265. var
  266. Button: TDemoButton;
  267. begin
  268. Doc.Root:=TDemoNode.Create(nil);
  269. Button:=TDemoButton.Create(Doc);
  270. Button.Parent:=Doc.Root;
  271. Doc.Style:='button { left: 11px; }';
  272. Doc.ApplyStyle;
  273. AssertEquals('Root.left','',Doc.Root.Left);
  274. AssertEquals('Button.left','11px',Button.Left);
  275. end;
  276. procedure TTestCSSResolver.Test_Selector_Id;
  277. var
  278. Button1: TDemoButton;
  279. begin
  280. Doc.Root:=TDemoNode.Create(nil);
  281. Button1:=TDemoButton.Create(Doc);
  282. Button1.Name:='Button1';
  283. Button1.Parent:=Doc.Root;
  284. Doc.Style:='#Button1 { left: 12px; }';
  285. Doc.ApplyStyle;
  286. AssertEquals('Root.left','',Doc.Root.Left);
  287. AssertEquals('Button1.left','12px',Button1.Left);
  288. end;
  289. procedure TTestCSSResolver.Test_Selector_Class;
  290. var
  291. Button1: TDemoButton;
  292. begin
  293. Doc.Root:=TDemoNode.Create(nil);
  294. Button1:=TDemoButton.Create(Doc);
  295. Button1.CSSClasses.Add('west');
  296. Button1.Parent:=Doc.Root;
  297. Doc.Style:='.west { left: 13px; }';
  298. Doc.ApplyStyle;
  299. AssertEquals('Root.left','',Doc.Root.Left);
  300. AssertEquals('Button1.left','13px',Button1.Left);
  301. end;
  302. procedure TTestCSSResolver.Test_Selector_ClassClass;
  303. var
  304. Button1, Button2: TDemoButton;
  305. begin
  306. Doc.Root:=TDemoNode.Create(nil);
  307. Button1:=TDemoButton.Create(Doc);
  308. Button1.CSSClasses.Add('west');
  309. Button1.Parent:=Doc.Root;
  310. Button2:=TDemoButton.Create(Doc);
  311. Button2.CSSClasses.DelimitedText:='west south';
  312. AssertEquals('Button2.CSSClasses.Count',2,Button2.CSSClasses.Count);
  313. Button2.Parent:=Doc.Root;
  314. Doc.Style:='.west.south { left: 10px; }';
  315. Doc.ApplyStyle;
  316. AssertEquals('Root.left','',Doc.Root.Left);
  317. AssertEquals('Button1.left','',Button1.Left);
  318. AssertEquals('Button2.left','10px',Button2.Left);
  319. end;
  320. procedure TTestCSSResolver.Test_Selector_ClassSpaceClass;
  321. var
  322. Button1: TDemoButton;
  323. begin
  324. Doc.Root:=TDemoNode.Create(nil);
  325. Doc.Root.CSSClasses.Add('bird');
  326. Button1:=TDemoButton.Create(Doc);
  327. Button1.CSSClasses.Add('west');
  328. Button1.Parent:=Doc.Root;
  329. Doc.Style:='.bird .west { left: 10px; }';
  330. Doc.ApplyStyle;
  331. AssertEquals('Root.left','',Doc.Root.Left);
  332. AssertEquals('Button1.left','10px',Button1.Left);
  333. end;
  334. procedure TTestCSSResolver.Test_Selector_TypeCommaType;
  335. var
  336. Button1: TDemoButton;
  337. Div1: TDemoDiv;
  338. begin
  339. Doc.Root:=TDemoNode.Create(nil);
  340. Button1:=TDemoButton.Create(Doc);
  341. Button1.Parent:=Doc.Root;
  342. Div1:=TDemoDiv.Create(Doc);
  343. Div1.Parent:=Doc.Root;
  344. Doc.Style:='div, button { left: 10px; }';
  345. Doc.ApplyStyle;
  346. AssertEquals('Root.left','',Doc.Root.Left);
  347. AssertEquals('Button1.left','10px',Button1.Left);
  348. AssertEquals('Div1.left','10px',Div1.Left);
  349. end;
  350. procedure TTestCSSResolver.Test_Selector_ClassGTClass;
  351. var
  352. Div1, Div2: TDemoDiv;
  353. begin
  354. Doc.Root:=TDemoNode.Create(nil);
  355. Doc.Root.CSSClasses.Add('lvl1');
  356. Div1:=TDemoDiv.Create(Doc);
  357. Div1.CSSClasses.Add('lvl2');
  358. Div1.Parent:=Doc.Root;
  359. Div2:=TDemoDiv.Create(Doc);
  360. Div2.CSSClasses.Add('lvl3');
  361. Div2.Parent:=Div1;
  362. Doc.Style:=LinesToStr([
  363. '.lvl1>.lvl2 { left: 10px; }',
  364. '.lvl1>.lvl3 { top: 11px; }',
  365. '.lvl2>.lvl3 { width: 12px; }',
  366. '']);
  367. Doc.ApplyStyle;
  368. AssertEquals('Root.left','',Doc.Root.Left);
  369. AssertEquals('Root.top','',Doc.Root.Top);
  370. AssertEquals('Root.width','',Doc.Root.Width);
  371. AssertEquals('Div1.left','10px',Div1.Left);
  372. AssertEquals('Div1.top','',Div1.Top);
  373. AssertEquals('Div1.width','',Div1.Width);
  374. AssertEquals('Div2.left','',Div2.Left);
  375. AssertEquals('Div2.top','',Div2.Top);
  376. AssertEquals('Div2.width','12px',Div2.Width);
  377. end;
  378. procedure TTestCSSResolver.Test_Selector_TypePlusType;
  379. var
  380. Button1, Button2, Button3: TDemoButton;
  381. Div1: TDemoDiv;
  382. begin
  383. Doc.Root:=TDemoNode.Create(nil);
  384. Button1:=TDemoButton.Create(Doc);
  385. Button1.Parent:=Doc.Root;
  386. Div1:=TDemoDiv.Create(Doc);
  387. Div1.Parent:=Doc.Root;
  388. Button2:=TDemoButton.Create(Doc);
  389. Button2.Parent:=Doc.Root;
  390. Button3:=TDemoButton.Create(Doc);
  391. Button3.Parent:=Doc.Root;
  392. Doc.Style:='div+button { left: 10px; }';
  393. Doc.ApplyStyle;
  394. AssertEquals('Root.left','',Doc.Root.Left);
  395. AssertEquals('Button1.left','',Button1.Left);
  396. AssertEquals('Div1.left','',Div1.Left);
  397. AssertEquals('Button2.left','10px',Button2.Left);
  398. AssertEquals('Button3.left','',Button3.Left);
  399. end;
  400. procedure TTestCSSResolver.Test_Selector_TypeTildeType;
  401. var
  402. Button1, Button2, Button3: TDemoButton;
  403. Div1: TDemoDiv;
  404. begin
  405. Doc.Root:=TDemoNode.Create(nil);
  406. Button1:=TDemoButton.Create(Doc);
  407. Button1.Parent:=Doc.Root;
  408. Div1:=TDemoDiv.Create(Doc);
  409. Div1.Parent:=Doc.Root;
  410. Button2:=TDemoButton.Create(Doc);
  411. Button2.Parent:=Doc.Root;
  412. Button3:=TDemoButton.Create(Doc);
  413. Button3.Parent:=Doc.Root;
  414. Doc.Style:='div~button { left: 10px; }';
  415. Doc.ApplyStyle;
  416. AssertEquals('Root.left','',Doc.Root.Left);
  417. AssertEquals('Button1.left','',Button1.Left);
  418. AssertEquals('Div1.left','',Div1.Left);
  419. AssertEquals('Button2.left','10px',Button2.Left);
  420. AssertEquals('Button3.left','10px',Button3.Left);
  421. end;
  422. procedure TTestCSSResolver.Test_Selector_HasAttribute;
  423. var
  424. Button1: TDemoButton;
  425. begin
  426. Doc.Root:=TDemoNode.Create(nil);
  427. Button1:=TDemoButton.Create(Doc);
  428. Button1.Parent:=Doc.Root;
  429. Button1.Left:='2px';
  430. Doc.Style:=LinesToStr([
  431. '[left] { top: 3px; }',
  432. '[caption] { width: 4px; }',
  433. '']);
  434. Doc.ApplyStyle;
  435. AssertEquals('Root.Top','3px',Doc.Root.Top);
  436. AssertEquals('Root.Width','',Doc.Root.Width);
  437. AssertEquals('Button1.Top','3px',Button1.Top);
  438. AssertEquals('Button1.Width','4px',Button1.Width);
  439. end;
  440. procedure TTestCSSResolver.Test_Selector_AttributeEquals;
  441. var
  442. Button1: TDemoButton;
  443. begin
  444. Doc.Root:=TDemoNode.Create(nil);
  445. Doc.Root.Left:='2px';
  446. Button1:=TDemoButton.Create(Doc);
  447. Button1.Parent:=Doc.Root;
  448. Button1.Left:='3px';
  449. Button1.Color:='maybe black';
  450. Doc.Style:=LinesToStr([
  451. '[left=2px] { top: 4px; }',
  452. '[color="maybe black"] { width: 5px; }',
  453. '']);
  454. Doc.ApplyStyle;
  455. AssertEquals('Root.Top','4px',Doc.Root.Top);
  456. AssertEquals('Button1.Top','',Button1.Top);
  457. AssertEquals('Button1.Width','5px',Button1.Width);
  458. end;
  459. procedure TTestCSSResolver.Test_Selector_AttributeEqualsI;
  460. var
  461. Button1: TDemoButton;
  462. begin
  463. Doc.Root:=TDemoNode.Create(nil);
  464. Doc.Root.Left:='2px';
  465. Button1:=TDemoButton.Create(Doc);
  466. Button1.Parent:=Doc.Root;
  467. Button1.Left:='3px';
  468. Button1.Color:='maybe Black';
  469. Doc.Style:=LinesToStr([
  470. '[left="2Px" i] { top: 4px; }',
  471. '[color="Maybe bLack" i] { width: 5px; }',
  472. '']);
  473. Doc.ApplyStyle;
  474. AssertEquals('Root.Top','4px',Doc.Root.Top);
  475. AssertEquals('Button1.Top','',Button1.Top);
  476. AssertEquals('Button1.Width','5px',Button1.Width);
  477. end;
  478. procedure TTestCSSResolver.Test_Selector_AttributeBeginsWith;
  479. var
  480. Button1: TDemoButton;
  481. begin
  482. Doc.Root:=TDemoNode.Create(nil);
  483. Doc.Root.Left:='Foo';
  484. Button1:=TDemoButton.Create(Doc);
  485. Button1.Parent:=Doc.Root;
  486. Button1.Left:='Foo Bar';
  487. Doc.Style:=LinesToStr([
  488. '[left^=Fo] { top: 4px; }',
  489. '[left^="Foo B"] { width: 5px; }',
  490. '']);
  491. Doc.ApplyStyle;
  492. AssertEquals('Root.Top','4px',Doc.Root.Top);
  493. AssertEquals('Root.Width','',Doc.Root.Width);
  494. AssertEquals('Button1.Top','4px',Button1.Top);
  495. AssertEquals('Button1.Width','5px',Button1.Width);
  496. end;
  497. procedure TTestCSSResolver.Test_Selector_AttributeEndsWith;
  498. var
  499. Button1: TDemoButton;
  500. begin
  501. Doc.Root:=TDemoNode.Create(nil);
  502. Doc.Root.Left:='Foo';
  503. Button1:=TDemoButton.Create(Doc);
  504. Button1.Parent:=Doc.Root;
  505. Button1.Left:='Foo Bar';
  506. Doc.Style:=LinesToStr([
  507. '[left$=o] { top: 4px; }',
  508. '[left$="o Bar"] { width: 5px; }',
  509. '']);
  510. Doc.ApplyStyle;
  511. AssertEquals('Root.Top','4px',Doc.Root.Top);
  512. AssertEquals('Root.Width','',Doc.Root.Width);
  513. AssertEquals('Button1.Top','',Button1.Top);
  514. AssertEquals('Button1.Width','5px',Button1.Width);
  515. end;
  516. procedure TTestCSSResolver.Test_Selector_AttributeBeginsWithHyphen;
  517. var
  518. Button1: TDemoButton;
  519. begin
  520. Doc.Root:=TDemoNode.Create(nil);
  521. Doc.Root.Left:='Foo';
  522. Button1:=TDemoButton.Create(Doc);
  523. Button1.Parent:=Doc.Root;
  524. Button1.Left:='Foo-Bar';
  525. Doc.Style:=LinesToStr([
  526. '[left|=Foo] { top: 4px; }',
  527. '[left|="Fo"] { width: 5px; }',
  528. '']);
  529. Doc.ApplyStyle;
  530. AssertEquals('Root.Top','4px',Doc.Root.Top);
  531. AssertEquals('Root.Width','',Doc.Root.Width);
  532. AssertEquals('Button1.Top','4px',Button1.Top);
  533. AssertEquals('Button1.Width','',Button1.Width);
  534. end;
  535. procedure TTestCSSResolver.Test_Selector_AttributeContainsWord;
  536. var
  537. Button1: TDemoButton;
  538. begin
  539. Doc.Root:=TDemoNode.Create(nil);
  540. Doc.Root.Left:='One Two Three';
  541. Button1:=TDemoButton.Create(Doc);
  542. Button1.Parent:=Doc.Root;
  543. Button1.Left:='Four Five';
  544. Doc.Style:=LinesToStr([
  545. '[left~=One] { top: 4px; }',
  546. '[left~=Two] { width: 5px; }',
  547. '[left~=Three] { height: 6px; }',
  548. '[left~="Four Five"] { color: #123; }', // not one word, so does not match!
  549. '[left~=our] { display: none; }',
  550. '']);
  551. Doc.ApplyStyle;
  552. AssertEquals('Root.Top','4px',Doc.Root.Top);
  553. AssertEquals('Root.Width','5px',Doc.Root.Width);
  554. AssertEquals('Root.Height','6px',Doc.Root.Height);
  555. AssertEquals('Root.Color','',Doc.Root.Color);
  556. AssertEquals('Root.Display','',Doc.Root.Display);
  557. AssertEquals('Button1.Top','',Button1.Top);
  558. AssertEquals('Button1.Width','',Button1.Width);
  559. AssertEquals('Button1.Height','',Button1.Height);
  560. AssertEquals('Button1.Color','',Button1.Color);
  561. AssertEquals('Button1.Display','',Button1.Display);
  562. end;
  563. procedure TTestCSSResolver.Test_Selector_AttributeContainsSubstring;
  564. var
  565. Button1: TDemoButton;
  566. begin
  567. Doc.Root:=TDemoNode.Create(nil);
  568. Doc.Root.Left:='Foo';
  569. Button1:=TDemoButton.Create(Doc);
  570. Button1.Parent:=Doc.Root;
  571. Button1.Left:='Foo Bar';
  572. Doc.Style:=LinesToStr([
  573. '[left*=oo] { top: 4px; }',
  574. '[left*="o B"] { width: 5px; }',
  575. '']);
  576. Doc.ApplyStyle;
  577. AssertEquals('Root.Top','4px',Doc.Root.Top);
  578. AssertEquals('Root.Width','',Doc.Root.Width);
  579. AssertEquals('Button1.Top','4px',Button1.Top);
  580. AssertEquals('Button1.Width','5px',Button1.Width);
  581. end;
  582. procedure TTestCSSResolver.Test_Selector_Root;
  583. var
  584. Button1: TDemoButton;
  585. begin
  586. Doc.Root:=TDemoNode.Create(nil);
  587. Doc.Root.Left:='Foo';
  588. Button1:=TDemoButton.Create(Doc);
  589. Button1.Parent:=Doc.Root;
  590. Doc.Style:=LinesToStr([
  591. ':roOt { top: 4px; }',
  592. '']);
  593. Doc.ApplyStyle;
  594. AssertEquals('Root.Top','4px',Doc.Root.Top);
  595. AssertEquals('Button1.Top','',Button1.Top);
  596. end;
  597. procedure TTestCSSResolver.Test_Selector_Empty;
  598. var
  599. Div1, Div11, Div2: TDemoDiv;
  600. begin
  601. Doc.Root:=TDemoNode.Create(nil);
  602. Div1:=TDemoDiv.Create(Doc);
  603. Div1.Parent:=Doc.Root;
  604. Div11:=TDemoDiv.Create(Doc);
  605. Div11.Parent:=Div1;
  606. Div2:=TDemoDiv.Create(Doc);
  607. Div2.Parent:=Doc.Root;
  608. Doc.Style:=LinesToStr([
  609. ':eMpty { left: 1px; }',
  610. 'div:emPty { top: 2px; }',
  611. '']);
  612. Doc.ApplyStyle;
  613. AssertEquals('Root.Left','',Doc.Root.Left);
  614. AssertEquals('Root.Top','',Doc.Root.Top);
  615. AssertEquals('Div1.Left','',Div1.Left);
  616. AssertEquals('Div1.Top','',Div1.Top);
  617. AssertEquals('Div11.Left','1px',Div11.Left);
  618. AssertEquals('Div11.Top','2px',Div11.Top);
  619. AssertEquals('Div2.Left','1px',Div2.Left);
  620. AssertEquals('Div2.Top','2px',Div2.Top);
  621. end;
  622. procedure TTestCSSResolver.Test_Selector_FirstChild;
  623. var
  624. Div1, Div11, Div12, Div2: TDemoDiv;
  625. begin
  626. Doc.Root:=TDemoNode.Create(nil);
  627. Div1:=TDemoDiv.Create(Doc);
  628. Div1.Parent:=Doc.Root;
  629. Div11:=TDemoDiv.Create(Doc);
  630. Div11.Parent:=Div1;
  631. Div12:=TDemoDiv.Create(Doc);
  632. Div12.Parent:=Div1;
  633. Div2:=TDemoDiv.Create(Doc);
  634. Div2.Parent:=Doc.Root;
  635. Doc.Style:=LinesToStr([
  636. ':first-child { left: 1px; }',
  637. 'div:first-child { top: 2px; }',
  638. '']);
  639. Doc.ApplyStyle;
  640. AssertEquals('Root.Left','1px',Doc.Root.Left);
  641. AssertEquals('Root.Top','',Doc.Root.Top);
  642. AssertEquals('Div1.Left','1px',Div1.Left);
  643. AssertEquals('Div1.Top','2px',Div1.Top);
  644. AssertEquals('Div11.Left','1px',Div11.Left);
  645. AssertEquals('Div11.Top','2px',Div11.Top);
  646. AssertEquals('Div12.Left','',Div12.Left);
  647. AssertEquals('Div12.Top','',Div12.Top);
  648. AssertEquals('Div2.Left','',Div2.Left);
  649. AssertEquals('Div2.Top','',Div2.Top);
  650. end;
  651. procedure TTestCSSResolver.Test_Selector_LastChild;
  652. var
  653. Div1, Div11, Div2: TDemoDiv;
  654. Button12: TDemoButton;
  655. begin
  656. Doc.Root:=TDemoNode.Create(nil);
  657. Div1:=TDemoDiv.Create(Doc);
  658. Div1.Parent:=Doc.Root;
  659. Div11:=TDemoDiv.Create(Doc);
  660. Div11.Parent:=Div1;
  661. Button12:=TDemoButton.Create(Doc);
  662. Button12.Parent:=Div1;
  663. Div2:=TDemoDiv.Create(Doc);
  664. Div2.Parent:=Doc.Root;
  665. Doc.Style:=LinesToStr([
  666. ':last-child { left: 6px; }',
  667. 'div:last-child { top: 7px; }',
  668. '']);
  669. Doc.ApplyStyle;
  670. AssertEquals('Root.Left','6px',Doc.Root.Left);
  671. AssertEquals('Root.Top','',Doc.Root.Top);
  672. AssertEquals('Div1.Left','',Div1.Left);
  673. AssertEquals('Div1.Top','',Div1.Top);
  674. AssertEquals('Div11.Left','',Div11.Left);
  675. AssertEquals('Div11.Top','',Div11.Top);
  676. AssertEquals('Button12.Left','6px',Button12.Left);
  677. AssertEquals('Button12.Top','',Button12.Top);
  678. AssertEquals('Div2.Left','6px',Div2.Left);
  679. AssertEquals('Div2.Top','7px',Div2.Top);
  680. end;
  681. procedure TTestCSSResolver.Test_Selector_OnlyChild;
  682. var
  683. Div1, Div11, Div2: TDemoDiv;
  684. Button12: TDemoButton;
  685. begin
  686. Doc.Root:=TDemoNode.Create(nil);
  687. Div1:=TDemoDiv.Create(Doc);
  688. Div1.Parent:=Doc.Root;
  689. Div11:=TDemoDiv.Create(Doc);
  690. Div11.Parent:=Div1;
  691. Div2:=TDemoDiv.Create(Doc);
  692. Div2.Parent:=Doc.Root;
  693. Button12:=TDemoButton.Create(Doc);
  694. Button12.Parent:=Div2;
  695. Doc.Style:=LinesToStr([
  696. ':only-child { left: 8px; }',
  697. 'div:only-child { top: 9px; }',
  698. '']);
  699. Doc.ApplyStyle;
  700. AssertEquals('Root.Left','8px',Doc.Root.Left);
  701. AssertEquals('Root.Top','',Doc.Root.Top);
  702. AssertEquals('Div1.Left','',Div1.Left);
  703. AssertEquals('Div1.Top','',Div1.Top);
  704. AssertEquals('Div11.Left','8px',Div11.Left);
  705. AssertEquals('Div11.Top','9px',Div11.Top);
  706. AssertEquals('Div2.Left','',Div2.Left);
  707. AssertEquals('Div2.Top','',Div2.Top);
  708. AssertEquals('Button12.Left','8px',Button12.Left);
  709. AssertEquals('Button12.Top','',Button12.Top);
  710. end;
  711. procedure TTestCSSResolver.Test_Selector_Not;
  712. var
  713. Div1, Div11, Div2: TDemoDiv;
  714. Button12: TDemoButton;
  715. begin
  716. Doc.Root:=TDemoNode.Create(nil);
  717. Div1:=TDemoDiv.Create(Doc);
  718. Div1.Parent:=Doc.Root;
  719. Div11:=TDemoDiv.Create(Doc);
  720. Div11.Parent:=Div1;
  721. Div2:=TDemoDiv.Create(Doc);
  722. Div2.Parent:=Doc.Root;
  723. Button12:=TDemoButton.Create(Doc);
  724. Button12.Parent:=Div2;
  725. Doc.Style:=LinesToStr([
  726. ':not(:only-child) { left: 8px; }',
  727. ':not(div:only-child) { top: 9px; }',
  728. '']);
  729. Doc.ApplyStyle;
  730. AssertEquals('Root.Left','',Doc.Root.Left);
  731. AssertEquals('Root.Top','9px',Doc.Root.Top);
  732. AssertEquals('Div1.Left','8px',Div1.Left);
  733. AssertEquals('Div1.Top','9px',Div1.Top);
  734. AssertEquals('Div11.Left','',Div11.Left);
  735. AssertEquals('Div11.Top','',Div11.Top);
  736. AssertEquals('Div2.Left','8px',Div2.Left);
  737. AssertEquals('Div2.Top','9px',Div2.Top);
  738. AssertEquals('Button12.Left','',Button12.Left);
  739. AssertEquals('Button12.Top','9px',Button12.Top);
  740. end;
  741. procedure TTestCSSResolver.Test_Selector_NthChild;
  742. var
  743. Div1, Div2, Div3, Div4: TDemoDiv;
  744. begin
  745. Doc.Root:=TDemoNode.Create(nil);
  746. Div1:=TDemoDiv.Create(Doc);
  747. Div1.Parent:=Doc.Root;
  748. Div2:=TDemoDiv.Create(Doc);
  749. Div2.Parent:=Doc.Root;
  750. Div3:=TDemoDiv.Create(Doc);
  751. Div3.Parent:=Doc.Root;
  752. Div4:=TDemoDiv.Create(Doc);
  753. Div4.Parent:=Doc.Root;
  754. Doc.Style:=LinesToStr([
  755. ':nth-child(2n+1) { left: 8px; }',
  756. ':nth-child(n+3) { border: 6px; }',
  757. ':nth-child(-n+2) { display: inline; }',
  758. ':nth-child(even) { top: 3px; }',
  759. ':nth-child(odd) { width: 4px; }',
  760. '']);
  761. Doc.ApplyStyle;
  762. AssertEquals('Root.Left','',Doc.Root.Left);
  763. AssertEquals('Root.Border','',Doc.Root.Border);
  764. AssertEquals('Root.Display','',Doc.Root.Display);
  765. AssertEquals('Root.Top','',Doc.Root.Top);
  766. AssertEquals('Root.Width','',Doc.Root.Width);
  767. AssertEquals('Div1.Left','8px',Div1.Left);
  768. AssertEquals('Div1.Border','',Div1.Border);
  769. AssertEquals('Div1.Display','inline',Div1.Display);
  770. AssertEquals('Div1.Top','',Div1.Top);
  771. AssertEquals('Div1.Width','4px',Div1.Width);
  772. AssertEquals('Div2.Left','',Div2.Left);
  773. AssertEquals('Div2.Border','',Div2.Border);
  774. AssertEquals('Div2.Display','inline',Div2.Display);
  775. AssertEquals('Div2.Top','3px',Div2.Top);
  776. AssertEquals('Div2.Width','',Div2.Width);
  777. AssertEquals('Div3.Left','8px',Div3.Left);
  778. AssertEquals('Div3.Border','6px',Div3.Border);
  779. AssertEquals('Div3.Display','',Div3.Display);
  780. AssertEquals('Div3.Top','',Div3.Top);
  781. AssertEquals('Div3.Width','4px',Div3.Width);
  782. AssertEquals('Div4.Left','',Div4.Left);
  783. AssertEquals('Div4.Border','6px',Div4.Border);
  784. AssertEquals('Div4.Display','',Div4.Display);
  785. AssertEquals('Div4.Top','3px',Div4.Top);
  786. AssertEquals('Div4.Width','',Div4.Width);
  787. end;
  788. procedure TTestCSSResolver.Test_Selector_NthLastChild;
  789. var
  790. Div1, Div2, Div3, Div4: TDemoDiv;
  791. begin
  792. Doc.Root:=TDemoNode.Create(nil);
  793. Div1:=TDemoDiv.Create(Doc);
  794. Div1.Parent:=Doc.Root;
  795. Div2:=TDemoDiv.Create(Doc);
  796. Div2.Parent:=Doc.Root;
  797. Div3:=TDemoDiv.Create(Doc);
  798. Div3.Parent:=Doc.Root;
  799. Div4:=TDemoDiv.Create(Doc);
  800. Div4.Parent:=Doc.Root;
  801. Doc.Style:=LinesToStr([
  802. ':nth-last-child(2n+1) { left: 8px; }',
  803. '']);
  804. Doc.ApplyStyle;
  805. AssertEquals('Root.Left','',Doc.Root.Left);
  806. AssertEquals('Div1.Left','',Div1.Left);
  807. AssertEquals('Div2.Left','8px',Div2.Left);
  808. AssertEquals('Div3.Left','',Div3.Left);
  809. AssertEquals('Div4.Left','8px',Div4.Left);
  810. end;
  811. procedure TTestCSSResolver.Test_Selector_NthChildOf;
  812. var
  813. Div1, Div2, Div3, Div4: TDemoDiv;
  814. begin
  815. Doc.Root:=TDemoNode.Create(nil);
  816. Div1:=TDemoDiv.Create(Doc);
  817. Div1.Parent:=Doc.Root;
  818. Div2:=TDemoDiv.Create(Doc);
  819. Div2.Parent:=Doc.Root;
  820. Div2.Top:='3px';
  821. Div3:=TDemoDiv.Create(Doc);
  822. Div3.Parent:=Doc.Root;
  823. Div3.Top:='3px';
  824. Div4:=TDemoDiv.Create(Doc);
  825. Div4.Parent:=Doc.Root;
  826. Div4.Top:='3px';
  827. Doc.Style:=LinesToStr([
  828. ':nth-child(2n+1 of [top=3px]) { left: 5px; }',
  829. '']);
  830. Doc.ApplyStyle;
  831. AssertEquals('Root.Left','',Doc.Root.Left);
  832. AssertEquals('Div1.Left','',Div1.Left);
  833. AssertEquals('Div2.Left','5px',Div2.Left);
  834. AssertEquals('Div3.Left','',Div3.Left);
  835. AssertEquals('Div4.Left','5px',Div4.Left);
  836. end;
  837. procedure TTestCSSResolver.Test_Selector_FirstOfType;
  838. var
  839. Div1, Div11, Div13, Div2: TDemoDiv;
  840. Button12: TDemoButton;
  841. begin
  842. Doc.Root:=TDemoNode.Create(nil);
  843. Div1:=TDemoDiv.Create(Doc);
  844. Div1.Parent:=Doc.Root;
  845. Div11:=TDemoDiv.Create(Doc);
  846. Div11.Parent:=Div1;
  847. Button12:=TDemoButton.Create(Doc);
  848. Button12.Parent:=Div1;
  849. Div13:=TDemoDiv.Create(Doc);
  850. Div13.Parent:=Div1;
  851. Div2:=TDemoDiv.Create(Doc);
  852. Div2.Parent:=Doc.Root;
  853. Doc.Style:=LinesToStr([
  854. ':first-of-type { left: 6px; }',
  855. 'div:first-of-type { top: 7px; }',
  856. '']);
  857. Doc.ApplyStyle;
  858. AssertEquals('Root.Left','6px',Doc.Root.Left);
  859. AssertEquals('Root.Top','',Doc.Root.Top);
  860. AssertEquals('Div1.Left','6px',Div1.Left);
  861. AssertEquals('Div1.Top','7px',Div1.Top);
  862. AssertEquals('Div11.Left','6px',Div11.Left);
  863. AssertEquals('Div11.Top','7px',Div11.Top);
  864. AssertEquals('Button12.Left','6px',Button12.Left);
  865. AssertEquals('Button12.Top','',Button12.Top);
  866. AssertEquals('Div13.Left','',Div13.Left);
  867. AssertEquals('Div13.Top','',Div13.Top);
  868. AssertEquals('Div2.Left','',Div2.Left);
  869. AssertEquals('Div2.Top','',Div2.Top);
  870. end;
  871. procedure TTestCSSResolver.Test_Selector_LastOfType;
  872. var
  873. Div1, Div11, Div13, Div2: TDemoDiv;
  874. Button12: TDemoButton;
  875. begin
  876. Doc.Root:=TDemoNode.Create(nil);
  877. Div1:=TDemoDiv.Create(Doc);
  878. Div1.Parent:=Doc.Root;
  879. Div11:=TDemoDiv.Create(Doc);
  880. Div11.Parent:=Div1;
  881. Button12:=TDemoButton.Create(Doc);
  882. Button12.Parent:=Div1;
  883. Div13:=TDemoDiv.Create(Doc);
  884. Div13.Parent:=Div1;
  885. Div2:=TDemoDiv.Create(Doc);
  886. Div2.Parent:=Doc.Root;
  887. Doc.Style:=LinesToStr([
  888. ':last-of-type { left: 6px; }',
  889. 'div:last-of-type { top: 7px; }',
  890. '']);
  891. Doc.ApplyStyle;
  892. AssertEquals('Root.Left','6px',Doc.Root.Left);
  893. AssertEquals('Root.Top','',Doc.Root.Top);
  894. AssertEquals('Div1.Left','',Div1.Left);
  895. AssertEquals('Div1.Top','',Div1.Top);
  896. AssertEquals('Div11.Left','',Div11.Left);
  897. AssertEquals('Div11.Top','',Div11.Top);
  898. AssertEquals('Button12.Left','6px',Button12.Left);
  899. AssertEquals('Button12.Top','',Button12.Top);
  900. AssertEquals('Div13.Left','6px',Div13.Left);
  901. AssertEquals('Div13.Top','7px',Div13.Top);
  902. AssertEquals('Div2.Left','6px',Div2.Left);
  903. AssertEquals('Div2.Top','7px',Div2.Top);
  904. end;
  905. procedure TTestCSSResolver.Test_Selector_OnlyOfType;
  906. var
  907. Div1, Div11, Div2: TDemoDiv;
  908. Button12: TDemoButton;
  909. begin
  910. Doc.Root:=TDemoNode.Create(nil);
  911. Div1:=TDemoDiv.Create(Doc);
  912. Div1.Parent:=Doc.Root;
  913. Div11:=TDemoDiv.Create(Doc);
  914. Div11.Parent:=Div1;
  915. Button12:=TDemoButton.Create(Doc);
  916. Button12.Parent:=Div1;
  917. Div2:=TDemoDiv.Create(Doc);
  918. Div2.Parent:=Doc.Root;
  919. Doc.Style:=LinesToStr([
  920. ':only-of-type { left: 6px; }',
  921. 'div:only-of-type { top: 7px; }',
  922. '']);
  923. Doc.ApplyStyle;
  924. AssertEquals('Root.Left','6px',Doc.Root.Left);
  925. AssertEquals('Root.Top','',Doc.Root.Top);
  926. AssertEquals('Div1.Left','',Div1.Left);
  927. AssertEquals('Div1.Top','',Div1.Top);
  928. AssertEquals('Div11.Left','6px',Div11.Left);
  929. AssertEquals('Div11.Top','7px',Div11.Top);
  930. AssertEquals('Button12.Left','6px',Button12.Left);
  931. AssertEquals('Button12.Top','',Button12.Top);
  932. AssertEquals('Div2.Left','',Div2.Left);
  933. AssertEquals('Div2.Top','',Div2.Top);
  934. end;
  935. procedure TTestCSSResolver.Test_Selector_NthOfType;
  936. var
  937. Div1, Div2, Div3, Div4: TDemoDiv;
  938. Button1, Button2: TDemoButton;
  939. begin
  940. Doc.Root:=TDemoNode.Create(nil);
  941. Div1:=TDemoDiv.Create(Doc);
  942. Div1.Parent:=Doc.Root;
  943. Button1:=TDemoButton.Create(Doc);
  944. Button1.Parent:=Doc.Root;
  945. Div2:=TDemoDiv.Create(Doc);
  946. Div2.Parent:=Doc.Root;
  947. Div3:=TDemoDiv.Create(Doc);
  948. Div3.Parent:=Doc.Root;
  949. Button2:=TDemoButton.Create(Doc);
  950. Button2.Parent:=Doc.Root;
  951. Div4:=TDemoDiv.Create(Doc);
  952. Div4.Parent:=Doc.Root;
  953. Doc.Style:=LinesToStr([
  954. ':nth-of-type(2n+1) { left: 8px; }',
  955. '']);
  956. Doc.ApplyStyle;
  957. AssertEquals('Root.Left','',Doc.Root.Left);
  958. AssertEquals('Div1.Left','8px',Div1.Left);
  959. AssertEquals('Button1.Left','8px',Button1.Left);
  960. AssertEquals('Div2.Left','',Div2.Left);
  961. AssertEquals('Div3.Left','8px',Div3.Left);
  962. AssertEquals('Button2.Left','',Button2.Left);
  963. AssertEquals('Div4.Left','',Div4.Left);
  964. end;
  965. procedure TTestCSSResolver.Test_Selector_NthLastOfType;
  966. var
  967. Div1, Div2, Div3, Div4: TDemoDiv;
  968. Button1, Button2: TDemoButton;
  969. begin
  970. Doc.Root:=TDemoNode.Create(nil);
  971. Div1:=TDemoDiv.Create(Doc);
  972. Div1.Parent:=Doc.Root;
  973. Button1:=TDemoButton.Create(Doc);
  974. Button1.Parent:=Doc.Root;
  975. Div2:=TDemoDiv.Create(Doc);
  976. Div2.Parent:=Doc.Root;
  977. Div3:=TDemoDiv.Create(Doc);
  978. Div3.Parent:=Doc.Root;
  979. Button2:=TDemoButton.Create(Doc);
  980. Button2.Parent:=Doc.Root;
  981. Div4:=TDemoDiv.Create(Doc);
  982. Div4.Parent:=Doc.Root;
  983. Doc.Style:=LinesToStr([
  984. ':nth-last-of-type(2n+1) { left: 8px; }',
  985. '']);
  986. Doc.ApplyStyle;
  987. AssertEquals('Root.Left','',Doc.Root.Left);
  988. AssertEquals('Div1.Left','',Div1.Left);
  989. AssertEquals('Button1.Left','',Button1.Left);
  990. AssertEquals('Div2.Left','8px',Div2.Left);
  991. AssertEquals('Div3.Left','',Div3.Left);
  992. AssertEquals('Button2.Left','8px',Button2.Left);
  993. AssertEquals('Div4.Left','8px',Div4.Left);
  994. end;
  995. procedure TTestCSSResolver.Test_Selector_Is;
  996. var
  997. Div1, Div2: TDemoDiv;
  998. Button1, Button2: TDemoButton;
  999. begin
  1000. Doc.Root:=TDemoNode.Create(nil);
  1001. Div1:=TDemoDiv.Create(Doc);
  1002. Div1.Parent:=Doc.Root;
  1003. Div1.Top:='3px';
  1004. Button1:=TDemoButton.Create(Doc);
  1005. Button1.Parent:=Doc.Root;
  1006. Div2:=TDemoDiv.Create(Doc);
  1007. Div2.Parent:=Doc.Root;
  1008. Button2:=TDemoButton.Create(Doc);
  1009. Button2.Parent:=Doc.Root;
  1010. Button2.Top:='3px';
  1011. Doc.Style:=LinesToStr([
  1012. ':is(div, button)[top=3px] { left: 7px; }',
  1013. '']);
  1014. Doc.ApplyStyle;
  1015. AssertEquals('Root.Left','',Doc.Root.Left);
  1016. AssertEquals('Div1.Left','7px',Div1.Left);
  1017. AssertEquals('Button1.Left','',Button1.Left);
  1018. AssertEquals('Div2.Left','',Div2.Left);
  1019. AssertEquals('Button2.Left','7px',Button2.Left);
  1020. end;
  1021. procedure TTestCSSResolver.Test_Selector_Where;
  1022. var
  1023. Div1, Div2: TDemoDiv;
  1024. begin
  1025. Doc.Root:=TDemoNode.Create(nil);
  1026. Div1:=TDemoDiv.Create(Doc);
  1027. Div1.Parent:=Doc.Root;
  1028. Div1.Top:='3px';
  1029. Div2:=TDemoDiv.Create(Doc);
  1030. Div2.Parent:=Div1;
  1031. Div2.Top:='3px';
  1032. Doc.Style:=LinesToStr([
  1033. ':where(div[top=3px]) { left: 1px; }',
  1034. 'div div { left: 2px; }',
  1035. '']);
  1036. Doc.ApplyStyle;
  1037. AssertEquals('Root.Left','',Doc.Root.Left);
  1038. AssertEquals('Div1.Left','1px',Div1.Left);
  1039. AssertEquals('Div2.Left','2px',Div2.Left);
  1040. end;
  1041. procedure TTestCSSResolver.Test_InlineStyle;
  1042. var
  1043. Div1: TDemoDiv;
  1044. begin
  1045. Doc.Root:=TDemoNode.Create(nil);
  1046. Div1:=TDemoDiv.Create(Doc);
  1047. Div1.Parent:=Doc.Root;
  1048. Div1.Style:='left: 10px; top: 5px';
  1049. Doc.Style:=LinesToStr([
  1050. 'div { left: 6px; }',
  1051. '']);
  1052. Doc.ApplyStyle;
  1053. AssertEquals('Root.Left','',Doc.Root.Left);
  1054. AssertEquals('Div1.Left','10px',Div1.Left);
  1055. AssertEquals('Div1.Top','5px',Div1.Top);
  1056. end;
  1057. { TDemoDiv }
  1058. class function TDemoDiv.CSSTypeName: TCSSString;
  1059. begin
  1060. Result:='div';
  1061. end;
  1062. class function TDemoDiv.CSSTypeID: TCSSNumericalID;
  1063. begin
  1064. Result:=101;
  1065. end;
  1066. { TDemoSpan }
  1067. class function TDemoSpan.CSSTypeName: TCSSString;
  1068. begin
  1069. Result:='span';
  1070. end;
  1071. class function TDemoSpan.CSSTypeID: TCSSNumericalID;
  1072. begin
  1073. Result:=102;
  1074. end;
  1075. { TDemoButton }
  1076. procedure TDemoButton.SetCaption(const AValue: string);
  1077. begin
  1078. if FCaption=AValue then Exit;
  1079. FCaption:=AValue;
  1080. end;
  1081. class function TDemoButton.CSSTypeName: TCSSString;
  1082. begin
  1083. Result:='button';
  1084. end;
  1085. class function TDemoButton.CSSTypeID: TCSSNumericalID;
  1086. begin
  1087. Result:=103;
  1088. end;
  1089. function TDemoButton.HasCSSAttribute(const AttrID: TCSSNumericalID): boolean;
  1090. begin
  1091. Result:=(AttrID=CSSCaptionID) or inherited HasCSSAttribute(AttrID);
  1092. end;
  1093. function TDemoButton.GetCSSAttribute(const AttrID: TCSSNumericalID): TCSSString;
  1094. begin
  1095. if AttrID=CSSCaptionID then
  1096. Result:=Caption
  1097. else
  1098. Result:=inherited GetCSSAttribute(AttrID);
  1099. end;
  1100. procedure TDemoButton.SetCSSValue(AttrID: TCSSNumericalID; Value: TCSSElement);
  1101. begin
  1102. if AttrID=CSSCaptionID then
  1103. SetCaption(Value.AsString)
  1104. else
  1105. inherited SetCSSValue(AttrID, Value);
  1106. end;
  1107. { TDemoDocument }
  1108. procedure TDemoDocument.SetStyle(const AValue: string);
  1109. var
  1110. ss: TStringStream;
  1111. aParser: TCSSParser;
  1112. begin
  1113. if FStyle=AValue then Exit;
  1114. FStyle:=AValue;
  1115. FreeAndNil(FStyleElements);
  1116. aParser:=nil;
  1117. ss:=TStringStream.Create(Style);
  1118. try
  1119. aParser:=TCSSParser.Create(ss);
  1120. FStyleElements:=aParser.Parse;
  1121. finally
  1122. aParser.Free;
  1123. end;
  1124. end;
  1125. function TDemoDocument.GetNumericalIDs(Kind: TCSSNumericalIDKind
  1126. ): TCSSNumericalIDs;
  1127. begin
  1128. Result:=FNumericalIDs[Kind];
  1129. end;
  1130. procedure TDemoDocument.SetNumericalIDs(Kind: TCSSNumericalIDKind;
  1131. const AValue: TCSSNumericalIDs);
  1132. begin
  1133. FNumericalIDs[Kind]:=AValue;
  1134. end;
  1135. procedure TDemoDocument.SetStyleElements(const AValue: TCSSElement);
  1136. begin
  1137. if FStyleElements=AValue then Exit;
  1138. FStyleElements.Free;
  1139. FStyleElements:=AValue;
  1140. end;
  1141. constructor TDemoDocument.Create(AOwner: TComponent);
  1142. var
  1143. Attr: TDemoNodeAttribute;
  1144. TypeIDs, AttributeIDs: TCSSNumericalIDs;
  1145. NumKind: TCSSNumericalIDKind;
  1146. AttrID: TCSSNumericalID;
  1147. begin
  1148. inherited Create(AOwner);
  1149. for NumKind in TCSSNumericalIDKind do
  1150. FNumericalIDs[NumKind]:=TCSSNumericalIDs.Create(NumKind);
  1151. TypeIDs:=FNumericalIDs[nikType];
  1152. TypeIDs['*']:=CSSTypeID_Universal;
  1153. if TypeIDs['*']<>CSSTypeID_Universal then
  1154. raise Exception.Create('20220909004740');
  1155. TypeIDs[TDemoNode.CSSTypeName]:=TDemoNode.CSSTypeID;
  1156. TypeIDs[TDemoDiv.CSSTypeName]:=TDemoDiv.CSSTypeID;
  1157. TypeIDs[TDemoButton.CSSTypeName]:=TDemoButton.CSSTypeID;
  1158. AttributeIDs:=FNumericalIDs[nikAttribute];
  1159. AttributeIDs['all']:=CSSAttributeID_All;
  1160. AttrID:=DemoAttrIDBase;
  1161. for Attr in TDemoNodeAttribute do
  1162. begin
  1163. AttributeIDs[DemoAttributeNames[Attr]]:=AttrID;
  1164. inc(AttrID);
  1165. end;
  1166. TDemoButton.CSSCaptionID:=AttrID;
  1167. AttributeIDs['caption']:=AttrID;
  1168. inc(AttrID);
  1169. FCSSResolver:=TCSSResolver.Create;
  1170. for NumKind in TCSSNumericalIDKind do
  1171. CSSResolver.NumericalIDs[NumKind]:=FNumericalIDs[NumKind];
  1172. Root:=TDemoNode.Create(Self);
  1173. Root.Name:='Root';
  1174. end;
  1175. destructor TDemoDocument.Destroy;
  1176. var
  1177. NumKind: TCSSNumericalIDKind;
  1178. begin
  1179. FreeAndNil(FCSSResolver);
  1180. FreeAndNil(Root);
  1181. FreeAndNil(FStyleElements);
  1182. for NumKind in TCSSNumericalIDKind do
  1183. FreeAndNil(FNumericalIDs[NumKind]);
  1184. inherited Destroy;
  1185. end;
  1186. procedure TDemoDocument.ApplyStyle;
  1187. procedure Traverse(Node: TDemoNode);
  1188. var
  1189. i: Integer;
  1190. begin
  1191. ApplyStyleToNode(Node);
  1192. for i:=0 to Node.NodeCount-1 do
  1193. Traverse(Node[i]);
  1194. end;
  1195. begin
  1196. if CSSResolver.StyleCount=0 then
  1197. CSSResolver.AddStyle(StyleElements)
  1198. else
  1199. CSSResolver.Styles[0]:=StyleElements;
  1200. Traverse(Root);
  1201. end;
  1202. procedure TDemoDocument.ApplyStyleToNode(Node: TDemoNode);
  1203. begin
  1204. CSSResolver.Compute(Node,Node.StyleElements);
  1205. end;
  1206. { TDemoNode }
  1207. function TDemoNode.GetAttribute(AIndex: TDemoNodeAttribute): string;
  1208. begin
  1209. Result:=FAttributeValues[AIndex];
  1210. end;
  1211. function TDemoNode.GetNodeCount: integer;
  1212. begin
  1213. Result:=FNodes.Count;
  1214. end;
  1215. function TDemoNode.GetNodes(Index: integer): TDemoNode;
  1216. begin
  1217. Result:=TDemoNode(FNodes[Index]);
  1218. end;
  1219. procedure TDemoNode.SetAttribute(AIndex: TDemoNodeAttribute;
  1220. const AValue: string);
  1221. begin
  1222. if FAttributeValues[AIndex]=AValue then exit;
  1223. FAttributeValues[AIndex]:=AValue;
  1224. end;
  1225. procedure TDemoNode.SetParent(const AValue: TDemoNode);
  1226. begin
  1227. if FParent=AValue then Exit;
  1228. if AValue=Self then
  1229. raise Exception.Create('cycle');
  1230. if FParent<>nil then
  1231. begin
  1232. FParent.FNodes.Remove(Self);
  1233. end;
  1234. FParent:=AValue;
  1235. if FParent<>nil then
  1236. begin
  1237. FParent.FNodes.Add(Self);
  1238. FreeNotification(FParent);
  1239. end;
  1240. end;
  1241. procedure TDemoNode.SetStyleElements(const AValue: TCSSElement);
  1242. begin
  1243. if FStyleElements=AValue then Exit;
  1244. FreeAndNil(FStyleElements);
  1245. FStyleElements:=AValue;
  1246. end;
  1247. procedure TDemoNode.SetStyle(const AValue: string);
  1248. var
  1249. ss: TStringStream;
  1250. aParser: TCSSParser;
  1251. begin
  1252. if FStyle=AValue then Exit;
  1253. FStyle:=AValue;
  1254. FreeAndNil(FStyleElements);
  1255. aParser:=nil;
  1256. ss:=TStringStream.Create(Style);
  1257. try
  1258. aParser:=TCSSParser.Create(ss);
  1259. FStyleElements:=aParser.ParseInline;
  1260. finally
  1261. aParser.Free;
  1262. end;
  1263. end;
  1264. procedure TDemoNode.Notification(AComponent: TComponent; Operation: TOperation);
  1265. begin
  1266. inherited Notification(AComponent, Operation);
  1267. if AComponent=Self then exit;
  1268. if Operation=opRemove then
  1269. begin
  1270. if FNodes<>nil then
  1271. FNodes.Remove(AComponent);
  1272. end;
  1273. end;
  1274. constructor TDemoNode.Create(AOwner: TComponent);
  1275. var
  1276. a: TDemoNodeAttribute;
  1277. begin
  1278. inherited Create(AOwner);
  1279. FNodes:=TFPObjectList.Create(false);
  1280. FCSSClasses:=TStringList.Create;
  1281. FCSSClasses.Delimiter:=' ';
  1282. for a in TDemoNodeAttribute do
  1283. FAttributeValues[a]:=FAttributeInitialValues[a];
  1284. end;
  1285. destructor TDemoNode.Destroy;
  1286. begin
  1287. Clear;
  1288. FreeAndNil(FNodes);
  1289. FreeAndNil(FCSSClasses);
  1290. inherited Destroy;
  1291. end;
  1292. procedure TDemoNode.Clear;
  1293. var
  1294. i: Integer;
  1295. begin
  1296. FCSSClasses.Clear;
  1297. for i:=NodeCount-1 downto 0 do
  1298. Nodes[i].Parent:=nil;
  1299. FNodes.Clear;
  1300. end;
  1301. function TDemoNode.GetCSSID: TCSSString;
  1302. begin
  1303. Result:=Name;
  1304. end;
  1305. class function TDemoNode.CSSTypeName: TCSSString;
  1306. begin
  1307. Result:='node';
  1308. end;
  1309. class function TDemoNode.GetAttributeInitialValue(Attr: TDemoNodeAttribute
  1310. ): string;
  1311. begin
  1312. case Attr of
  1313. naLeft: Result:='0px';
  1314. naTop: Result:='0px';
  1315. naWidth: Result:='';
  1316. naHeight: Result:='';
  1317. naBorder: Result:='1px';
  1318. naDisplay: Result:='inline';
  1319. naColor: Result:='#000';
  1320. end;
  1321. end;
  1322. function TDemoNode.HasCSSClass(const aClassName: TCSSString): boolean;
  1323. var
  1324. i: Integer;
  1325. begin
  1326. for i:=0 to CSSClasses.Count-1 do
  1327. if aClassName=CSSClasses[i] then
  1328. exit(true);
  1329. Result:=false;
  1330. end;
  1331. function TDemoNode.CheckCSSValue(AttrID: TCSSNumericalID; Value: TCSSElement
  1332. ): boolean;
  1333. begin
  1334. if (AttrID<DemoAttrIDBase) or (AttrID>ord(High(TDemoNodeAttribute))+DemoAttrIDBase) then
  1335. exit(false);
  1336. Result:=Value<>nil;
  1337. end;
  1338. procedure TDemoNode.SetCSSValue(AttrID: TCSSNumericalID; Value: TCSSElement);
  1339. var
  1340. Attr: TDemoNodeAttribute;
  1341. s: TCSSString;
  1342. begin
  1343. if (AttrID<DemoAttrIDBase) or (AttrID>ord(High(TDemoNodeAttribute))+DemoAttrIDBase) then
  1344. raise Exception.Create('TDemoNode.SetCSSValue invalid AttrID '+IntToStr(AttrID));
  1345. Attr:=TDemoNodeAttribute(AttrID-DemoAttrIDBase);
  1346. s:=Value.AsString;
  1347. {$IFDEF VerboseCSSResolver}
  1348. writeln('TDemoNode.SetCSSValue ',DemoAttributeNames[Attr],':="',s,'"');
  1349. {$ENDIF}
  1350. Attribute[Attr]:=s;
  1351. end;
  1352. function TDemoNode.GetCSSParent: ICSSNode;
  1353. begin
  1354. Result:=Parent;
  1355. end;
  1356. function TDemoNode.GetCSSIndex: integer;
  1357. begin
  1358. if Parent=nil then
  1359. Result:=-1
  1360. else
  1361. Result:=Parent.FNodes.IndexOf(Self);
  1362. end;
  1363. function TDemoNode.GetCSSNextSibling: ICSSNode;
  1364. var
  1365. i: Integer;
  1366. begin
  1367. i:=GetCSSIndex;
  1368. if (i<0) or (i+1>=Parent.NodeCount) then
  1369. Result:=nil
  1370. else
  1371. Result:=Parent.Nodes[i+1];
  1372. end;
  1373. function TDemoNode.GetCSSPreviousSibling: ICSSNode;
  1374. var
  1375. i: Integer;
  1376. begin
  1377. i:=GetCSSIndex;
  1378. if i<1 then
  1379. Result:=nil
  1380. else
  1381. Result:=Parent.Nodes[i-1];
  1382. end;
  1383. function TDemoNode.GetCSSChildCount: integer;
  1384. begin
  1385. Result:=NodeCount;
  1386. end;
  1387. function TDemoNode.GetCSSChild(const anIndex: integer): ICSSNode;
  1388. begin
  1389. Result:=Nodes[anIndex];
  1390. end;
  1391. function TDemoNode.GetCSSNextOfType: ICSSNode;
  1392. var
  1393. i, Cnt: Integer;
  1394. MyID: TCSSNumericalID;
  1395. aNode: TDemoNode;
  1396. begin
  1397. Result:=nil;
  1398. i:=GetCSSIndex;
  1399. if i<0 then exit;
  1400. inc(i);
  1401. MyID:=CSSTypeID;
  1402. Cnt:=Parent.NodeCount;
  1403. while i<Cnt do
  1404. begin
  1405. aNode:=Parent.Nodes[i];
  1406. if aNode.CSSTypeID=MyID then
  1407. exit(aNode);
  1408. inc(i);
  1409. end;
  1410. end;
  1411. function TDemoNode.GetCSSPreviousOfType: ICSSNode;
  1412. var
  1413. i: Integer;
  1414. MyID: TCSSNumericalID;
  1415. aNode: TDemoNode;
  1416. begin
  1417. Result:=nil;
  1418. i:=GetCSSIndex;
  1419. if i<0 then exit;
  1420. dec(i);
  1421. MyID:=CSSTypeID;
  1422. while i>=0 do
  1423. begin
  1424. aNode:=Parent.Nodes[i];
  1425. if aNode.CSSTypeID=MyID then
  1426. exit(aNode);
  1427. dec(i);
  1428. end;
  1429. end;
  1430. function TDemoNode.GetCSSAttributeClass: TCSSString;
  1431. begin
  1432. FCSSClasses.Delimiter:=' ';
  1433. Result:=FCSSClasses.DelimitedText;
  1434. end;
  1435. function TDemoNode.HasCSSAttribute(const AttrID: TCSSNumericalID): boolean;
  1436. begin
  1437. Result:=(AttrID>=DemoAttrIDBase) and (AttrID<=DemoAttrIDBase+ord(High(TDemoNodeAttribute)));
  1438. end;
  1439. function TDemoNode.GetCSSAttribute(const AttrID: TCSSNumericalID): TCSSString;
  1440. var
  1441. Attr: TDemoNodeAttribute;
  1442. begin
  1443. if (AttrID<DemoAttrIDBase) or (AttrID>DemoAttrIDBase+ord(High(TDemoNodeAttribute))) then
  1444. exit('');
  1445. Attr:=TDemoNodeAttribute(AttrID-DemoAttrIDBase);
  1446. Result:=Attribute[Attr];
  1447. end;
  1448. function TDemoNode.HasCSSPseudo(const AttrID: TCSSNumericalID
  1449. ): boolean;
  1450. begin
  1451. Result:=false;
  1452. end;
  1453. function TDemoNode.GetCSSPseudo(const AttrID: TCSSNumericalID
  1454. ): TCSSString;
  1455. begin
  1456. Result:='';
  1457. end;
  1458. function TDemoNode.GetCSSEmpty: boolean;
  1459. begin
  1460. Result:=NodeCount=0;
  1461. end;
  1462. function TDemoNode.GetCSSDepth: integer;
  1463. var
  1464. Node: TDemoNode;
  1465. begin
  1466. Result:=0;
  1467. Node:=Parent;
  1468. while Node<>nil do
  1469. begin
  1470. inc(Result);
  1471. Node:=Node.Parent;
  1472. end;
  1473. end;
  1474. function TDemoNode.GetCSSTypeName: TCSSString;
  1475. begin
  1476. Result:=CSSTypeName;
  1477. end;
  1478. class function TDemoNode.CSSTypeID: TCSSNumericalID;
  1479. begin
  1480. Result:=100;
  1481. end;
  1482. function TDemoNode.GetCSSTypeID: TCSSNumericalID;
  1483. begin
  1484. Result:=CSSTypeID;
  1485. end;
  1486. initialization
  1487. RegisterTests([TTestCSSResolver]);
  1488. end.