tccssresolver.pp 73 KB

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