tccssresolver.pp 72 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724
  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. {$IF FPC_FULLVERSION>30300}
  14. {$WARN 6060 off : Case statement does not handle all possible cases}
  15. {$ENDIF}
  16. interface
  17. uses
  18. Classes, SysUtils, Contnrs, fpcunit, testregistry, fpCSSTree,
  19. fpCSSResParser, fpCSSResolver;
  20. type
  21. TDemoNodeAttribute = (
  22. naNone,
  23. naLeft,
  24. naTop,
  25. naWidth,
  26. naHeight,
  27. naBorderWidth,
  28. naBorderColor,
  29. naBorder, // shorthand after longhands
  30. naDisplay,
  31. naColor,
  32. naBackground,
  33. naDirection
  34. );
  35. TDemoNodeAttributes = set of TDemoNodeAttribute;
  36. const
  37. DemoAttributeNames: array[TDemoNodeAttribute] of TCSSString = (
  38. // case sensitive!
  39. '?',
  40. 'left',
  41. 'top',
  42. 'width',
  43. 'height',
  44. 'border-width',
  45. 'border-color',
  46. 'border',
  47. 'display',
  48. 'color',
  49. 'background',
  50. 'direction'
  51. );
  52. DemoAttributesInherited = [naBackground,naColor,naBorderColor];
  53. DemoAttributesNotAll = [naDirection];
  54. DemoAttributeInitialValues: array[TDemoNodeAttribute] of TCSSString = (
  55. '?',
  56. 'auto', // left
  57. 'auto', // top
  58. 'auto', // width
  59. 'auto', // height
  60. '0px', // border-width
  61. 'none', // border-color
  62. 'none', // border
  63. 'inline', // display
  64. 'none', // color
  65. 'none', // background
  66. 'auto' // direction
  67. );
  68. type
  69. TDemoPseudoClass = (
  70. pcActive,
  71. pcHover
  72. );
  73. TDemoPseudoClasses = set of TDemoPseudoClass;
  74. const
  75. DemoPseudoClassNames: array[TDemoPseudoClass] of TCSSString = (
  76. // case sensitive!
  77. 'active',
  78. 'hover'
  79. );
  80. type
  81. TDemoElementType = (
  82. detNode,
  83. detDiv,
  84. detSpan,
  85. detButton
  86. );
  87. const
  88. DemoElementTypeNames: array[TDemoElementType] of TCSSString = (
  89. // case sensitive!
  90. 'node',
  91. 'div',
  92. 'span',
  93. 'button'
  94. );
  95. type
  96. TDemoNode = class;
  97. { TDemoCSSAttributeDesc }
  98. TDemoCSSAttributeDesc = class(TCSSAttributeDesc)
  99. public
  100. type
  101. TComputeEvent = procedure(Resolver: TCSSResolver; Node: TDemoNode; Value: TCSSAttributeValue) of object;
  102. public
  103. DemoID: TDemoNodeAttribute;
  104. OnCompute: TComputeEvent;
  105. end;
  106. { TDemoCSSPseudoClassDesc }
  107. TDemoCSSPseudoClassDesc = class(TCSSPseudoClassDesc)
  108. public
  109. DemoID: TDemoPseudoClass;
  110. end;
  111. { TDemoCSSTypeDesc }
  112. TDemoCSSTypeDesc = class(TCSSTypeDesc)
  113. public
  114. DemoID: TDemoElementType;
  115. end;
  116. { TDemoCSSRegistry }
  117. TDemoCSSRegistry = class(TCSSRegistry)
  118. private
  119. // check attribute declarations for validity
  120. function OnCheck_BorderColor(Resolver: TCSSBaseResolver): boolean;
  121. function OnCheck_BorderWidth(Resolver: TCSSBaseResolver): boolean;
  122. function OnCheck_Border(Resolver: TCSSBaseResolver): boolean;
  123. function OnCheck_Direction(Resolver: TCSSBaseResolver): boolean;
  124. function OnCheck_Display(Resolver: TCSSBaseResolver): boolean;
  125. function OnCheck_LeftTop(Resolver: TCSSBaseResolver): boolean;
  126. function OnCheck_WidthHeight(Resolver: TCSSBaseResolver): boolean;
  127. // clean up and normalize attribute values
  128. procedure OnCompute_Direction(Resolver: TCSSResolver; Node: TDemoNode;
  129. Value: TCSSAttributeValue);
  130. procedure OnCompute_LeftTop(Resolver: TCSSResolver; Node: TDemoNode;
  131. Value: TCSSAttributeValue);
  132. procedure OnCompute_WidthHeight(Resolver: TCSSResolver; Node: TDemoNode;
  133. Value: TCSSAttributeValue);
  134. // split shorthands into longhands
  135. procedure OnSplit_Border(Resolver: TCSSBaseResolver;
  136. var AttrIDs: TCSSNumericalIDArray; var Values: TCSSStringArray);
  137. public
  138. DemoAttrIDBase: TCSSNumericalID;
  139. DemoPseudoClassIDBase: TCSSNumericalID;
  140. DemoElementTypeIDBase: TCSSNumericalID;
  141. DemoAttrs: array[TDemoNodeAttribute] of TDemoCSSAttributeDesc;
  142. DemoPseudoClasses: array[TDemoPseudoClass] of TDemoCSSPseudoClassDesc;
  143. DemoTypes: array[TDemoElementType] of TDemoCSSTypeDesc;
  144. // keywords
  145. kwRed,
  146. kwGreen,
  147. kwBlue,
  148. kwWhite,
  149. kwBlack,
  150. kwBlock,
  151. kwInline_Block,
  152. kwLTR,
  153. kwRTL: TCSSNumericalID;
  154. // check parameters
  155. Chk_BorderWidth: TCSSCheckAttrParams_Dimension;
  156. Chk_DirectionAllowedKeywordIDs: TCSSNumericalIDArray;
  157. Chk_DisplayAllowedKeywordIDs: TCSSNumericalIDArray;
  158. Chk_LeftTop: TCSSCheckAttrParams_Dimension;
  159. Chk_WidthHeight: TCSSCheckAttrParams_Dimension;
  160. constructor Create;
  161. function AddDemoAttr(Attr: TDemoNodeAttribute): TDemoCSSAttributeDesc;
  162. function AddDemoPseudoClass(PC: TDemoPseudoClass): TDemoCSSPseudoClassDesc;
  163. function AddDemoType(aType: TDemoElementType): TDemoCSSTypeDesc;
  164. end;
  165. { TDemoNode }
  166. TDemoNode = class(TComponent,ICSSNode)
  167. private
  168. class var CSSRegistry: TDemoCSSRegistry;
  169. class var FDemoNodeTypeID: TCSSNumericalID;
  170. private
  171. FNodes: TFPObjectList; // list of TDemoNode
  172. FCSSClasses: TStrings;
  173. FParent: TDemoNode;
  174. FPseudoClasses: array [TDemoPseudoClass] of boolean;
  175. FInlineStyleElements: TCSSRuleElement;
  176. FInlineStyle: TCSSString;
  177. function GetAttribute(DemoAttr: TDemoNodeAttribute): TCSSString;
  178. function GetNodeCount: integer;
  179. function GetNodes(Index: integer): TDemoNode;
  180. function GetPseudoClasses(PseudoClass: TDemoPseudoClass): boolean;
  181. procedure SetParent(const AValue: TDemoNode);
  182. procedure SetInlineStyleElements(const AValue: TCSSRuleElement);
  183. procedure SetInlineStyle(const AValue: TCSSString);
  184. procedure SetPseudoClasses(PseudoClass: TDemoPseudoClass; const AValue: boolean);
  185. protected
  186. procedure Notification(AComponent: TComponent; Operation: TOperation);
  187. override;
  188. public
  189. // computed by resolver:
  190. Rules: TCSSSharedRuleList; // owned by resolver
  191. Values: TCSSAttributeValues;
  192. ExplicitAttributes: array[TDemoNodeAttribute] of TCSSString;
  193. constructor Create(AOwner: TComponent); override;
  194. destructor Destroy; override;
  195. procedure Clear;
  196. procedure ApplyCSS(Resolver: TCSSResolver); virtual;
  197. class function CSSTypeName: TCSSString; virtual;
  198. class function GetClassCSSTypeID: TCSSNumericalID; virtual;
  199. class procedure SetClassCSSTypeID(aID: TCSSNumericalID); virtual;
  200. class function GetCSSTypeStyle: TCSSString; virtual;
  201. // ICSSNode interface:
  202. function GetCSSID: TCSSString; virtual;
  203. function GetCSSTypeName: TCSSString;
  204. function GetCSSTypeID: TCSSNumericalID;
  205. function HasCSSClass(const aClassName: TCSSString): boolean; virtual;
  206. function GetCSSParent: ICSSNode; virtual;
  207. function GetCSSIndex: integer; virtual;
  208. function GetCSSNextSibling: ICSSNode; virtual;
  209. function GetCSSPreviousSibling: ICSSNode; virtual;
  210. function GetCSSChildCount: integer; virtual;
  211. function GetCSSChild(const anIndex: integer): ICSSNode; virtual;
  212. function GetCSSNextOfType: ICSSNode; virtual;
  213. function GetCSSPreviousOfType: ICSSNode; virtual;
  214. function GetCSSAttributeClass: TCSSString; virtual;
  215. function GetCSSCustomAttribute(const AttrID: TCSSNumericalID): TCSSString; virtual;
  216. function HasCSSExplicitAttribute(const AttrID: TCSSNumericalID): boolean; virtual;
  217. function GetCSSExplicitAttribute(const AttrID: TCSSNumericalID): TCSSString; virtual;
  218. function HasCSSPseudoClass(const {%H-}AttrID: TCSSNumericalID): boolean; virtual;
  219. function GetCSSEmpty: boolean; virtual;
  220. function GetCSSDepth: integer; virtual;
  221. property Parent: TDemoNode read FParent write SetParent;
  222. property NodeCount: integer read GetNodeCount;
  223. property Nodes[Index: integer]: TDemoNode read GetNodes; default;
  224. property CSSClasses: TStrings read FCSSClasses;
  225. property InlineStyleElement: TCSSRuleElement read FInlineStyleElements write SetInlineStyleElements;
  226. property InlineStyle: TCSSString read FInlineStyle write SetInlineStyle;
  227. // CSS attributes
  228. property Left: TCSSString index naLeft read GetAttribute;
  229. property Top: TCSSString index naTop read GetAttribute;
  230. property Width: TCSSString index naWidth read GetAttribute;
  231. property Height: TCSSString index naHeight read GetAttribute;
  232. property Border: TCSSString index naBorder read GetAttribute;
  233. property BorderWidth: TCSSString index naBorderWidth read GetAttribute;
  234. property BorderColor: TCSSString index naBorderColor read GetAttribute;
  235. property Display: TCSSString index naDisplay read GetAttribute;
  236. property Color: TCSSString index naColor read GetAttribute;
  237. property Background: TCSSString index naBackground read GetAttribute;
  238. property Direction: TCSSString index naDirection read GetAttribute;
  239. property Attribute[Attr: TDemoNodeAttribute]: TCSSString read GetAttribute;
  240. // CSS pseudo classes
  241. property Active: boolean index pcActive read GetPseudoClasses write SetPseudoClasses;
  242. property Hover: boolean index pcHover read GetPseudoClasses write SetPseudoClasses;
  243. property HasPseudoClass[PseudoClass: TDemoPseudoClass]: boolean read GetPseudoClasses write SetPseudoClasses;
  244. end;
  245. TDemoNodeClass = class of TDemoNode;
  246. { TDemoDiv }
  247. TDemoDiv = class(TDemoNode)
  248. private
  249. class var FDemoDivTypeID: TCSSNumericalID;
  250. public
  251. class function CSSTypeName: TCSSString; override;
  252. class function GetClassCSSTypeID: TCSSNumericalID; override;
  253. class procedure SetClassCSSTypeID(aID: TCSSNumericalID); override;
  254. class function GetCSSTypeStyle: TCSSString; override;
  255. end;
  256. { TDemoSpan }
  257. TDemoSpan = class(TDemoNode)
  258. private
  259. class var FDemoSpanTypeID: TCSSNumericalID;
  260. public
  261. class function CSSTypeName: TCSSString; override;
  262. class function GetClassCSSTypeID: TCSSNumericalID; override;
  263. class procedure SetClassCSSTypeID(aID: TCSSNumericalID); override;
  264. class function GetCSSTypeStyle: TCSSString; override;
  265. end;
  266. { TDemoButton }
  267. TDemoButton = class(TDemoNode)
  268. private
  269. FCaption: TCSSString;
  270. class var FDemoButtonTypeID: TCSSNumericalID;
  271. procedure SetCaption(const AValue: TCSSString);
  272. public
  273. ExplicitCaption: TCSSString;
  274. class var CSSCaptionID: TCSSNumericalID;
  275. class function CSSTypeName: TCSSString; override;
  276. class function GetClassCSSTypeID: TCSSNumericalID; override;
  277. class procedure SetClassCSSTypeID(aID: TCSSNumericalID); override;
  278. class function GetCSSTypeStyle: TCSSString; override;
  279. function HasCSSExplicitAttribute(const AttrID: TCSSNumericalID): boolean; override;
  280. function GetCSSExplicitAttribute(const AttrID: TCSSNumericalID): TCSSString; override;
  281. property Caption: TCSSString read FCaption write SetCaption;
  282. end;
  283. { TDemoDocument }
  284. TDemoDocument = class(TComponent)
  285. private
  286. FCSSResolver: TCSSResolver;
  287. FStyle: TCSSString;
  288. procedure OnResolverLog(Sender: TObject; Entry: TCSSResolverLogEntry);
  289. protected
  290. procedure ApplyTypeStyles; virtual;
  291. procedure SetStyle(const AValue: TCSSString); virtual;
  292. public
  293. Root: TDemoNode;
  294. constructor Create(AOwner: TComponent); override;
  295. destructor Destroy; override;
  296. procedure ApplyStyle; virtual;
  297. property Style: TCSSString read FStyle write SetStyle;
  298. property CSSResolver: TCSSResolver read FCSSResolver;
  299. end;
  300. { TCustomTestNewCSSResolver }
  301. TCustomTestNewCSSResolver = class(TTestCase)
  302. Private
  303. FDoc: TDemoDocument;
  304. protected
  305. procedure SetUp; override;
  306. procedure TearDown; override;
  307. procedure ApplyStyle; virtual;
  308. procedure CheckWarnings; virtual;
  309. public
  310. property Doc: TDemoDocument read FDoc;
  311. end;
  312. { TTestNewCSSResolver }
  313. TTestNewCSSResolver = class(TCustomTestNewCSSResolver)
  314. published
  315. // invalid attributes while parsing stylesheet
  316. procedure Test_ParseAttr_Keyword;
  317. procedure Test_ParseAttr_Keyword_SkipInvalid;
  318. procedure Test_ParseAttr_Float;
  319. procedure Test_ParseAttr_Float_SkipInvalid; // todo
  320. procedure Test_Selector_Universal;
  321. procedure Test_Selector_Type;
  322. procedure Test_Selector_Type_Spaces;
  323. procedure Test_Selector_Id;
  324. procedure Test_Selector_Class;
  325. procedure Test_Selector_ClassClass; // AND combinator
  326. procedure Test_Selector_ClassSpaceClass; // Descendant combinator
  327. procedure Test_Selector_TypeCommaType; // OR combinator
  328. procedure Test_Selector_ClassGTClass; // child combinator
  329. procedure Test_Selector_TypePlusType; // adjacent sibling combinator
  330. procedure Test_Selector_TypeTildeType; // general sibling combinator
  331. // explicit attributes, e.g. set by HTML
  332. procedure Test_Selector_HasAttribute;
  333. procedure Test_Selector_AttributeEquals;
  334. procedure Test_Selector_AttributeEqualsI;
  335. procedure Test_Selector_AttributeBeginsWith;
  336. procedure Test_Selector_AttributeEndsWith;
  337. procedure Test_Selector_AttributeBeginsWithHyphen;
  338. procedure Test_Selector_AttributeContainsWord;
  339. procedure Test_Selector_AttributeContainsSubstring;
  340. // pseudo classes and functions
  341. // test unknown pseudo class
  342. // test unknown pseudo function
  343. procedure Test_Selector_Root;
  344. procedure Test_Selector_Empty;
  345. procedure Test_Selector_FirstChild;
  346. procedure Test_Selector_LastChild;
  347. procedure Test_Selector_OnlyChild;
  348. procedure Test_Selector_Not;
  349. procedure Test_Selector_NthChild;
  350. procedure Test_Selector_NthLastChild;
  351. procedure Test_Selector_NthChildOf;
  352. procedure Test_Selector_FirstOfType;
  353. procedure Test_Selector_LastOfType;
  354. procedure Test_Selector_OnlyOfType;
  355. procedure Test_Selector_NthOfType;
  356. procedure Test_Selector_NthLastOfType;
  357. procedure Test_Selector_Is;
  358. // ToDo: procedure Test_Selector_Is_Descendant; :is(div button, .hawk .eagle)
  359. procedure Test_Selector_Where;
  360. // ToDo: div:has(>img)
  361. // ToDo: div:has(+img)
  362. // custom pseudo classes and functions
  363. procedure Test_Selector_Hover;
  364. // inline style
  365. procedure Test_InlineStyle;
  366. // specifity
  367. procedure Test_Specifity_Id_Class;
  368. procedure Test_Specifity_Important;
  369. procedure Test_Specifity_Shorthand_OneRule;
  370. procedure Test_Specifity_Shorthand_ClassClass;
  371. procedure Test_Specifity_Longhand_All_Longhand;
  372. procedure Test_Specifity_Shorthand_All_Shorthand;
  373. // origin
  374. procedure Test_Origin_Id_Class;
  375. // var()
  376. procedure Test_Var_NoDefault;
  377. procedure Test_Var_Inline_NoDefault;
  378. procedure Test_Var_Defaults;
  379. // skipping for forward compatibility
  380. // ToDo: invalid token in selector makes selector invalid
  381. // ToDo: invalid domain in attribute value is skipped
  382. // ToDo: invalid keyword in attribute value is skipped
  383. // ToDo: invalid keyword in attribute value is skipped
  384. // test skip invalid value color: 3 red;
  385. // test skip invalid attribute color: 3;
  386. // pseudo elements
  387. end;
  388. function LinesToStr(const Args: array of const): TCSSString;
  389. implementation
  390. function LinesToStr(const Args: array of const): TCSSString;
  391. var
  392. s: TCSSString;
  393. i: Integer;
  394. begin
  395. s:='';
  396. for i:=Low(Args) to High(Args) do
  397. begin
  398. case Args[i].VType of
  399. vtChar: s += Args[i].VChar+LineEnding;
  400. vtString: s += Args[i].VString^+LineEnding;
  401. vtPChar: s += Args[i].VPChar+LineEnding;
  402. vtWideChar: s += TCSSString(Args[i].VWideChar)+LineEnding;
  403. vtPWideChar: s += TCSSString(Args[i].VPWideChar)+LineEnding;
  404. vtAnsiString: s += AnsiString(Args[i].VAnsiString)+LineEnding; // FPC uses encoding CP_UTF8 for TVarRec.VAnsiString
  405. vtWidestring: s += TCSSString(WideString(Args[i].VWideString))+LineEnding;
  406. vtUnicodeString:s += TCSSString(UnicodeString(Args[i].VUnicodeString))+LineEnding;
  407. end;
  408. end;
  409. Result:=s;
  410. end;
  411. { TDemoDiv }
  412. class function TDemoDiv.CSSTypeName: TCSSString;
  413. begin
  414. Result:=DemoElementTypeNames[detDiv];
  415. end;
  416. class function TDemoDiv.GetClassCSSTypeID: TCSSNumericalID;
  417. begin
  418. Result:=FDemoDivTypeID;
  419. end;
  420. class procedure TDemoDiv.SetClassCSSTypeID(aID: TCSSNumericalID);
  421. begin
  422. FDemoDivTypeID:=aID;
  423. end;
  424. class function TDemoDiv.GetCSSTypeStyle: TCSSString;
  425. begin
  426. Result:='div{ display: block }';
  427. end;
  428. { TDemoSpan }
  429. class function TDemoSpan.CSSTypeName: TCSSString;
  430. begin
  431. Result:=DemoElementTypeNames[detSpan];
  432. end;
  433. class function TDemoSpan.GetClassCSSTypeID: TCSSNumericalID;
  434. begin
  435. Result:=FDemoSpanTypeID;
  436. end;
  437. class procedure TDemoSpan.SetClassCSSTypeID(aID: TCSSNumericalID);
  438. begin
  439. FDemoSpanTypeID:=aID;
  440. end;
  441. class function TDemoSpan.GetCSSTypeStyle: TCSSString;
  442. begin
  443. Result:='span{display: inline-block }';
  444. end;
  445. { TDemoButton }
  446. procedure TDemoButton.SetCaption(const AValue: TCSSString);
  447. begin
  448. if FCaption=AValue then Exit;
  449. FCaption:=AValue;
  450. end;
  451. class function TDemoButton.CSSTypeName: TCSSString;
  452. begin
  453. Result:=DemoElementTypeNames[detButton];
  454. end;
  455. class function TDemoButton.GetClassCSSTypeID: TCSSNumericalID;
  456. begin
  457. Result:=FDemoButtonTypeID;
  458. end;
  459. class procedure TDemoButton.SetClassCSSTypeID(aID: TCSSNumericalID);
  460. begin
  461. FDemoButtonTypeID:=aID;
  462. end;
  463. class function TDemoButton.GetCSSTypeStyle: TCSSString;
  464. begin
  465. Result:='button{display: inline-block }';
  466. end;
  467. function TDemoButton.HasCSSExplicitAttribute(const AttrID: TCSSNumericalID
  468. ): boolean;
  469. begin
  470. //writeln('TDemoButton.HasCSSExplicitAttribute ',AttrID,' CSSCaptionID=',CSSCaptionID);
  471. if AttrID=CSSCaptionID then
  472. Result:=ExplicitCaption<>''
  473. else
  474. Result:=inherited HasCSSExplicitAttribute(AttrID);
  475. end;
  476. function TDemoButton.GetCSSExplicitAttribute(const AttrID: TCSSNumericalID
  477. ): TCSSString;
  478. begin
  479. if AttrID=CSSCaptionID then
  480. Result:=ExplicitCaption
  481. else
  482. Result:=inherited GetCSSExplicitAttribute(AttrID);
  483. end;
  484. { TDemoDocument }
  485. procedure TDemoDocument.SetStyle(const AValue: TCSSString);
  486. begin
  487. if FStyle=AValue then Exit;
  488. FStyle:=AValue;
  489. end;
  490. constructor TDemoDocument.Create(AOwner: TComponent);
  491. begin
  492. inherited Create(AOwner);
  493. // create the css resolver
  494. FCSSResolver:=TCSSResolver.Create(nil);
  495. FCSSResolver.CSSRegistry:=TDemoNode.CSSRegistry;
  496. FCSSResolver.OnLog:=@OnResolverLog;
  497. end;
  498. destructor TDemoDocument.Destroy;
  499. begin
  500. FreeAndNil(Root);
  501. FreeAndNil(FCSSResolver);
  502. inherited Destroy;
  503. end;
  504. procedure TDemoDocument.ApplyStyle;
  505. procedure Traverse(Node: TDemoNode);
  506. var
  507. i: Integer;
  508. begin
  509. Node.ApplyCSS(CSSResolver);
  510. for i:=0 to Node.NodeCount-1 do
  511. Traverse(Node[i]);
  512. end;
  513. begin
  514. ApplyTypeStyles;
  515. CSSResolver.AddStyleSheet(cssoAuthor,'test.css',Style);
  516. CSSResolver.Init;
  517. Traverse(Root);
  518. end;
  519. procedure TDemoDocument.OnResolverLog(Sender: TObject; Entry: TCSSResolverLogEntry);
  520. begin
  521. if Sender=nil then ;
  522. if Entry=nil then ;
  523. end;
  524. procedure TDemoDocument.ApplyTypeStyles;
  525. var
  526. FoundStyles: array of TDemoNodeClass;
  527. procedure AddTypeStyle(NodeClass: TDemoNodeClass);
  528. var
  529. i: Integer;
  530. Src, ParentSrc: TCSSString;
  531. ParentNodeClass: TDemoNodeClass;
  532. begin
  533. for i:=0 to length(FoundStyles)-1 do
  534. if FoundStyles[i]=NodeClass then exit;
  535. Insert(NodeClass,FoundStyles,length(FoundStyles));
  536. Src:=NodeClass.GetCSSTypeStyle;
  537. //writeln('AddTypeStyle ',NodeClass.ClassName,' Src="',Src,'"');
  538. if Src='' then exit;
  539. if NodeClass.ClassType<>TDemoNode then
  540. begin
  541. ParentNodeClass:=TDemoNodeClass(NodeClass.ClassParent);
  542. AddTypeStyle(ParentNodeClass);
  543. ParentSrc:=ParentNodeClass.GetCSSTypeStyle;
  544. if Src=ParentSrc then exit;
  545. end;
  546. //writeln('AddTypeStyle ',NodeClass.ClassName,' [',Src,']');
  547. FCSSResolver.AddStyleSheet(cssoUserAgent,NodeClass.ClassName,Src);
  548. end;
  549. procedure CollectTypeStyles(Node: TDemoNode);
  550. var
  551. NodeClass: TDemoNodeClass;
  552. i: Integer;
  553. begin
  554. NodeClass:=TDemoNodeClass(Node.ClassType);
  555. AddTypeStyle(NodeClass);
  556. for i:=0 to Node.NodeCount-1 do
  557. CollectTypeStyles(Node[i]);
  558. end;
  559. begin
  560. FoundStyles:=[];
  561. CollectTypeStyles(Root);
  562. end;
  563. { TDemoCSSRegistry }
  564. function TDemoCSSRegistry.OnCheck_Border(Resolver: TCSSBaseResolver): boolean;
  565. var
  566. HasWidth, HasColor: Boolean;
  567. begin
  568. HasWidth:=false;
  569. HasColor:=false;
  570. repeat
  571. case Resolver.CurComp.Kind of
  572. rvkFloat:
  573. if not HasWidth then
  574. HasWidth:=Resolver.CurComp.FloatUnit in ([cuNONE,cuPERCENT]+cuAllLengths);
  575. rvkKeyword:
  576. if not HasColor then
  577. HasColor:=(Resolver.CurComp.KeywordID>=kwFirstColor) and (Resolver.CurComp.KeywordID<=kwLastColor);
  578. end;
  579. until not Resolver.ReadNext;
  580. Result:=HasWidth or HasColor;
  581. end;
  582. function TDemoCSSRegistry.OnCheck_BorderColor(Resolver: TCSSBaseResolver): boolean;
  583. begin
  584. Result:=Resolver.CheckAttribute_Color([]);
  585. end;
  586. function TDemoCSSRegistry.OnCheck_BorderWidth(Resolver: TCSSBaseResolver): boolean;
  587. begin
  588. Result:=Resolver.CheckAttribute_Dimension(Chk_BorderWidth);
  589. end;
  590. procedure TDemoCSSRegistry.OnSplit_Border(Resolver: TCSSBaseResolver;
  591. var AttrIDs: TCSSNumericalIDArray; var Values: TCSSStringArray);
  592. var
  593. aWidth, aColor: TCSSString;
  594. begin
  595. aWidth:='';
  596. aColor:='';
  597. repeat
  598. case Resolver.CurComp.Kind of
  599. rvkFloat:
  600. if aWidth='' then begin
  601. if Resolver.CurComp.FloatUnit in ([cuNONE,cuPERCENT]+cuAllLengths) then
  602. aWidth:=Resolver.CurComp.FloatAsString;
  603. end;
  604. rvkKeyword:
  605. if aColor='' then
  606. begin
  607. if (Resolver.CurComp.KeywordID>=kwFirstColor) and (Resolver.CurComp.KeywordID<=kwLastColor) then
  608. aColor:=Keywords[Resolver.CurComp.KeywordID];
  609. end;
  610. end;
  611. until not Resolver.ReadNext;
  612. SetLength(AttrIDs,2);
  613. SetLength(Values,2);
  614. AttrIDs[0]:=DemoAttrs[naBorderWidth].Index;
  615. Values[0]:=aWidth;
  616. AttrIDs[1]:=DemoAttrs[naBorderColor].Index;
  617. Values[1]:=aColor;
  618. end;
  619. function TDemoCSSRegistry.OnCheck_Direction(Resolver: TCSSBaseResolver): boolean;
  620. begin
  621. Result:=Resolver.CheckAttribute_Keyword(Chk_DirectionAllowedKeywordIDs);
  622. end;
  623. function TDemoCSSRegistry.OnCheck_Display(Resolver: TCSSBaseResolver): boolean;
  624. begin
  625. Result:=Resolver.CheckAttribute_Keyword(Chk_DisplayAllowedKeywordIDs);
  626. end;
  627. function TDemoCSSRegistry.OnCheck_LeftTop(Resolver: TCSSBaseResolver): boolean;
  628. begin
  629. Result:=Resolver.CheckAttribute_Dimension(Chk_LeftTop);
  630. end;
  631. function TDemoCSSRegistry.OnCheck_WidthHeight(Resolver: TCSSBaseResolver): boolean;
  632. begin
  633. Result:=Resolver.CheckAttribute_Dimension(Chk_WidthHeight);
  634. end;
  635. procedure TDemoCSSRegistry.OnCompute_Direction(Resolver: TCSSResolver;
  636. Node: TDemoNode; Value: TCSSAttributeValue);
  637. var
  638. Invalid: boolean;
  639. begin
  640. if Resolver.ReadAttribute_Keyword(Invalid,Chk_DirectionAllowedKeywordIDs) then
  641. begin
  642. Value.Value:=Keywords[Resolver.CurComp.KeywordID];
  643. Value.State:=cavsComputed;
  644. end
  645. else begin
  646. Value.Value:='invalid';
  647. Value.State:=cavsInvalid;
  648. end;
  649. if Node=nil then ;
  650. end;
  651. procedure TDemoCSSRegistry.OnCompute_LeftTop(Resolver: TCSSResolver;
  652. Node: TDemoNode; Value: TCSSAttributeValue);
  653. var
  654. Invalid: boolean;
  655. begin
  656. if Resolver.ReadAttribute_Dimension(Invalid,Chk_LeftTop) then
  657. begin
  658. case Resolver.CurComp.Kind of
  659. rvkFloat:
  660. Value.Value:=Resolver.CurComp.FloatAsString;
  661. rvkKeyword:
  662. Value.Value:=Keywords[Resolver.CurComp.KeywordID];
  663. end;
  664. Value.State:=cavsComputed;
  665. end
  666. else begin
  667. Value.Value:='invalid';
  668. Value.State:=cavsInvalid;
  669. end;
  670. if Node=nil then ;
  671. end;
  672. procedure TDemoCSSRegistry.OnCompute_WidthHeight(Resolver: TCSSResolver;
  673. Node: TDemoNode; Value: TCSSAttributeValue);
  674. var
  675. Invalid: boolean;
  676. begin
  677. if Resolver.ReadAttribute_Dimension(Invalid,Chk_WidthHeight) then
  678. begin
  679. Value.Value:=Resolver.CurComp.FloatAsString;
  680. Value.State:=cavsComputed;
  681. end
  682. else begin
  683. Value.Value:='invalid';
  684. Value.State:=cavsInvalid;
  685. end;
  686. if Node=nil then ;
  687. end;
  688. constructor TDemoCSSRegistry.Create;
  689. procedure SetDemoElementTypeID(aClass: TDemoNodeClass);
  690. var
  691. Desc: TCSSTypeDesc;
  692. begin
  693. Desc:=FindType(aClass.CSSTypeName);
  694. if Desc=nil then
  695. raise Exception.Create('20240625190912');
  696. aClass.SetClassCSSTypeID(Desc.Index);
  697. end;
  698. procedure SetCompProps(ShorthandID: TDemoNodeAttribute; Longhands: array of TDemoNodeAttribute);
  699. var
  700. i: Integer;
  701. begin
  702. SetLength(DemoAttrs[ShorthandID].CompProps,length(Longhands));
  703. for i:=0 to length(Longhands)-1 do
  704. DemoAttrs[ShorthandID].CompProps[i]:=DemoAttrs[Longhands[i]];
  705. end;
  706. var
  707. Attr: TDemoNodeAttribute;
  708. PseudoClass: TDemoPseudoClass;
  709. aType: TDemoElementType;
  710. begin
  711. inherited Create;
  712. Init;
  713. // register demo attributes
  714. for Attr in TDemoNodeAttribute do
  715. AddDemoAttr(Attr);
  716. DemoAttrIDBase:=DemoAttrs[low(TDemoNodeAttribute)].Index;
  717. if FindAttribute(DemoAttributeNames[naBackground]).Index<>DemoAttrIDBase+ord(naBackground) then
  718. raise Exception.Create('20240617200337');
  719. // register demo pseudo classes
  720. for PseudoClass in TDemoPseudoClass do
  721. AddDemoPseudoClass(PseudoClass);
  722. DemoPseudoClassIDBase:=DemoPseudoClasses[low(TDemoPseudoClass)].Index;
  723. if FindPseudoClass(DemoPseudoClassNames[pcHover]).Index<>DemoPseudoClassIDBase+ord(pcHover) then
  724. raise Exception.Create('20231008232201');
  725. // register demo element types
  726. for aType in TDemoElementType do
  727. AddDemoType(aType);
  728. DemoElementTypeIDBase:=DemoTypes[low(TDemoElementType)].Index;
  729. if FindType(DemoElementTypeNames[detButton]).Index<>DemoElementTypeIDBase+ord(detButton) then
  730. raise Exception.Create('20240625181725');
  731. SetDemoElementTypeID(TDemoNode);
  732. SetDemoElementTypeID(TDemoDiv);
  733. SetDemoElementTypeID(TDemoSpan);
  734. SetDemoElementTypeID(TDemoButton);
  735. kwRed:=AddKeyword('red');
  736. kwFirstColor:=kwRed;
  737. kwGreen:=AddKeyword('green');
  738. kwBlue:=AddKeyword('blue');
  739. kwWhite:=AddKeyword('white');
  740. kwBlack:=AddKeyword('black');
  741. kwLastColor:=kwBlack;
  742. kwBlock:=AddKeyword('block');
  743. kwInline_Block:=AddKeyword('inline-block');
  744. kwLTR:=AddKeyword('ltr');
  745. kwRTL:=AddKeyword('rtl');
  746. // check parameters - - - - - - - - - - - - - - - - - - - - - - - -
  747. // border-color
  748. DemoAttrs[naBorderColor].OnCheck:=@OnCheck_BorderColor;
  749. // border-width
  750. DemoAttrs[naBorderWidth].OnCheck:=@OnCheck_BorderWidth;
  751. Chk_BorderWidth.AllowedUnits:=[cuNONE,cuPERCENT]+cuAllLengths;
  752. Chk_BorderWidth.AllowFrac:=true;
  753. // border shorthand
  754. SetCompProps(naBorder,[naBorderColor,naBorderWidth]);
  755. DemoAttrs[naBorder].OnCheck:=@OnCheck_Border;
  756. DemoAttrs[naBorder].OnSplitShorthand:=@OnSplit_Border;
  757. // direction
  758. DemoAttrs[naDirection].OnCheck:=@OnCheck_Direction;
  759. Chk_DirectionAllowedKeywordIDs:=[kwLTR,kwRTL];
  760. DemoAttrs[naDirection].OnCompute:=@OnCompute_Direction;
  761. // display
  762. DemoAttrs[naDisplay].OnCheck:=@OnCheck_Display;
  763. Chk_DisplayAllowedKeywordIDs:=[kwBlock,kwInline_Block];
  764. // left, top
  765. DemoAttrs[naLeft].OnCheck:=@OnCheck_LeftTop;
  766. DemoAttrs[naLeft].OnCompute:=@OnCompute_LeftTop;
  767. DemoAttrs[naTop].OnCheck:=@OnCheck_LeftTop;
  768. DemoAttrs[naTop].OnCompute:=@OnCompute_LeftTop;
  769. Chk_LeftTop.AllowedUnits:=[cuNONE,cuPERCENT]+cuAllLengths;
  770. Chk_LeftTop.AllowNegative:=true;
  771. Chk_LeftTop.AllowFrac:=true;
  772. // width, height
  773. DemoAttrs[naWidth].OnCheck:=@OnCheck_WidthHeight;
  774. DemoAttrs[naWidth].OnCompute:=@OnCompute_WidthHeight;
  775. DemoAttrs[naHeight].OnCheck:=@OnCheck_WidthHeight;
  776. DemoAttrs[naHeight].OnCompute:=@OnCompute_WidthHeight;
  777. Chk_WidthHeight.AllowedUnits:=[cuNONE,cuPERCENT]+cuAllLengths;
  778. Chk_WidthHeight.AllowFrac:=true;
  779. end;
  780. function TDemoCSSRegistry.AddDemoAttr(Attr: TDemoNodeAttribute
  781. ): TDemoCSSAttributeDesc;
  782. begin
  783. Result:=TDemoCSSAttributeDesc(AddAttribute(DemoAttributeNames[Attr],
  784. DemoAttributeInitialValues[Attr],
  785. Attr in DemoAttributesInherited,
  786. not (Attr in DemoAttributesNotAll),
  787. TDemoCSSAttributeDesc));
  788. Result.DemoID:=Attr;
  789. DemoAttrs[Attr]:=Result;
  790. end;
  791. function TDemoCSSRegistry.AddDemoPseudoClass(PC: TDemoPseudoClass
  792. ): TDemoCSSPseudoClassDesc;
  793. begin
  794. Result:=TDemoCSSPseudoClassDesc(AddPseudoClass(DemoPseudoClassNames[PC],
  795. TDemoCSSPseudoClassDesc));
  796. Result.DemoID:=PC;
  797. DemoPseudoClasses[PC]:=Result;
  798. end;
  799. function TDemoCSSRegistry.AddDemoType(aType: TDemoElementType
  800. ): TDemoCSSTypeDesc;
  801. begin
  802. Result:=TDemoCSSTypeDesc(AddType(DemoElementTypeNames[aType],
  803. TDemoCSSTypeDesc));
  804. Result.DemoID:=aType;
  805. DemoTypes[aType]:=Result;
  806. end;
  807. { TDemoNode }
  808. function TDemoNode.GetAttribute(DemoAttr: TDemoNodeAttribute): TCSSString;
  809. var
  810. AttrDesc: TDemoCSSAttributeDesc;
  811. i: Integer;
  812. begin
  813. AttrDesc:=CSSRegistry.DemoAttrs[DemoAttr];
  814. i:=Values.IndexOf(AttrDesc.Index);
  815. if i>=0 then
  816. Result:=Values.Values[i].Value
  817. else
  818. Result:='';
  819. end;
  820. function TDemoNode.GetNodeCount: integer;
  821. begin
  822. Result:=FNodes.Count;
  823. end;
  824. function TDemoNode.GetNodes(Index: integer): TDemoNode;
  825. begin
  826. Result:=TDemoNode(FNodes[Index]);
  827. end;
  828. function TDemoNode.GetPseudoClasses(PseudoClass: TDemoPseudoClass): boolean;
  829. begin
  830. Result:=FPseudoClasses[PseudoClass];
  831. end;
  832. procedure TDemoNode.SetParent(const AValue: TDemoNode);
  833. begin
  834. if FParent=AValue then Exit;
  835. if AValue=Self then
  836. raise Exception.Create('cycle');
  837. if FParent<>nil then
  838. begin
  839. FParent.FNodes.Remove(Self);
  840. end;
  841. FParent:=AValue;
  842. if FParent<>nil then
  843. begin
  844. FParent.FNodes.Add(Self);
  845. FreeNotification(FParent);
  846. end;
  847. end;
  848. procedure TDemoNode.SetInlineStyleElements(const AValue: TCSSRuleElement);
  849. begin
  850. if FInlineStyleElements=AValue then Exit;
  851. FreeAndNil(FInlineStyleElements);
  852. FInlineStyleElements:=AValue;
  853. end;
  854. procedure TDemoNode.SetInlineStyle(const AValue: TCSSString);
  855. begin
  856. if FInlineStyle=AValue then Exit;
  857. FInlineStyle:=AValue;
  858. FreeAndNil(FInlineStyleElements);
  859. end;
  860. procedure TDemoNode.SetPseudoClasses(PseudoClass: TDemoPseudoClass;
  861. const AValue: boolean);
  862. begin
  863. FPseudoClasses[PseudoClass]:=AValue;
  864. end;
  865. procedure TDemoNode.Notification(AComponent: TComponent; Operation: TOperation);
  866. begin
  867. inherited Notification(AComponent, Operation);
  868. if AComponent=Self then exit;
  869. if Operation=opRemove then
  870. begin
  871. if FNodes<>nil then
  872. FNodes.Remove(AComponent);
  873. end;
  874. end;
  875. constructor TDemoNode.Create(AOwner: TComponent);
  876. begin
  877. inherited Create(AOwner);
  878. FNodes:=TFPObjectList.Create(false);
  879. FCSSClasses:=TStringList.Create;
  880. FCSSClasses.Delimiter:=' ';
  881. end;
  882. destructor TDemoNode.Destroy;
  883. begin
  884. Clear;
  885. FreeAndNil(FNodes);
  886. FreeAndNil(FCSSClasses);
  887. inherited Destroy;
  888. end;
  889. procedure TDemoNode.Clear;
  890. var
  891. i: Integer;
  892. begin
  893. Rules:=nil;
  894. FreeAndNil(Values);
  895. FCSSClasses.Clear;
  896. FreeAndNil(FInlineStyleElements);
  897. for i:=NodeCount-1 downto 0 do
  898. Nodes[i].Free;
  899. if FNodes.Count>0 then
  900. raise Exception.Create('20240710174459');
  901. end;
  902. procedure TDemoNode.ApplyCSS(Resolver: TCSSResolver);
  903. var
  904. AttrDesc: TDemoCSSAttributeDesc;
  905. i: Integer;
  906. AttrID: TCSSNumericalID;
  907. CurValue: TCSSAttributeValue;
  908. Desc: TCSSAttributeDesc;
  909. begin
  910. if (InlineStyleElement=nil) and (InlineStyle<>'') then
  911. InlineStyleElement:=Resolver.ParseInlineStyle(InlineStyle) as TCSSRuleElement;
  912. Resolver.Compute(Self,InlineStyleElement,Rules,Values);
  913. {$IFDEF VerboseCSSResolver}
  914. writeln('TDemoNode.ApplyCSS ',Name,' length(Values)=',length(Values.Values),' All="',CSSRegistry.Keywords[Values.AllValue],'"');
  915. for i:=0 to length(Values.Values)-1 do begin
  916. AttrID:=Values.Values[i].AttrID;
  917. writeln('TDemoNode.ApplyCSS ',Name,' resolved ',CSSRegistry.Attributes[AttrID].Name,'/',AttrID,':="',Values.Values[i].Value,'"');
  918. end;
  919. {$ENDIF}
  920. // compute values
  921. for i:=0 to length(Values.Values)-1 do
  922. begin
  923. CurValue:=Values.Values[i];
  924. case CurValue.State of
  925. cavsSource, cavsBaseKeywords:
  926. begin
  927. AttrID:=CurValue.AttrID;
  928. Desc:=Resolver.GetAttributeDesc(AttrID);
  929. if Desc=nil then
  930. raise Exception.Create('20240823100115 AttrID='+IntToStr(AttrID));
  931. if Desc is TDemoCSSAttributeDesc then
  932. begin
  933. AttrDesc:=TDemoCSSAttributeDesc(Desc);
  934. if AttrDesc.OnCompute<>nil then
  935. begin
  936. Resolver.CurComp.EndP:=PChar(CurValue.Value);
  937. Resolver.ReadNext;
  938. AttrDesc.OnCompute(Resolver,Self,CurValue);
  939. {$IFDEF VerboseCSSResolver}
  940. writeln('TDemoNode.ApplyCSS ',Name,' computed ',CSSRegistry.Attributes[AttrID].Name,'/',AttrID,':="',CurValue.Value,'"');
  941. {$ENDIF}
  942. end else
  943. CurValue.State:=cavsComputed;
  944. end;
  945. end;
  946. cavsComputed: ;
  947. cavsInvalid: ;
  948. end;
  949. end;
  950. end;
  951. function TDemoNode.GetCSSID: TCSSString;
  952. begin
  953. Result:=Name;
  954. end;
  955. class function TDemoNode.CSSTypeName: TCSSString;
  956. begin
  957. Result:=DemoElementTypeNames[detNode];
  958. end;
  959. function TDemoNode.HasCSSClass(const aClassName: TCSSString): boolean;
  960. var
  961. i: Integer;
  962. begin
  963. for i:=0 to CSSClasses.Count-1 do
  964. if aClassName=CSSClasses[i] then
  965. exit(true);
  966. Result:=false;
  967. end;
  968. function TDemoNode.GetCSSParent: ICSSNode;
  969. begin
  970. Result:=Parent;
  971. end;
  972. function TDemoNode.GetCSSIndex: integer;
  973. begin
  974. if Parent=nil then
  975. Result:=-1
  976. else
  977. Result:=Parent.FNodes.IndexOf(Self);
  978. end;
  979. function TDemoNode.GetCSSNextSibling: ICSSNode;
  980. var
  981. i: Integer;
  982. begin
  983. i:=GetCSSIndex;
  984. if (i<0) or (i+1>=Parent.NodeCount) then
  985. Result:=nil
  986. else
  987. Result:=Parent.Nodes[i+1];
  988. end;
  989. function TDemoNode.GetCSSPreviousSibling: ICSSNode;
  990. var
  991. i: Integer;
  992. begin
  993. i:=GetCSSIndex;
  994. if i<1 then
  995. Result:=nil
  996. else
  997. Result:=Parent.Nodes[i-1];
  998. end;
  999. function TDemoNode.GetCSSChildCount: integer;
  1000. begin
  1001. Result:=NodeCount;
  1002. end;
  1003. function TDemoNode.GetCSSChild(const anIndex: integer): ICSSNode;
  1004. begin
  1005. Result:=Nodes[anIndex];
  1006. end;
  1007. function TDemoNode.GetCSSNextOfType: ICSSNode;
  1008. var
  1009. i, Cnt: Integer;
  1010. MyID: TCSSNumericalID;
  1011. aNode: TDemoNode;
  1012. begin
  1013. Result:=nil;
  1014. i:=GetCSSIndex;
  1015. if i<0 then exit;
  1016. inc(i);
  1017. MyID:=GetClassCSSTypeID;
  1018. Cnt:=Parent.NodeCount;
  1019. while i<Cnt do
  1020. begin
  1021. aNode:=Parent.Nodes[i];
  1022. if aNode.GetClassCSSTypeID=MyID then
  1023. exit(aNode);
  1024. inc(i);
  1025. end;
  1026. end;
  1027. function TDemoNode.GetCSSPreviousOfType: ICSSNode;
  1028. var
  1029. i: Integer;
  1030. MyID: TCSSNumericalID;
  1031. aNode: TDemoNode;
  1032. begin
  1033. Result:=nil;
  1034. i:=GetCSSIndex;
  1035. if i<0 then exit;
  1036. dec(i);
  1037. MyID:=GetClassCSSTypeID;
  1038. while i>=0 do
  1039. begin
  1040. aNode:=Parent.Nodes[i];
  1041. if aNode.GetClassCSSTypeID=MyID then
  1042. exit(aNode);
  1043. dec(i);
  1044. end;
  1045. end;
  1046. function TDemoNode.GetCSSAttributeClass: TCSSString;
  1047. begin
  1048. FCSSClasses.Delimiter:=' ';
  1049. Result:=FCSSClasses.DelimitedText;
  1050. end;
  1051. function TDemoNode.GetCSSCustomAttribute(const AttrID: TCSSNumericalID): TCSSString;
  1052. var
  1053. i: Integer;
  1054. El: TDemoNode;
  1055. begin
  1056. Result:='';
  1057. El:=Self;
  1058. repeat
  1059. if El.Values<>nil then
  1060. begin
  1061. i:=El.Values.IndexOf(AttrID);
  1062. if i>=0 then
  1063. begin
  1064. Result:=El.Values.Values[i].Value;
  1065. if Result<>'' then exit;
  1066. end;
  1067. end;
  1068. El:=El.Parent;
  1069. until El=nil;
  1070. end;
  1071. function TDemoNode.HasCSSExplicitAttribute(const AttrID: TCSSNumericalID): boolean;
  1072. var
  1073. b: TCSSNumericalID;
  1074. Attr: TDemoNodeAttribute;
  1075. begin
  1076. b:=CSSRegistry.DemoAttrIDBase;
  1077. if (AttrID<b) or (AttrID>b+ord(High(TDemoNodeAttribute))) then
  1078. exit(false);
  1079. Attr:=TDemoNodeAttribute(AttrID-b);
  1080. Result:=ExplicitAttributes[Attr]<>'';
  1081. end;
  1082. function TDemoNode.GetCSSExplicitAttribute(const AttrID: TCSSNumericalID): TCSSString;
  1083. var
  1084. Attr: TDemoNodeAttribute;
  1085. b: TCSSNumericalID;
  1086. begin
  1087. b:=CSSRegistry.DemoAttrIDBase;
  1088. if (AttrID<b) or (AttrID>b+ord(High(TDemoNodeAttribute))) then
  1089. exit('');
  1090. Attr:=TDemoNodeAttribute(AttrID-b);
  1091. Result:=ExplicitAttributes[Attr];
  1092. end;
  1093. function TDemoNode.HasCSSPseudoClass(const AttrID: TCSSNumericalID): boolean;
  1094. var
  1095. b: TCSSNumericalID;
  1096. begin
  1097. b:=CSSRegistry.DemoPseudoClassIDBase;
  1098. if (AttrID>=b) and (AttrID<=b+ord(High(TDemoPseudoClass))) then
  1099. Result:=HasPseudoClass[TDemoPseudoClass(AttrID-b)]
  1100. else
  1101. Result:=false;
  1102. end;
  1103. function TDemoNode.GetCSSEmpty: boolean;
  1104. begin
  1105. Result:=NodeCount=0;
  1106. end;
  1107. function TDemoNode.GetCSSDepth: integer;
  1108. var
  1109. Node: TDemoNode;
  1110. begin
  1111. Result:=0;
  1112. Node:=Parent;
  1113. while Node<>nil do
  1114. begin
  1115. inc(Result);
  1116. Node:=Node.Parent;
  1117. end;
  1118. end;
  1119. function TDemoNode.GetCSSTypeName: TCSSString;
  1120. begin
  1121. Result:=CSSTypeName;
  1122. end;
  1123. class function TDemoNode.GetClassCSSTypeID: TCSSNumericalID;
  1124. begin
  1125. Result:=FDemoNodeTypeID;
  1126. end;
  1127. class procedure TDemoNode.SetClassCSSTypeID(aID: TCSSNumericalID);
  1128. begin
  1129. FDemoNodeTypeID:=aID;
  1130. end;
  1131. function TDemoNode.GetCSSTypeID: TCSSNumericalID;
  1132. begin
  1133. Result:=GetClassCSSTypeID;
  1134. end;
  1135. class function TDemoNode.GetCSSTypeStyle: TCSSString;
  1136. begin
  1137. Result:='';
  1138. end;
  1139. { TCustomTestNewCSSResolver }
  1140. procedure TCustomTestNewCSSResolver.SetUp;
  1141. var
  1142. AttrDesc: TCSSAttributeDesc;
  1143. begin
  1144. inherited SetUp;
  1145. TDemoNode.CSSRegistry:=TDemoCSSRegistry.Create();
  1146. // register button attribute 'caption'
  1147. AttrDesc:=TDemoNode.CSSRegistry.AddAttribute('caption');
  1148. TDemoButton.CSSCaptionID:=AttrDesc.Index;
  1149. FDoc:=TDemoDocument.Create(nil);
  1150. end;
  1151. procedure TCustomTestNewCSSResolver.TearDown;
  1152. begin
  1153. FreeAndNil(FDoc);
  1154. FreeAndNil(TDemoNode.CSSRegistry);
  1155. inherited TearDown;
  1156. end;
  1157. procedure TCustomTestNewCSSResolver.ApplyStyle;
  1158. begin
  1159. Doc.ApplyStyle;
  1160. CheckWarnings;
  1161. end;
  1162. procedure TCustomTestNewCSSResolver.CheckWarnings;
  1163. var
  1164. aResolver: TCSSResolver;
  1165. i: Integer;
  1166. Entry: TCSSResolverLogEntry;
  1167. s: String;
  1168. begin
  1169. aResolver:=FDoc.CSSResolver;
  1170. if aResolver.LogCount=0 then exit;
  1171. writeln('TCustomTestNewCSSResolver.CheckWarnings LogCount=',aResolver.LogCount);
  1172. for i:=0 to aResolver.LogCount-1 do
  1173. begin
  1174. Entry:=aResolver.LogEntries[i];
  1175. s:='';
  1176. if Entry.PosEl<>nil then
  1177. s:=' at '+Entry.PosEl.SourceFileName+'('+IntToStr(Entry.PosEl.SourceRow)+','+IntToStr(Entry.PosEl.SourceRow)+')';
  1178. writeln(' ',Entry.MsgType,': ',Entry.ID,' ',Entry.Msg,s);
  1179. end;
  1180. end;
  1181. { TTestNewCSSResolver }
  1182. procedure TTestNewCSSResolver.Test_ParseAttr_Keyword;
  1183. begin
  1184. Doc.Root:=TDemoNode.Create(nil);
  1185. Doc.Style:='* { direction: ltr; }';
  1186. ApplyStyle;
  1187. AssertEquals('Root.direction','ltr',Doc.Root.Direction);
  1188. end;
  1189. procedure TTestNewCSSResolver.Test_ParseAttr_Keyword_SkipInvalid;
  1190. begin
  1191. Doc.Root:=TDemoNode.Create(nil);
  1192. Doc.Style:='* { direction: something ltr; }';
  1193. ApplyStyle;
  1194. AssertEquals('Root.direction','ltr',Doc.Root.Direction);
  1195. end;
  1196. procedure TTestNewCSSResolver.Test_ParseAttr_Float;
  1197. var
  1198. Div1: TDemoDiv;
  1199. begin
  1200. Doc.Root:=TDemoNode.Create(nil);
  1201. Doc.Style:=
  1202. ':root {'
  1203. +' left: 10px;'
  1204. +' top: .1px;'
  1205. +' width: 3e2em;'
  1206. +' height: 3e-2px;'
  1207. +'}'
  1208. +'div {'
  1209. +' left: -4mm;'
  1210. +' top: -.5pc;'
  1211. +' width: .6cm;'
  1212. +' height: 6E+1rem;'
  1213. +'}';
  1214. Div1:=TDemoDiv.Create(nil);
  1215. Div1.Name:='Div1';
  1216. Div1.Parent:=Doc.Root;
  1217. ApplyStyle;
  1218. AssertEquals('Root.Left','10px',Doc.Root.Left);
  1219. AssertEquals('Root.Top','0.1px',Doc.Root.Top);
  1220. AssertEquals('Root.Width','300em',Doc.Root.Width);
  1221. AssertEquals('Root.Height','0.03px',Doc.Root.Height);
  1222. AssertEquals('Div1.Left','-4mm',Div1.Left);
  1223. AssertEquals('Div1.Top','-0.5pc',Div1.Top);
  1224. AssertEquals('Div1.Width','0.6cm',Div1.Width);
  1225. AssertEquals('Div1.Height','60rem',Div1.Height);
  1226. end;
  1227. procedure TTestNewCSSResolver.Test_ParseAttr_Float_SkipInvalid;
  1228. begin
  1229. exit;
  1230. Doc.Root:=TDemoNode.Create(nil);
  1231. Doc.Style:=
  1232. ':root {'
  1233. +' left: something 10px;'
  1234. +' top: 1 px;' // no space between number
  1235. +' width: 0 px;' // the px is ignored because of the space, 0 without unit is allowed
  1236. +' height: -4cm;' // no negative
  1237. +'}';
  1238. ApplyStyle;
  1239. AssertEquals('Root.Left','10px',Doc.Root.Left);
  1240. AssertEquals('Root.Top','invalid',Doc.Root.Top);
  1241. AssertEquals('Root.Width','0',Doc.Root.Width);
  1242. AssertEquals('Root.Height','invalid',Doc.Root.Height);
  1243. end;
  1244. procedure TTestNewCSSResolver.Test_Selector_Universal;
  1245. begin
  1246. Doc.Root:=TDemoNode.Create(nil);
  1247. Doc.Style:='* { left: 10px; }';
  1248. ApplyStyle;
  1249. AssertEquals('Root.left','10px',Doc.Root.Left);
  1250. end;
  1251. procedure TTestNewCSSResolver.Test_Selector_Type;
  1252. var
  1253. Button: TDemoButton;
  1254. begin
  1255. Doc.Root:=TDemoNode.Create(nil);
  1256. Button:=TDemoButton.Create(nil);
  1257. Button.Parent:=Doc.Root;
  1258. Doc.Style:='button { left: 11px; }';
  1259. ApplyStyle;
  1260. AssertEquals('Root.left','',Doc.Root.Left);
  1261. AssertEquals('Button.left','11px',Button.Left);
  1262. end;
  1263. procedure TTestNewCSSResolver.Test_Selector_Type_Spaces;
  1264. var
  1265. Button1, Button2: TDemoButton;
  1266. begin
  1267. Doc.Root:=TDemoNode.Create(nil);
  1268. Button1:=TDemoButton.Create(nil);
  1269. Button1.Parent:=Doc.Root;
  1270. Button2:=TDemoButton.Create(nil);
  1271. Button2.Parent:=Doc.Root;
  1272. Doc.Style:='div, button ,span { left: 11px; }';
  1273. ApplyStyle;
  1274. AssertEquals('Root.left','',Doc.Root.Left);
  1275. AssertEquals('Button1.left','11px',Button1.Left);
  1276. AssertEquals('Button2.left','11px',Button2.Left);
  1277. end;
  1278. procedure TTestNewCSSResolver.Test_Selector_Id;
  1279. var
  1280. Button1: TDemoButton;
  1281. begin
  1282. Doc.Root:=TDemoNode.Create(nil);
  1283. Button1:=TDemoButton.Create(nil);
  1284. Button1.Name:='Button1';
  1285. Button1.Parent:=Doc.Root;
  1286. Doc.Style:='#Button1 { left: 12px; }';
  1287. ApplyStyle;
  1288. AssertEquals('Root.left','',Doc.Root.Left);
  1289. AssertEquals('Button1.left','12px',Button1.Left);
  1290. end;
  1291. procedure TTestNewCSSResolver.Test_Selector_Class;
  1292. var
  1293. Button1: TDemoButton;
  1294. begin
  1295. Doc.Root:=TDemoNode.Create(nil);
  1296. Button1:=TDemoButton.Create(nil);
  1297. Button1.CSSClasses.Add('west');
  1298. Button1.Parent:=Doc.Root;
  1299. Doc.Style:='.west { left: 13px; }';
  1300. ApplyStyle;
  1301. AssertEquals('Root.left','',Doc.Root.Left);
  1302. AssertEquals('Button1.left','13px',Button1.Left);
  1303. end;
  1304. procedure TTestNewCSSResolver.Test_Selector_ClassClass;
  1305. var
  1306. Button1, Button2: TDemoButton;
  1307. begin
  1308. Doc.Root:=TDemoNode.Create(nil);
  1309. Button1:=TDemoButton.Create(nil);
  1310. Button1.CSSClasses.Add('west');
  1311. Button1.Parent:=Doc.Root;
  1312. Button2:=TDemoButton.Create(nil);
  1313. Button2.CSSClasses.DelimitedText:='west south';
  1314. AssertEquals('Button2.CSSClasses.Count',2,Button2.CSSClasses.Count);
  1315. Button2.Parent:=Doc.Root;
  1316. Doc.Style:='.west.south { left: 10px; }';
  1317. ApplyStyle;
  1318. AssertEquals('Root.left','',Doc.Root.Left);
  1319. AssertEquals('Button1.left','',Button1.Left);
  1320. AssertEquals('Button2.left','10px',Button2.Left);
  1321. end;
  1322. procedure TTestNewCSSResolver.Test_Selector_ClassSpaceClass;
  1323. var
  1324. Button1: TDemoButton;
  1325. begin
  1326. Doc.Root:=TDemoNode.Create(nil);
  1327. Doc.Root.CSSClasses.Add('bird');
  1328. Button1:=TDemoButton.Create(nil);
  1329. Button1.CSSClasses.Add('west');
  1330. Button1.Parent:=Doc.Root;
  1331. Doc.Style:='.bird .west { left: 10px; }';
  1332. ApplyStyle;
  1333. AssertEquals('Root.left','',Doc.Root.Left);
  1334. AssertEquals('Button1.left','10px',Button1.Left);
  1335. end;
  1336. procedure TTestNewCSSResolver.Test_Selector_TypeCommaType;
  1337. var
  1338. Button1: TDemoButton;
  1339. Div1: TDemoDiv;
  1340. begin
  1341. Doc.Root:=TDemoNode.Create(nil);
  1342. Button1:=TDemoButton.Create(nil);
  1343. Button1.Parent:=Doc.Root;
  1344. Div1:=TDemoDiv.Create(nil);
  1345. Div1.Parent:=Doc.Root;
  1346. Doc.Style:='div, button { left: 10px; }';
  1347. ApplyStyle;
  1348. AssertEquals('Root.left','',Doc.Root.Left);
  1349. AssertEquals('Button1.left','10px',Button1.Left);
  1350. AssertEquals('Div1.left','10px',Div1.Left);
  1351. end;
  1352. procedure TTestNewCSSResolver.Test_Selector_ClassGTClass;
  1353. var
  1354. Div1, Div2: TDemoDiv;
  1355. begin
  1356. Doc.Root:=TDemoNode.Create(nil);
  1357. Doc.Root.Name:='root';
  1358. Doc.Root.CSSClasses.Add('lvl1');
  1359. Div1:=TDemoDiv.Create(nil);
  1360. Div1.Name:='Div1';
  1361. Div1.CSSClasses.Add('lvl2');
  1362. Div1.Parent:=Doc.Root;
  1363. Div2:=TDemoDiv.Create(nil);
  1364. Div2.Name:='Div2';
  1365. Div2.CSSClasses.Add('lvl3');
  1366. Div2.Parent:=Div1;
  1367. Doc.Style:=LinesToStr([
  1368. '.lvl1>.lvl2 { left: 10px; }', // set
  1369. '.lvl1>.lvl3 { top: 11px; }', // not set, not direct children
  1370. '.lvl2>.lvl3 { width: 12px; }', // set
  1371. '']);
  1372. ApplyStyle;
  1373. AssertEquals('Root.left','',Doc.Root.Left);
  1374. AssertEquals('Root.top','',Doc.Root.Top);
  1375. AssertEquals('Root.width','',Doc.Root.Width);
  1376. AssertEquals('Div1.left','10px',Div1.Left);
  1377. AssertEquals('Div1.top','',Div1.Top);
  1378. AssertEquals('Div1.width','',Div1.Width);
  1379. AssertEquals('Div2.left','',Div2.Left);
  1380. AssertEquals('Div2.top','',Div2.Top);
  1381. AssertEquals('Div2.width','12px',Div2.Width);
  1382. end;
  1383. procedure TTestNewCSSResolver.Test_Selector_TypePlusType;
  1384. var
  1385. Button1, Button2, Button3: TDemoButton;
  1386. Div1: TDemoDiv;
  1387. begin
  1388. Doc.Root:=TDemoNode.Create(nil);
  1389. Doc.Root.Name:='root';
  1390. Button1:=TDemoButton.Create(nil);
  1391. Button1.Name:='Button1';
  1392. Button1.Parent:=Doc.Root;
  1393. Div1:=TDemoDiv.Create(nil);
  1394. Div1.Name:='Div1';
  1395. Div1.Parent:=Doc.Root;
  1396. Button2:=TDemoButton.Create(nil);
  1397. Button2.Name:='Button2';
  1398. Button2.Parent:=Doc.Root;
  1399. Button3:=TDemoButton.Create(nil);
  1400. Button3.Name:='Button3';
  1401. Button3.Parent:=Doc.Root;
  1402. Doc.Style:='div+button { left: 10px; }'; // only Button2 has a prev sibling div
  1403. ApplyStyle;
  1404. AssertEquals('Root.left','',Doc.Root.Left);
  1405. AssertEquals('Button1.left','',Button1.Left);
  1406. AssertEquals('Div1.left','',Div1.Left);
  1407. AssertEquals('Button2.left','10px',Button2.Left);
  1408. AssertEquals('Button3.left','',Button3.Left);
  1409. end;
  1410. procedure TTestNewCSSResolver.Test_Selector_TypeTildeType;
  1411. var
  1412. Button1, Button2, Button3: TDemoButton;
  1413. Div1: TDemoDiv;
  1414. begin
  1415. Doc.Root:=TDemoNode.Create(nil);
  1416. Button1:=TDemoButton.Create(nil);
  1417. Button1.Parent:=Doc.Root;
  1418. Div1:=TDemoDiv.Create(nil);
  1419. Div1.Parent:=Doc.Root;
  1420. Button2:=TDemoButton.Create(nil);
  1421. Button2.Parent:=Doc.Root;
  1422. Button3:=TDemoButton.Create(nil);
  1423. Button3.Parent:=Doc.Root;
  1424. Doc.Style:='div~button { left: 10px; }';
  1425. ApplyStyle;
  1426. AssertEquals('Root.left','',Doc.Root.Left);
  1427. AssertEquals('Button1.left','',Button1.Left);
  1428. AssertEquals('Div1.left','',Div1.Left);
  1429. AssertEquals('Button2.left','10px',Button2.Left);
  1430. AssertEquals('Button3.left','10px',Button3.Left);
  1431. end;
  1432. procedure TTestNewCSSResolver.Test_Selector_HasAttribute;
  1433. var
  1434. Button1: TDemoButton;
  1435. begin
  1436. Doc.Root:=TDemoNode.Create(nil);
  1437. Doc.Root.Name:='root';
  1438. Doc.Root.ExplicitAttributes[naLeft]:='100px';
  1439. Button1:=TDemoButton.Create(nil);
  1440. Button1.Name:='Button1';
  1441. Button1.Parent:=Doc.Root;
  1442. Button1.ExplicitAttributes[naLeft]:='2px';
  1443. Button1.ExplicitCaption:='Click Button1';
  1444. Doc.Style:=LinesToStr([
  1445. '[left] { top: 3px; }',
  1446. '[caption] { width: 4px; }',
  1447. '']);
  1448. ApplyStyle;
  1449. AssertEquals('Root.Top','3px',Doc.Root.Top);
  1450. AssertEquals('Root.Width','',Doc.Root.Width);
  1451. AssertEquals('Button1.Top','3px',Button1.Top);
  1452. AssertEquals('Button1.Width','4px',Button1.Width);
  1453. end;
  1454. procedure TTestNewCSSResolver.Test_Selector_AttributeEquals;
  1455. var
  1456. Button1: TDemoButton;
  1457. begin
  1458. Doc.Root:=TDemoNode.Create(nil);
  1459. Doc.Root.ExplicitAttributes[naLeft]:='2px';
  1460. Button1:=TDemoButton.Create(nil);
  1461. Button1.Parent:=Doc.Root;
  1462. Button1.ExplicitAttributes[naLeft]:='3px';
  1463. Button1.ExplicitAttributes[naColor]:='maybe black';
  1464. Doc.Style:=LinesToStr([
  1465. '[left=2px] { top: 4px; }',
  1466. '[color="maybe black"] { width: 5px; }',
  1467. '']);
  1468. ApplyStyle;
  1469. AssertEquals('Root.Top','4px',Doc.Root.Top);
  1470. AssertEquals('Button1.Top','',Button1.Top);
  1471. AssertEquals('Button1.Width','5px',Button1.Width);
  1472. end;
  1473. procedure TTestNewCSSResolver.Test_Selector_AttributeEqualsI;
  1474. var
  1475. Button1: TDemoButton;
  1476. begin
  1477. Doc.Root:=TDemoNode.Create(nil);
  1478. Doc.Root.ExplicitAttributes[naLeft]:='2px';
  1479. Button1:=TDemoButton.Create(nil);
  1480. Button1.Parent:=Doc.Root;
  1481. Button1.ExplicitAttributes[naLeft]:='3px';
  1482. Button1.ExplicitAttributes[naColor]:='maybe Black';
  1483. Doc.Style:=LinesToStr([
  1484. '[left="2Px" i] { top: 4px; }',
  1485. '[color="Maybe bLack" i] { width: 5px; }',
  1486. '']);
  1487. ApplyStyle;
  1488. AssertEquals('Root.Top','4px',Doc.Root.Top);
  1489. AssertEquals('Button1.Top','',Button1.Top);
  1490. AssertEquals('Button1.Width','5px',Button1.Width);
  1491. end;
  1492. procedure TTestNewCSSResolver.Test_Selector_AttributeBeginsWith;
  1493. var
  1494. Button1: TDemoButton;
  1495. begin
  1496. Doc.Root:=TDemoNode.Create(nil);
  1497. Doc.Root.ExplicitAttributes[naLeft]:='Foo';
  1498. Button1:=TDemoButton.Create(nil);
  1499. Button1.Parent:=Doc.Root;
  1500. Button1.ExplicitAttributes[naLeft]:='Foo Bar';
  1501. Doc.Style:=LinesToStr([
  1502. '[left^=Fo] { top: 4px; }',
  1503. '[left^="Foo B"] { width: 5px; }',
  1504. '']);
  1505. ApplyStyle;
  1506. AssertEquals('Root.Top','4px',Doc.Root.Top);
  1507. AssertEquals('Root.Width','',Doc.Root.Width);
  1508. AssertEquals('Button1.Top','4px',Button1.Top);
  1509. AssertEquals('Button1.Width','5px',Button1.Width);
  1510. end;
  1511. procedure TTestNewCSSResolver.Test_Selector_AttributeEndsWith;
  1512. var
  1513. Button1: TDemoButton;
  1514. begin
  1515. Doc.Root:=TDemoNode.Create(nil);
  1516. Doc.Root.ExplicitAttributes[naLeft]:='Foo';
  1517. Button1:=TDemoButton.Create(nil);
  1518. Button1.Parent:=Doc.Root;
  1519. Button1.ExplicitAttributes[naLeft]:='Foo Bar';
  1520. Doc.Style:=LinesToStr([
  1521. '[left$=o] { top: 4px; }',
  1522. '[left$="o Bar"] { width: 5px; }',
  1523. '']);
  1524. ApplyStyle;
  1525. AssertEquals('Root.Top','4px',Doc.Root.Top);
  1526. AssertEquals('Root.Width','',Doc.Root.Width);
  1527. AssertEquals('Button1.Top','',Button1.Top);
  1528. AssertEquals('Button1.Width','5px',Button1.Width);
  1529. end;
  1530. procedure TTestNewCSSResolver.Test_Selector_AttributeBeginsWithHyphen;
  1531. var
  1532. Button1: TDemoButton;
  1533. begin
  1534. Doc.Root:=TDemoNode.Create(nil);
  1535. Doc.Root.ExplicitAttributes[naLeft]:='Foo';
  1536. Button1:=TDemoButton.Create(nil);
  1537. Button1.Parent:=Doc.Root;
  1538. Button1.ExplicitAttributes[naLeft]:='Foo-Bar';
  1539. Doc.Style:=LinesToStr([
  1540. '[left|=Foo] { top: 4px; }',
  1541. '[left|="Fo"] { width: 5px; }',
  1542. '']);
  1543. ApplyStyle;
  1544. AssertEquals('Root.Top','4px',Doc.Root.Top);
  1545. AssertEquals('Root.Width','',Doc.Root.Width);
  1546. AssertEquals('Button1.Top','4px',Button1.Top);
  1547. AssertEquals('Button1.Width','',Button1.Width);
  1548. end;
  1549. procedure TTestNewCSSResolver.Test_Selector_AttributeContainsWord;
  1550. var
  1551. Button1: TDemoButton;
  1552. begin
  1553. Doc.Root:=TDemoNode.Create(nil);
  1554. Doc.Root.Name:='root';
  1555. Doc.Root.ExplicitAttributes[naLeft]:='One Two Three';
  1556. Button1:=TDemoButton.Create(nil);
  1557. Button1.Name:='Button1';
  1558. Button1.Parent:=Doc.Root;
  1559. Button1.ExplicitAttributes[naLeft]:='Four Five';
  1560. Doc.Style:=LinesToStr([
  1561. '[left~=One] { top: 4px; }',
  1562. '[left~=Two] { width: 5px; }',
  1563. '[left~=Three] { height: 6px; }',
  1564. '[left~="Four Five"] { color: #123; }', // not one word, so does not match!
  1565. '[left~=our] { display: none; }',
  1566. '']);
  1567. ApplyStyle;
  1568. AssertEquals('Root.Top','4px',Doc.Root.Top);
  1569. AssertEquals('Root.Width','5px',Doc.Root.Width);
  1570. AssertEquals('Root.Height','6px',Doc.Root.Height);
  1571. AssertEquals('Root.Color','',Doc.Root.Color);
  1572. AssertEquals('Root.Display','',Doc.Root.Display);
  1573. AssertEquals('Button1.Top','',Button1.Top);
  1574. AssertEquals('Button1.Width','',Button1.Width);
  1575. AssertEquals('Button1.Height','',Button1.Height);
  1576. AssertEquals('Button1.Color','',Button1.Color);
  1577. AssertEquals('Button1.Display','inline-block',Button1.Display);
  1578. end;
  1579. procedure TTestNewCSSResolver.Test_Selector_AttributeContainsSubstring;
  1580. var
  1581. Button1: TDemoButton;
  1582. begin
  1583. Doc.Root:=TDemoNode.Create(nil);
  1584. Doc.Root.ExplicitAttributes[naLeft]:='Foo';
  1585. Button1:=TDemoButton.Create(nil);
  1586. Button1.Parent:=Doc.Root;
  1587. Button1.ExplicitAttributes[naLeft]:='Foo Bar';
  1588. Doc.Style:=LinesToStr([
  1589. '[left*=oo] { top: 4px; }',
  1590. '[left*="o B"] { width: 5px; }',
  1591. '']);
  1592. ApplyStyle;
  1593. AssertEquals('Root.Top','4px',Doc.Root.Top);
  1594. AssertEquals('Root.Width','',Doc.Root.Width);
  1595. AssertEquals('Button1.Top','4px',Button1.Top);
  1596. AssertEquals('Button1.Width','5px',Button1.Width);
  1597. end;
  1598. procedure TTestNewCSSResolver.Test_Selector_Root;
  1599. var
  1600. Button1: TDemoButton;
  1601. begin
  1602. Doc.Root:=TDemoNode.Create(nil);
  1603. Doc.Root.ExplicitAttributes[naLeft]:='Foo';
  1604. Button1:=TDemoButton.Create(nil);
  1605. Button1.Parent:=Doc.Root;
  1606. Doc.Style:=LinesToStr([
  1607. ':roOt { top: 4px; }',
  1608. '']);
  1609. ApplyStyle;
  1610. AssertEquals('Root.Top','4px',Doc.Root.Top);
  1611. AssertEquals('Button1.Top','',Button1.Top);
  1612. end;
  1613. procedure TTestNewCSSResolver.Test_Selector_Empty;
  1614. var
  1615. Div1, Div11, Div2: TDemoDiv;
  1616. begin
  1617. Doc.Root:=TDemoNode.Create(nil);
  1618. Div1:=TDemoDiv.Create(nil);
  1619. Div1.Parent:=Doc.Root;
  1620. Div11:=TDemoDiv.Create(nil);
  1621. Div11.Parent:=Div1;
  1622. Div2:=TDemoDiv.Create(nil);
  1623. Div2.Parent:=Doc.Root;
  1624. Doc.Style:=LinesToStr([
  1625. ':eMpty { left: 1px; }',
  1626. 'div:emPty { top: 2px; }',
  1627. '']);
  1628. ApplyStyle;
  1629. AssertEquals('Root.Left','',Doc.Root.Left);
  1630. AssertEquals('Root.Top','',Doc.Root.Top);
  1631. AssertEquals('Div1.Left','',Div1.Left);
  1632. AssertEquals('Div1.Top','',Div1.Top);
  1633. AssertEquals('Div11.Left','1px',Div11.Left);
  1634. AssertEquals('Div11.Top','2px',Div11.Top);
  1635. AssertEquals('Div2.Left','1px',Div2.Left);
  1636. AssertEquals('Div2.Top','2px',Div2.Top);
  1637. end;
  1638. procedure TTestNewCSSResolver.Test_Selector_FirstChild;
  1639. var
  1640. Div1, Div11, Div12, Div2: TDemoDiv;
  1641. begin
  1642. Doc.Root:=TDemoNode.Create(nil);
  1643. Div1:=TDemoDiv.Create(nil);
  1644. Div1.Parent:=Doc.Root;
  1645. Div11:=TDemoDiv.Create(nil);
  1646. Div11.Parent:=Div1;
  1647. Div12:=TDemoDiv.Create(nil);
  1648. Div12.Parent:=Div1;
  1649. Div2:=TDemoDiv.Create(nil);
  1650. Div2.Parent:=Doc.Root;
  1651. Doc.Style:=LinesToStr([
  1652. ':first-child { left: 1px; }',
  1653. 'div:first-child { top: 2px; }',
  1654. '']);
  1655. ApplyStyle;
  1656. AssertEquals('Root.Left','1px',Doc.Root.Left);
  1657. AssertEquals('Root.Top','',Doc.Root.Top);
  1658. AssertEquals('Div1.Left','1px',Div1.Left);
  1659. AssertEquals('Div1.Top','2px',Div1.Top);
  1660. AssertEquals('Div11.Left','1px',Div11.Left);
  1661. AssertEquals('Div11.Top','2px',Div11.Top);
  1662. AssertEquals('Div12.Left','',Div12.Left);
  1663. AssertEquals('Div12.Top','',Div12.Top);
  1664. AssertEquals('Div2.Left','',Div2.Left);
  1665. AssertEquals('Div2.Top','',Div2.Top);
  1666. end;
  1667. procedure TTestNewCSSResolver.Test_Selector_LastChild;
  1668. var
  1669. Div1, Div11, Div2: TDemoDiv;
  1670. Button12: TDemoButton;
  1671. begin
  1672. Doc.Root:=TDemoNode.Create(nil);
  1673. Div1:=TDemoDiv.Create(nil);
  1674. Div1.Parent:=Doc.Root;
  1675. Div11:=TDemoDiv.Create(nil);
  1676. Div11.Parent:=Div1;
  1677. Button12:=TDemoButton.Create(nil);
  1678. Button12.Parent:=Div1;
  1679. Div2:=TDemoDiv.Create(nil);
  1680. Div2.Parent:=Doc.Root;
  1681. Doc.Style:=LinesToStr([
  1682. ':last-child { left: 6px; }',
  1683. 'div:last-child { top: 7px; }',
  1684. '']);
  1685. ApplyStyle;
  1686. AssertEquals('Root.Left','6px',Doc.Root.Left);
  1687. AssertEquals('Root.Top','',Doc.Root.Top);
  1688. AssertEquals('Div1.Left','',Div1.Left);
  1689. AssertEquals('Div1.Top','',Div1.Top);
  1690. AssertEquals('Div11.Left','',Div11.Left);
  1691. AssertEquals('Div11.Top','',Div11.Top);
  1692. AssertEquals('Button12.Left','6px',Button12.Left);
  1693. AssertEquals('Button12.Top','',Button12.Top);
  1694. AssertEquals('Div2.Left','6px',Div2.Left);
  1695. AssertEquals('Div2.Top','7px',Div2.Top);
  1696. end;
  1697. procedure TTestNewCSSResolver.Test_Selector_OnlyChild;
  1698. var
  1699. Div1, Div11, Div2: TDemoDiv;
  1700. Button12: TDemoButton;
  1701. begin
  1702. Doc.Root:=TDemoNode.Create(nil);
  1703. Doc.Root.Name:='root';
  1704. Div1:=TDemoDiv.Create(nil);
  1705. Div1.Name:='Div1';
  1706. Div1.Parent:=Doc.Root;
  1707. Div11:=TDemoDiv.Create(nil);
  1708. Div11.Name:='Div11';
  1709. Div11.Parent:=Div1;
  1710. Div2:=TDemoDiv.Create(nil);
  1711. Div2.Name:='Div2';
  1712. Div2.Parent:=Doc.Root;
  1713. Button12:=TDemoButton.Create(nil);
  1714. Button12.Name:='Button12';
  1715. Button12.Parent:=Div2;
  1716. Doc.Style:=LinesToStr([
  1717. ':only-child { left: 8px; }',
  1718. 'div:only-child { top: 9px; }',
  1719. '']);
  1720. ApplyStyle;
  1721. AssertEquals('Root.Left','8px',Doc.Root.Left);
  1722. AssertEquals('Root.Top','',Doc.Root.Top);
  1723. AssertEquals('Div1.Left','',Div1.Left);
  1724. AssertEquals('Div1.Top','',Div1.Top);
  1725. AssertEquals('Div11.Left','8px',Div11.Left);
  1726. AssertEquals('Div11.Top','9px',Div11.Top);
  1727. AssertEquals('Div2.Left','',Div2.Left);
  1728. AssertEquals('Div2.Top','',Div2.Top);
  1729. AssertEquals('Button12.Left','8px',Button12.Left);
  1730. AssertEquals('Button12.Top','',Button12.Top);
  1731. end;
  1732. procedure TTestNewCSSResolver.Test_Selector_Not;
  1733. var
  1734. Div1, Div11, Div2: TDemoDiv;
  1735. Button12: TDemoButton;
  1736. begin
  1737. Doc.Root:=TDemoNode.Create(nil);
  1738. Doc.Root.Name:='root';
  1739. Div1:=TDemoDiv.Create(nil);
  1740. Div1.Name:='Div1';
  1741. Div1.Parent:=Doc.Root;
  1742. Div11:=TDemoDiv.Create(nil);
  1743. Div11.Name:='Div11';
  1744. Div11.Parent:=Div1;
  1745. Div2:=TDemoDiv.Create(nil);
  1746. Div2.Name:='Div2';
  1747. Div2.Parent:=Doc.Root;
  1748. Button12:=TDemoButton.Create(nil);
  1749. Button12.Name:='Button12';
  1750. Button12.Parent:=Div2;
  1751. Doc.Style:=LinesToStr([
  1752. ':not(:only-child) { left: 8px; }',
  1753. ':not(div:only-child) { top: 9px; }',
  1754. '']);
  1755. ApplyStyle;
  1756. AssertEquals('Root.Left','',Doc.Root.Left);
  1757. AssertEquals('Root.Top','9px',Doc.Root.Top);
  1758. AssertEquals('Div1.Left','8px',Div1.Left);
  1759. AssertEquals('Div1.Top','9px',Div1.Top);
  1760. AssertEquals('Div11.Left','',Div11.Left);
  1761. AssertEquals('Div11.Top','',Div11.Top);
  1762. AssertEquals('Div2.Left','8px',Div2.Left);
  1763. AssertEquals('Div2.Top','9px',Div2.Top);
  1764. AssertEquals('Button12.Left','',Button12.Left);
  1765. AssertEquals('Button12.Top','9px',Button12.Top);
  1766. end;
  1767. procedure TTestNewCSSResolver.Test_Selector_NthChild;
  1768. var
  1769. Div1, Div2, Div3, Div4: TDemoDiv;
  1770. begin
  1771. Doc.Root:=TDemoNode.Create(nil);
  1772. Div1:=TDemoDiv.Create(nil);
  1773. Div1.Parent:=Doc.Root;
  1774. Div2:=TDemoDiv.Create(nil);
  1775. Div2.Parent:=Doc.Root;
  1776. Div3:=TDemoDiv.Create(nil);
  1777. Div3.Parent:=Doc.Root;
  1778. Div4:=TDemoDiv.Create(nil);
  1779. Div4.Parent:=Doc.Root;
  1780. Doc.Style:=LinesToStr([
  1781. 'div:nth-child(2n+1) { left: 8px; }',
  1782. 'div:nth-child(n+3) { border-width: 6px; }',
  1783. 'div:nth-child(-n+2) { height: 7em; }',
  1784. 'div:nth-child(even) { top: 3px; }',
  1785. 'div:nth-child(odd) { width: 4px; }',
  1786. '']);
  1787. ApplyStyle;
  1788. AssertEquals('Root.Left','',Doc.Root.Left);
  1789. AssertEquals('Root.BorderWidth','',Doc.Root.BorderWidth);
  1790. AssertEquals('Root.Height','',Doc.Root.Height);
  1791. AssertEquals('Root.Top','',Doc.Root.Top);
  1792. AssertEquals('Root.Width','',Doc.Root.Width);
  1793. AssertEquals('Div1.Left','8px',Div1.Left);
  1794. AssertEquals('Div1.BorderWidth','',Div1.BorderWidth);
  1795. AssertEquals('Div1.Height','7em',Div1.Height);
  1796. AssertEquals('Div1.Top','',Div1.Top);
  1797. AssertEquals('Div1.Width','4px',Div1.Width);
  1798. AssertEquals('Div2.Left','',Div2.Left);
  1799. AssertEquals('Div2.BorderWidth','',Div2.BorderWidth);
  1800. AssertEquals('Div2.Height','7em',Div2.Height);
  1801. AssertEquals('Div2.Top','3px',Div2.Top);
  1802. AssertEquals('Div2.Width','',Div2.Width);
  1803. AssertEquals('Div3.Left','8px',Div3.Left);
  1804. AssertEquals('Div3.BorderWidth','6px',Div3.BorderWidth);
  1805. AssertEquals('Div3.Height','',Div3.Height);
  1806. AssertEquals('Div3.Top','',Div3.Top);
  1807. AssertEquals('Div3.Width','4px',Div3.Width);
  1808. AssertEquals('Div4.Left','',Div4.Left);
  1809. AssertEquals('Div4.BorderWidth','6px',Div4.BorderWidth);
  1810. AssertEquals('Div4.Height','',Div4.Height);
  1811. AssertEquals('Div4.Top','3px',Div4.Top);
  1812. AssertEquals('Div4.Width','',Div4.Width);
  1813. end;
  1814. procedure TTestNewCSSResolver.Test_Selector_NthLastChild;
  1815. var
  1816. Div1, Div2, Div3, Div4: TDemoDiv;
  1817. begin
  1818. Doc.Root:=TDemoNode.Create(nil);
  1819. Div1:=TDemoDiv.Create(nil);
  1820. Div1.Parent:=Doc.Root;
  1821. Div2:=TDemoDiv.Create(nil);
  1822. Div2.Parent:=Doc.Root;
  1823. Div3:=TDemoDiv.Create(nil);
  1824. Div3.Parent:=Doc.Root;
  1825. Div4:=TDemoDiv.Create(nil);
  1826. Div4.Parent:=Doc.Root;
  1827. Doc.Style:=LinesToStr([
  1828. ':nth-last-child(2n+1) { left: 8px; }',
  1829. '']);
  1830. ApplyStyle;
  1831. AssertEquals('Root.Left','',Doc.Root.Left);
  1832. AssertEquals('Div1.Left','',Div1.Left);
  1833. AssertEquals('Div2.Left','8px',Div2.Left);
  1834. AssertEquals('Div3.Left','',Div3.Left);
  1835. AssertEquals('Div4.Left','8px',Div4.Left);
  1836. end;
  1837. procedure TTestNewCSSResolver.Test_Selector_NthChildOf;
  1838. var
  1839. Div1, Div2, Div3, Div4: TDemoDiv;
  1840. begin
  1841. Doc.Root:=TDemoNode.Create(nil);
  1842. Doc.Root.Name:='root';
  1843. Div1:=TDemoDiv.Create(nil);
  1844. Div1.Name:='Div1';
  1845. Div1.Parent:=Doc.Root;
  1846. Div2:=TDemoDiv.Create(nil);
  1847. Div2.Name:='Div2';
  1848. Div2.Parent:=Doc.Root;
  1849. Div2.ExplicitAttributes[naTop]:='3px';
  1850. Div3:=TDemoDiv.Create(nil);
  1851. Div3.Name:='Div3';
  1852. Div3.Parent:=Doc.Root;
  1853. Div3.ExplicitAttributes[naTop]:='3px';
  1854. Div4:=TDemoDiv.Create(nil);
  1855. Div4.Name:='Div4';
  1856. Div4.Parent:=Doc.Root;
  1857. Div4.ExplicitAttributes[naTop]:='3px';
  1858. Doc.Style:=LinesToStr([
  1859. ':nth-child(2n+1 of [top=3px]) { left: 5px; }',
  1860. '']);
  1861. ApplyStyle;
  1862. AssertEquals('Root.Left','',Doc.Root.Left);
  1863. AssertEquals('Div1.Left','',Div1.Left);
  1864. AssertEquals('Div2.Left','5px',Div2.Left);
  1865. AssertEquals('Div3.Left','',Div3.Left);
  1866. AssertEquals('Div4.Left','5px',Div4.Left);
  1867. end;
  1868. procedure TTestNewCSSResolver.Test_Selector_FirstOfType;
  1869. var
  1870. Div1, Div11, Div13, Div2: TDemoDiv;
  1871. Button12: TDemoButton;
  1872. begin
  1873. Doc.Root:=TDemoNode.Create(nil);
  1874. Div1:=TDemoDiv.Create(nil);
  1875. Div1.Parent:=Doc.Root;
  1876. Div11:=TDemoDiv.Create(nil);
  1877. Div11.Parent:=Div1;
  1878. Button12:=TDemoButton.Create(nil);
  1879. Button12.Parent:=Div1;
  1880. Div13:=TDemoDiv.Create(nil);
  1881. Div13.Parent:=Div1;
  1882. Div2:=TDemoDiv.Create(nil);
  1883. Div2.Parent:=Doc.Root;
  1884. Doc.Style:=LinesToStr([
  1885. ':first-of-type { left: 6px; }',
  1886. 'div:first-of-type { top: 7px; }',
  1887. '']);
  1888. ApplyStyle;
  1889. AssertEquals('Root.Left','6px',Doc.Root.Left);
  1890. AssertEquals('Root.Top','',Doc.Root.Top);
  1891. AssertEquals('Div1.Left','6px',Div1.Left);
  1892. AssertEquals('Div1.Top','7px',Div1.Top);
  1893. AssertEquals('Div11.Left','6px',Div11.Left);
  1894. AssertEquals('Div11.Top','7px',Div11.Top);
  1895. AssertEquals('Button12.Left','6px',Button12.Left);
  1896. AssertEquals('Button12.Top','',Button12.Top);
  1897. AssertEquals('Div13.Left','',Div13.Left);
  1898. AssertEquals('Div13.Top','',Div13.Top);
  1899. AssertEquals('Div2.Left','',Div2.Left);
  1900. AssertEquals('Div2.Top','',Div2.Top);
  1901. end;
  1902. procedure TTestNewCSSResolver.Test_Selector_LastOfType;
  1903. var
  1904. Div1, Div11, Div13, Div2: TDemoDiv;
  1905. Button12: TDemoButton;
  1906. begin
  1907. Doc.Root:=TDemoNode.Create(nil);
  1908. Div1:=TDemoDiv.Create(nil);
  1909. Div1.Parent:=Doc.Root;
  1910. Div11:=TDemoDiv.Create(nil);
  1911. Div11.Parent:=Div1;
  1912. Button12:=TDemoButton.Create(nil);
  1913. Button12.Parent:=Div1;
  1914. Div13:=TDemoDiv.Create(nil);
  1915. Div13.Parent:=Div1;
  1916. Div2:=TDemoDiv.Create(nil);
  1917. Div2.Parent:=Doc.Root;
  1918. Doc.Style:=LinesToStr([
  1919. ':last-of-type { left: 6px; }',
  1920. 'div:last-of-type { top: 7px; }',
  1921. '']);
  1922. ApplyStyle;
  1923. AssertEquals('Root.Left','6px',Doc.Root.Left);
  1924. AssertEquals('Root.Top','',Doc.Root.Top);
  1925. AssertEquals('Div1.Left','',Div1.Left);
  1926. AssertEquals('Div1.Top','',Div1.Top);
  1927. AssertEquals('Div11.Left','',Div11.Left);
  1928. AssertEquals('Div11.Top','',Div11.Top);
  1929. AssertEquals('Button12.Left','6px',Button12.Left);
  1930. AssertEquals('Button12.Top','',Button12.Top);
  1931. AssertEquals('Div13.Left','6px',Div13.Left);
  1932. AssertEquals('Div13.Top','7px',Div13.Top);
  1933. AssertEquals('Div2.Left','6px',Div2.Left);
  1934. AssertEquals('Div2.Top','7px',Div2.Top);
  1935. end;
  1936. procedure TTestNewCSSResolver.Test_Selector_OnlyOfType;
  1937. var
  1938. Div1, Div11, Div2: TDemoDiv;
  1939. Button12: TDemoButton;
  1940. begin
  1941. Doc.Root:=TDemoNode.Create(nil);
  1942. Div1:=TDemoDiv.Create(nil);
  1943. Div1.Parent:=Doc.Root;
  1944. Div11:=TDemoDiv.Create(nil);
  1945. Div11.Parent:=Div1;
  1946. Button12:=TDemoButton.Create(nil);
  1947. Button12.Parent:=Div1;
  1948. Div2:=TDemoDiv.Create(nil);
  1949. Div2.Parent:=Doc.Root;
  1950. Doc.Style:=LinesToStr([
  1951. ':only-of-type { left: 6px; }',
  1952. 'div:only-of-type { top: 7px; }',
  1953. '']);
  1954. ApplyStyle;
  1955. AssertEquals('Root.Left','6px',Doc.Root.Left);
  1956. AssertEquals('Root.Top','',Doc.Root.Top);
  1957. AssertEquals('Div1.Left','',Div1.Left);
  1958. AssertEquals('Div1.Top','',Div1.Top);
  1959. AssertEquals('Div11.Left','6px',Div11.Left);
  1960. AssertEquals('Div11.Top','7px',Div11.Top);
  1961. AssertEquals('Button12.Left','6px',Button12.Left);
  1962. AssertEquals('Button12.Top','',Button12.Top);
  1963. AssertEquals('Div2.Left','',Div2.Left);
  1964. AssertEquals('Div2.Top','',Div2.Top);
  1965. end;
  1966. procedure TTestNewCSSResolver.Test_Selector_NthOfType;
  1967. var
  1968. Div1, Div2, Div3, Div4: TDemoDiv;
  1969. Button1, Button2: TDemoButton;
  1970. begin
  1971. Doc.Root:=TDemoNode.Create(nil);
  1972. Doc.Root.Name:='root';
  1973. Div1:=TDemoDiv.Create(nil);
  1974. Div1.Name:='Div1';
  1975. Div1.Parent:=Doc.Root;
  1976. Button1:=TDemoButton.Create(nil);
  1977. Button1.Name:='Button1';
  1978. Button1.Parent:=Doc.Root;
  1979. Div2:=TDemoDiv.Create(nil);
  1980. Div2.Name:='Div2';
  1981. Div2.Parent:=Doc.Root;
  1982. Div3:=TDemoDiv.Create(nil);
  1983. Div3.Name:='Div3';
  1984. Div3.Parent:=Doc.Root;
  1985. Button2:=TDemoButton.Create(nil);
  1986. Button2.Name:='Button2';
  1987. Button2.Parent:=Doc.Root;
  1988. Div4:=TDemoDiv.Create(nil);
  1989. Div4.Name:='Div4';
  1990. Div4.Parent:=Doc.Root;
  1991. Doc.Style:=LinesToStr([
  1992. ':nth-of-type(2n+1) { left: 8px; }',
  1993. '']);
  1994. ApplyStyle;
  1995. AssertEquals('Root.Left','',Doc.Root.Left);
  1996. AssertEquals('Div1.Left','8px',Div1.Left);
  1997. AssertEquals('Button1.Left','8px',Button1.Left);
  1998. AssertEquals('Div2.Left','',Div2.Left);
  1999. AssertEquals('Div3.Left','8px',Div3.Left);
  2000. AssertEquals('Button2.Left','',Button2.Left);
  2001. AssertEquals('Div4.Left','',Div4.Left);
  2002. end;
  2003. procedure TTestNewCSSResolver.Test_Selector_NthLastOfType;
  2004. var
  2005. Div1, Div2, Div3, Div4: TDemoDiv;
  2006. Button1, Button2: TDemoButton;
  2007. begin
  2008. Doc.Root:=TDemoNode.Create(nil);
  2009. Div1:=TDemoDiv.Create(nil);
  2010. Div1.Parent:=Doc.Root;
  2011. Button1:=TDemoButton.Create(nil);
  2012. Button1.Parent:=Doc.Root;
  2013. Div2:=TDemoDiv.Create(nil);
  2014. Div2.Parent:=Doc.Root;
  2015. Div3:=TDemoDiv.Create(nil);
  2016. Div3.Parent:=Doc.Root;
  2017. Button2:=TDemoButton.Create(nil);
  2018. Button2.Parent:=Doc.Root;
  2019. Div4:=TDemoDiv.Create(nil);
  2020. Div4.Parent:=Doc.Root;
  2021. Doc.Style:=LinesToStr([
  2022. ':nth-last-of-type(2n+1) { left: 8px; }',
  2023. '']);
  2024. ApplyStyle;
  2025. AssertEquals('Root.Left','',Doc.Root.Left);
  2026. AssertEquals('Div1.Left','',Div1.Left);
  2027. AssertEquals('Button1.Left','',Button1.Left);
  2028. AssertEquals('Div2.Left','8px',Div2.Left);
  2029. AssertEquals('Div3.Left','',Div3.Left);
  2030. AssertEquals('Button2.Left','8px',Button2.Left);
  2031. AssertEquals('Div4.Left','8px',Div4.Left);
  2032. end;
  2033. procedure TTestNewCSSResolver.Test_Selector_Is;
  2034. var
  2035. Div1, Div2: TDemoDiv;
  2036. Button1, Button2: TDemoButton;
  2037. Span1: TDemoSpan;
  2038. begin
  2039. Doc.Root:=TDemoNode.Create(nil);
  2040. Doc.Root.Name:='root';
  2041. Div1:=TDemoDiv.Create(nil);
  2042. Div1.Name:='Div1';
  2043. Div1.Parent:=Doc.Root;
  2044. Div1.ExplicitAttributes[naTop]:='3px';
  2045. Button1:=TDemoButton.Create(nil);
  2046. Button1.Name:='Button1';
  2047. Button1.Parent:=Doc.Root;
  2048. Div2:=TDemoDiv.Create(nil);
  2049. Div2.Parent:=Doc.Root;
  2050. Span1:=TDemoSpan.Create(nil);
  2051. Span1.Parent:=Doc.Root;
  2052. Span1.ExplicitAttributes[naTop]:='3px';
  2053. Button2:=TDemoButton.Create(nil);
  2054. Button2.Parent:=Doc.Root;
  2055. Button2.ExplicitAttributes[naTop]:='3px';
  2056. Doc.Style:=LinesToStr([
  2057. ':is(div, button)[top=3px] { left: 7px; }',
  2058. '']);
  2059. ApplyStyle;
  2060. AssertEquals('Root.Left','',Doc.Root.Left);
  2061. AssertEquals('Div1.Left','7px',Div1.Left);
  2062. AssertEquals('Button1.Left','',Button1.Left);
  2063. AssertEquals('Div2.Left','',Div2.Left);
  2064. AssertEquals('Span1.Left','',Div2.Left);
  2065. AssertEquals('Button2.Left','7px',Button2.Left);
  2066. end;
  2067. procedure TTestNewCSSResolver.Test_Selector_Where;
  2068. var
  2069. Div1, Div2: TDemoDiv;
  2070. begin
  2071. Doc.Root:=TDemoNode.Create(nil);
  2072. Doc.Root.Name:='root';
  2073. Div1:=TDemoDiv.Create(nil);
  2074. Div1.Name:='Div1';
  2075. Div1.Parent:=Doc.Root;
  2076. Div1.ExplicitAttributes[naTop]:='3px';
  2077. Div2:=TDemoDiv.Create(nil);
  2078. Div2.Name:='Div2';
  2079. Div2.Parent:=Div1;
  2080. Div2.ExplicitAttributes[naTop]:='3px';
  2081. Doc.Style:=LinesToStr([
  2082. ':where(div[top=3px]) { left: 1px; }',
  2083. 'div div { left: 2px; }',
  2084. '']);
  2085. ApplyStyle;
  2086. AssertEquals('Root.Left','',Doc.Root.Left);
  2087. AssertEquals('Div1.Left','1px',Div1.Left);
  2088. AssertEquals('Div2.Left','2px',Div2.Left);
  2089. end;
  2090. procedure TTestNewCSSResolver.Test_Selector_Hover;
  2091. var
  2092. Div1, Div11: TDemoDiv;
  2093. Button1: TDemoButton;
  2094. begin
  2095. Doc.Root:=TDemoNode.Create(nil);
  2096. Doc.Root.Name:='root';
  2097. Div1:=TDemoDiv.Create(nil);
  2098. Div1.Name:='Div1';
  2099. Div1.Parent:=Doc.Root;
  2100. Div1.Hover:=true;
  2101. Button1:=TDemoButton.Create(nil);
  2102. Button1.Name:='Button1';
  2103. Button1.Parent:=Div1;
  2104. Button1.Hover:=true;
  2105. Div11:=TDemoDiv.Create(nil);
  2106. Div11.Name:='Div11';
  2107. Div11.Parent:=Div1;
  2108. Doc.Style:=LinesToStr([
  2109. ':hover { left: 1px; }',
  2110. 'button:hover { top: 2px; }',
  2111. '']);
  2112. ApplyStyle;
  2113. AssertEquals('Root.Left','',Doc.Root.Left);
  2114. AssertEquals('Root.Top','',Doc.Root.Top);
  2115. AssertEquals('Div1.Left','1px',Div1.Left);
  2116. AssertEquals('Div1.Top','',Div1.Top);
  2117. AssertEquals('Button1.Left','1px',Button1.Left);
  2118. AssertEquals('Button1.Top','2px',Button1.Top);
  2119. AssertEquals('Div11.Left','',Div11.Left);
  2120. AssertEquals('Div11.Top','',Div11.Top);
  2121. end;
  2122. procedure TTestNewCSSResolver.Test_InlineStyle;
  2123. var
  2124. Div1: TDemoDiv;
  2125. begin
  2126. Doc.Root:=TDemoNode.Create(nil);
  2127. Div1:=TDemoDiv.Create(nil);
  2128. Div1.Parent:=Doc.Root;
  2129. Div1.InlineStyle:='left: 10px; top: 5px';
  2130. Doc.Style:=LinesToStr([
  2131. 'div { left: 6px; }',
  2132. '']);
  2133. ApplyStyle;
  2134. AssertEquals('Root.Left','',Doc.Root.Left);
  2135. AssertEquals('Div1.Left','10px',Div1.Left);
  2136. AssertEquals('Div1.Top','5px',Div1.Top);
  2137. end;
  2138. procedure TTestNewCSSResolver.Test_Specifity_Id_Class;
  2139. var
  2140. Div1: TDemoDiv;
  2141. begin
  2142. Doc.Root:=TDemoNode.Create(nil);
  2143. Div1:=TDemoDiv.Create(nil);
  2144. Div1.Name:='Div1';
  2145. Div1.Parent:=Doc.Root;
  2146. Div1.CSSClasses.Add('bird');
  2147. Doc.Style:=LinesToStr([
  2148. '.bird { left: 6px; }',
  2149. '#Div1 { left: 7px; top: 8px; }', // id has higher specifity, no matter if before or after a .class
  2150. '.bird { top: 9px; }',
  2151. '']);
  2152. ApplyStyle;
  2153. AssertEquals('Root.Left','',Doc.Root.Left);
  2154. AssertEquals('Div1.Left','7px',Div1.Left);
  2155. AssertEquals('Div1.Top','8px',Div1.Top);
  2156. end;
  2157. procedure TTestNewCSSResolver.Test_Specifity_Important;
  2158. var
  2159. Div1: TDemoDiv;
  2160. begin
  2161. Doc.Root:=TDemoNode.Create(nil);
  2162. Div1:=TDemoDiv.Create(nil);
  2163. Div1.Name:='Div1';
  2164. Div1.Parent:=Doc.Root;
  2165. Div1.CSSClasses.Add('bird');
  2166. Doc.Style:=LinesToStr([
  2167. '.bird { left: 6px !important; }',
  2168. '#Div1 { left: 7px; top: 8px; }',
  2169. '.bird { top: 9px ! important; }',
  2170. '']);
  2171. ApplyStyle;
  2172. AssertEquals('Root.Left','',Doc.Root.Left);
  2173. AssertEquals('Div1.Left','6px',Div1.Left);
  2174. AssertEquals('Div1.Top','9px',Div1.Top);
  2175. end;
  2176. procedure TTestNewCSSResolver.Test_Specifity_Shorthand_OneRule;
  2177. var
  2178. Div1: TDemoDiv;
  2179. begin
  2180. Doc.Root:=TDemoNode.Create(nil);
  2181. Div1:=TDemoDiv.Create(nil);
  2182. Div1.Name:='Div1';
  2183. Div1.Parent:=Doc.Root;
  2184. Div1.CSSClasses.Add('bird');
  2185. Doc.Style:='.bird { border-color: blue; border: 6px red; border-width: 7px; }';
  2186. ApplyStyle;
  2187. AssertEquals('Div1.BorderColor','red',Div1.BorderColor);
  2188. AssertEquals('Div1.BorderWidth','7px',Div1.BorderWidth);
  2189. end;
  2190. procedure TTestNewCSSResolver.Test_Specifity_Shorthand_ClassClass;
  2191. var
  2192. Div1: TDemoDiv;
  2193. begin
  2194. Doc.Root:=TDemoNode.Create(nil);
  2195. Div1:=TDemoDiv.Create(nil);
  2196. Div1.Name:='Div1';
  2197. Div1.Parent:=Doc.Root;
  2198. Div1.CSSClasses.Add('bird');
  2199. Div1.CSSClasses.Add('eagle');
  2200. Doc.Style:=LinesToStr([
  2201. '.bird.eagle { border-color: blue; }',
  2202. '.bird { border-width: 6px; }',
  2203. '.bird { border: 7px red; }',
  2204. '']);
  2205. ApplyStyle;
  2206. AssertEquals('Div1.BorderColor','blue',Div1.BorderColor);
  2207. AssertEquals('Div1.BorderWidth','7px',Div1.BorderWidth);
  2208. end;
  2209. procedure TTestNewCSSResolver.Test_Specifity_Longhand_All_Longhand;
  2210. var
  2211. Div1: TDemoDiv;
  2212. begin
  2213. Doc.Root:=TDemoNode.Create(nil);
  2214. Div1:=TDemoDiv.Create(nil);
  2215. Div1.Name:='Div1';
  2216. Div1.Parent:=Doc.Root;
  2217. Div1.CSSClasses.Add('bird');
  2218. Div1.CSSClasses.Add('eagle');
  2219. Doc.Style:=LinesToStr([
  2220. '.bird.eagle { border-color: blue; }',
  2221. '.bird { border-width: 7px; }',
  2222. '.bird { all: initial; }',
  2223. '.bird { background: red; }',
  2224. '']);
  2225. ApplyStyle;
  2226. AssertEquals('Div1.BorderColor','blue',Div1.BorderColor);
  2227. AssertEquals('Div1.BorderWidth','',Div1.BorderWidth);
  2228. AssertEquals('Div1.Background','red',Div1.Background);
  2229. end;
  2230. procedure TTestNewCSSResolver.Test_Specifity_Shorthand_All_Shorthand;
  2231. var
  2232. Div1, Div2: TDemoDiv;
  2233. begin
  2234. Doc.Root:=TDemoNode.Create(nil);
  2235. Div1:=TDemoDiv.Create(nil);
  2236. Div1.Name:='Div1';
  2237. Div1.Parent:=Doc.Root;
  2238. Div1.CSSClasses.Add('bird');
  2239. Div2:=TDemoDiv.Create(nil);
  2240. Div2.Name:='Div2';
  2241. Div2.Parent:=Doc.Root;
  2242. Div2.CSSClasses.Add('eagle');
  2243. Doc.Style:=LinesToStr([
  2244. '.bird { border: 7px blue; }',
  2245. '.bird { all: initial; }',
  2246. '.eagle { all: initial; }',
  2247. '.eagle { border: 8px red; }',
  2248. '']);
  2249. ApplyStyle;
  2250. AssertEquals('Div1.BorderColor','',Div1.BorderColor);
  2251. AssertEquals('Div1.BorderWidth','',Div1.BorderWidth);
  2252. AssertEquals('Div2.BorderColor','red',Div2.BorderColor);
  2253. AssertEquals('Div2.BorderWidth','8px',Div2.BorderWidth);
  2254. end;
  2255. procedure TTestNewCSSResolver.Test_Origin_Id_Class;
  2256. var
  2257. Div1: TDemoDiv;
  2258. begin
  2259. Doc.Root:=TDemoNode.Create(nil);
  2260. Div1:=TDemoDiv.Create(nil);
  2261. Div1.Name:='Div1';
  2262. Div1.Parent:=Doc.Root;
  2263. Doc.CSSResolver.AddStyleSheet(cssoUserAgent,'testagent',
  2264. '#Div1 { border-width: 2px;'
  2265. +' border-color: blue !important;'
  2266. +' background: green; }'
  2267. );
  2268. Doc.Style:=LinesToStr([
  2269. 'div { border-width: 3px; ', // although class has lower spec than id, author origin wins
  2270. ' border-color: orange;', // former important always wins
  2271. '}']);
  2272. ApplyStyle;
  2273. AssertEquals('Div1.BorderColor','blue',Div1.BorderColor);
  2274. AssertEquals('Div1.BorderWidth','3px',Div1.BorderWidth);
  2275. AssertEquals('Div1.Background','green',Div1.Background);
  2276. end;
  2277. procedure TTestNewCSSResolver.Test_Var_NoDefault;
  2278. var
  2279. Div1: TDemoDiv;
  2280. begin
  2281. Doc.Root:=TDemoNode.Create(nil);
  2282. Div1:=TDemoDiv.Create(nil);
  2283. Div1.Name:='Div1';
  2284. Div1.Parent:=Doc.Root;
  2285. Doc.Style:=LinesToStr([
  2286. 'div {',
  2287. ' --bird-color: red;',
  2288. '}',
  2289. 'div {',
  2290. ' border-color: var(--bird-color);',
  2291. ' border-width: var(--bird-width);',
  2292. ' color: var(--bird-nothing);',
  2293. '}',
  2294. 'div {',
  2295. ' --bird-width: 3px;',
  2296. '}']);
  2297. ApplyStyle;
  2298. AssertEquals('Div1.BorderColor','red',Div1.BorderColor);
  2299. AssertEquals('Div1.BorderWidth','3px',Div1.BorderWidth);
  2300. AssertEquals('Div1.Color','',Div1.Color);
  2301. end;
  2302. procedure TTestNewCSSResolver.Test_Var_Inline_NoDefault;
  2303. var
  2304. Div1: TDemoDiv;
  2305. begin
  2306. Doc.Root:=TDemoNode.Create(nil);
  2307. Doc.Style:=LinesToStr([
  2308. 'div {',
  2309. ' --bird-color: red;',
  2310. '}']);
  2311. Div1:=TDemoDiv.Create(nil);
  2312. Div1.Name:='Div1';
  2313. Div1.Parent:=Doc.Root;
  2314. Div1.InlineStyle:='--bird-width: 3px; border-color: var(--bird-color); border-width: var(--bird-width);';
  2315. ApplyStyle;
  2316. AssertEquals('Div1.BorderColor','red',Div1.BorderColor);
  2317. AssertEquals('Div1.BorderWidth','3px',Div1.BorderWidth);
  2318. end;
  2319. procedure TTestNewCSSResolver.Test_Var_Defaults;
  2320. var
  2321. Div1: TDemoDiv;
  2322. begin
  2323. Doc.Root:=TDemoNode.Create(nil);
  2324. Div1:=TDemoDiv.Create(nil);
  2325. Div1.Name:='Div1';
  2326. Div1.Parent:=Doc.Root;
  2327. Doc.Style:=LinesToStr([
  2328. 'div {',
  2329. ' --def-color:blue;',
  2330. '}',
  2331. 'div {',
  2332. ' color: var(--bird-color,);',
  2333. ' border-color: var(--bird-border-color,var(--def-color));',
  2334. ' border-width: var(--bird-border-width,3px);',
  2335. '}']);
  2336. ApplyStyle;
  2337. AssertEquals('Div1.BorderColor','blue',Div1.BorderColor);
  2338. AssertEquals('Div1.BorderWidth','3px',Div1.BorderWidth);
  2339. AssertEquals('Div1.Color','',Div1.Color);
  2340. end;
  2341. initialization
  2342. RegisterTests([TTestNewCSSResolver]);
  2343. end.