tccssresolver.pp 78 KB

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