tccssresolver.pp 69 KB

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