tccssresolver.pp 47 KB

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