From ccbec9ee7d6b1c7ccf6faad32b02e606fbcf50f2 Mon Sep 17 00:00:00 2001
From: mattias <nc-gaertnma@netcologne.de>
Date: Thu, 22 Aug 2024 14:03:04 +0200
Subject: [PATCH] fcl-css: resolver: test warnings

---
 packages/fcl-css/src/fpcssresolver.pas  |    1 -
 packages/fcl-css/src/fpcssresparser.pas |  100 +-
 packages/fcl-css/tests/tccssresolver.pp | 2696 ++++++++++++-----------
 3 files changed, 1452 insertions(+), 1345 deletions(-)

diff --git a/packages/fcl-css/src/fpcssresolver.pas b/packages/fcl-css/src/fpcssresolver.pas
index 2f5a2888b7..85d880654b 100644
--- a/packages/fcl-css/src/fpcssresolver.pas
+++ b/packages/fcl-css/src/fpcssresolver.pas
@@ -857,7 +857,6 @@ begin
     ms.Write(Src[1],length(Src)*SizeOf(TCSSChar));
     ms.Position:=0;
     aParser:=TCSSResolverParser.Create(ms); // ss is freed by the parser
-    aParser.Registry:=CSSRegistry;
     aParser.Resolver:=Self;
     aParser.OnLog:=@Log;
     aParser.CSSNthChildParamsClass:=TCSSResolverNthChildParams;
diff --git a/packages/fcl-css/src/fpcssresparser.pas b/packages/fcl-css/src/fpcssresparser.pas
index 4987f77f42..86da1afbbf 100644
--- a/packages/fcl-css/src/fpcssresparser.pas
+++ b/packages/fcl-css/src/fpcssresparser.pas
@@ -407,7 +407,7 @@ type
     function IndexOfPseudoClassName(const aName: TCSSString): TCSSNumericalID; overload;
     property PseudoClassCount: TCSSNumericalID read FPseudoClassCount;
   public
-    // pseudo functions
+    // pseudo functions lowercase (they are parsed case insensitive)
     PseudoFunctions: TCSSStringArray;
     function AddPseudoFunction(const aName: TCSSString): TCSSNumericalID; overload;
     function IndexOfPseudoFunction(const aName: TCSSString): TCSSNumericalID; overload;
@@ -564,6 +564,13 @@ type
     procedure SkipToEndOfAttribute(var p: PCSSChar);
     function SkipString(var p: PCSSChar): boolean;
     function SkipBrackets(var p: PCSSChar; Lvl: integer = 1): boolean;
+    // registry
+    function GetAttributeID(const aName: TCSSString): TCSSNumericalID; virtual;
+    function GetAttributeDesc(AttrID: TCSSNumericalID): TCSSAttributeDesc; virtual;
+    function GetTypeID(const aName: TCSSString): TCSSNumericalID; virtual;
+    function GetPseudoClassID(const aName: TCSSString): TCSSNumericalID; virtual;
+    function GetPseudoFunctionID(const aName: TCSSString): TCSSNumericalID; virtual;
+
     property CSSRegistry: TCSSRegistry read FCSSRegistry write SetCSSRegistry;
   end;
 
@@ -574,10 +581,10 @@ type
   TCSSResolverParser = class(TCSSParser)
   private
     FOnLog: TCSSValueParserLogEvent;
-    FRegistry: TCSSRegistry;
     FResolver: TCSSBaseResolver;
   protected
-    function ResolveIdentifier(El: TCSSResolvedIdentifierElement; Kind: TCSSNumericalIDKind): TCSSNumericalID; virtual;
+    function ResolveAttribute(El: TCSSResolvedIdentifierElement): TCSSNumericalID; virtual;
+    function ResolveType(El: TCSSResolvedIdentifierElement): TCSSNumericalID; virtual;
     function ResolvePseudoClass(El: TCSSResolvedPseudoClassElement): TCSSNumericalID; virtual;
     function ResolvePseudoFunction(El: TCSSResolvedCallElement): TCSSNumericalID; virtual;
     function ParseCall(aName: TCSSString; IsSelector: boolean): TCSSCallElement; override;
@@ -597,7 +604,6 @@ type
     destructor Destroy; override;
     procedure Log(MsgType: TEventType; const ID: TCSSMsgID; const Msg: TCSSString; PosEl: TCSSElement); virtual;
     class function IsWhiteSpace(const s: TCSSString): boolean; virtual; overload;
-    property Registry: TCSSRegistry read FRegistry write FRegistry;
     property Resolver: TCSSBaseResolver read FResolver write FResolver;
     property OnLog: TCSSValueParserLogEvent read FOnLog write FOnLog;
   end;
@@ -1175,6 +1181,8 @@ begin
     raise ECSSParser.Create('missing name');
   if length(aName)>255 then
     raise ECSSParser.Create('pseudo function name too long');
+  if aName<>LowerCase(aName) then
+    raise ECSSParser.Create('pseudo function name not lowercase');
   Result:=IndexOfKeyword(aName);
   if Result>0 then
     raise ECSSParser.Create('duplicate pseudo function "'+aName+'"');
@@ -2044,30 +2052,67 @@ begin
   until false;
 end;
 
+function TCSSBaseResolver.GetAttributeID(const aName: TCSSString): TCSSNumericalID;
+begin
+  Result:=CSSRegistry.IndexOfAttributeName(aName);
+end;
+
+function TCSSBaseResolver.GetAttributeDesc(AttrID: TCSSNumericalID): TCSSAttributeDesc;
+begin
+  if (AttrID>0) and (AttrID<CSSRegistry.AttributeCount) then
+    Result:=CSSRegistry.Attributes[AttrID]
+  else
+    Result:=nil;
+end;
+
+function TCSSBaseResolver.GetTypeID(const aName: TCSSString): TCSSNumericalID;
+begin
+  Result:=CSSRegistry.IndexOfTypeName(aName);
+end;
+
+function TCSSBaseResolver.GetPseudoClassID(const aName: TCSSString): TCSSNumericalID;
+begin
+  Result:=CSSRegistry.IndexOfPseudoClassName(aName);
+end;
+
+function TCSSBaseResolver.GetPseudoFunctionID(const aName: TCSSString): TCSSNumericalID;
+begin
+  Result:=CSSRegistry.IndexOfPseudoFunction(aName);
+end;
+
 { TCSSResolverParser }
 
-function TCSSResolverParser.ResolveIdentifier(El: TCSSResolvedIdentifierElement;
-  Kind: TCSSNumericalIDKind): TCSSNumericalID;
+function TCSSResolverParser.ResolveAttribute(El: TCSSResolvedIdentifierElement): TCSSNumericalID;
 var
   aName: TCSSString;
 begin
   if El.NumericalID<>CSSIDNone then
     raise Exception.Create('20240701143234');
   aName:=El.Name;
-  if Kind=nikPseudoClass then
-  begin
-    // pseudo classes are ASCII case insensitive
-    System.Delete(aName,1,1);
-    aName:=lowercase(aName);
-  end;
-
-  El.Kind:=Kind;
-  Result:=Registry.IndexOfNamedItem(Kind,aName);
-  //writeln('TCSSResolverParser.ResolveIdentifier ',aName,' ID=',Result);
-  if Result=CSSIDNone then
+  El.Kind:=nikAttribute;
+  Result:=Resolver.GetAttributeID(aName);
+  writeln('AAA1 TCSSResolverParser.ResolveAttribute ',aName,' ',Result);
+  if Result<=CSSIDNone then
   begin
     El.NumericalID:=-1;
-    Log(etWarning,20240625130648,'unknown '+CSSNumericalIDKindNames[Kind]+' "'+aName+'"',El);
+    Log(etWarning,20240625130648,'unknown attribute "'+aName+'"',El);
+  end else
+    El.NumericalID:=Result;
+end;
+
+function TCSSResolverParser.ResolveType(El: TCSSResolvedIdentifierElement): TCSSNumericalID;
+var
+  aName: TCSSString;
+begin
+  if El.NumericalID<>CSSIDNone then
+    raise Exception.Create('20240822133813');
+  aName:=El.Name;
+  El.Kind:=nikType;
+  Result:=Resolver.GetTypeID(aName);
+  if Result<=CSSIDNone then
+  begin
+    El.NumericalID:=-1;
+    Log(etWarning,20240822133816,'unknown type "'+aName+'"',El);
   end else
     El.NumericalID:=Result;
 end;
@@ -2086,7 +2131,7 @@ begin
     raise Exception.Create('20240701143234');
 
   El.Kind:=nikPseudoClass;
-  Result:=Registry.IndexOfNamedItem(nikPseudoClass,aName);
+  Result:=Resolver.GetPseudoClassID(aName);
   //writeln('TCSSResolverParser.ResolvePseudoClass ',aName,' ID=',Result);
   if Result<=CSSIDNone then
   begin
@@ -2112,12 +2157,12 @@ begin
   aName:=lowercase(aName);
 
   El.Kind:=nikPseudoFunction;
-  Result:=Registry.IndexOfNamedItem(nikPseudoFunction,aName);
+  Result:=Resolver.GetPseudoFunctionID(aName);
   //writeln('TCSSResolverParser.ResolvePseudoFunction ',aName,' ID=',Result);
-  if Result=CSSIDNone then
+  if Result<=CSSIDNone then
   begin
     El.NameNumericalID:=-1;
-    Log(etWarning,20240625130648,'unknown pseudo class "'+aName+'"',El);
+    Log(etWarning,20240625130648,'unknown pseudo function "'+aName+'"',El);
   end else
     El.NameNumericalID:=Result;
 end;
@@ -2165,8 +2210,7 @@ begin
   aKey:=Result.Keys[0];
   if aKey is TCSSResolvedIdentifierElement then
   begin
-    // todo: custom attributes
-    AttrId:=ResolveIdentifier(TCSSResolvedIdentifierElement(aKey),nikAttribute);
+    AttrId:=ResolveAttribute(TCSSResolvedIdentifierElement(aKey));
 
     if aKey.CustomData<>nil then
       raise Exception.Create('20240626113536');
@@ -2188,7 +2232,7 @@ begin
 
     if AttrId>=CSSAttributeID_All then
     begin
-      Desc:=Registry.Attributes[AttrId];
+      Desc:=Resolver.GetAttributeDesc(AttrId);
 
       if Pos('var(',AttrData.Value)>0 then
       begin
@@ -2221,7 +2265,7 @@ begin
   C:=El.ClassType;
   if C=TCSSResolvedIdentifierElement then
     // e.g. div {}
-    ResolveIdentifier(TCSSResolvedIdentifierElement(El),nikType)
+    ResolveType(TCSSResolvedIdentifierElement(El))
   else if C=TCSSHashIdentifierElement then
     // e.g. #id {}
   else if C=TCSSClassNameElement then
@@ -2293,7 +2337,7 @@ begin
   if C=TCSSResolvedIdentifierElement then
   begin
     // [name]  ->  has explicit attribute
-    ResolveIdentifier(TCSSResolvedIdentifierElement(El),nikAttribute);
+    ResolveAttribute(TCSSResolvedIdentifierElement(El));
   end else if C=TCSSBinaryElement then
     CheckSelectorArrayBinary(TCSSBinaryElement(El))
   else begin
@@ -2313,7 +2357,7 @@ begin
     Log(etWarning,20240625154314,'Invalid CSS array selector, expected attribute',Left);
     exit;
   end;
-  ResolveIdentifier(TCSSResolvedIdentifierElement(Left),nikAttribute);
+  ResolveAttribute(TCSSResolvedIdentifierElement(Left));
 
   Right:=aBinary.Right;
   C:=Right.ClassType;
diff --git a/packages/fcl-css/tests/tccssresolver.pp b/packages/fcl-css/tests/tccssresolver.pp
index 235af48399..f52848268f 100644
--- a/packages/fcl-css/tests/tccssresolver.pp
+++ b/packages/fcl-css/tests/tccssresolver.pp
@@ -322,6 +322,7 @@ type
   private
     FCSSResolver: TCSSResolver;
     FStyle: TCSSString;
+    procedure OnResolverLog(Sender: TObject; Entry: TCSSResolverLogEntry);
   protected
     procedure ApplyTypeStyles; virtual;
     procedure SetStyle(const AValue: TCSSString); virtual;
@@ -344,6 +345,8 @@ type
   protected
     procedure SetUp; override;
     procedure TearDown; override;
+    procedure ApplyStyle; virtual;
+    procedure CheckWarnings; virtual;
   public
     property Doc: TDemoDocument read FDoc;
   end;
@@ -421,6 +424,8 @@ type
     procedure Test_Origin_Id_Class;
 
     // var()
+    procedure Test_Var;
+    // todo: Test_Var_Inline; // var() in inline, custom attr in inline
 
     // skipping for forward compatibility
     // ToDo: invalid token in selector makes selector invalid
@@ -459,1322 +464,6 @@ begin
   Result:=s;
 end;
 
-{ TCustomTestNewCSSResolver }
-
-procedure TCustomTestNewCSSResolver.SetUp;
-var
-  AttrDesc: TCSSAttributeDesc;
-begin
-  inherited SetUp;
-
-  TDemoNode.CSSRegistry:=TDemoCSSRegistry.Create();
-
-  // register button attribute 'caption'
-  AttrDesc:=TDemoNode.CSSRegistry.AddAttribute('caption');
-  TDemoButton.CSSCaptionID:=AttrDesc.Index;
-
-  FDoc:=TDemoDocument.Create(nil);
-end;
-
-procedure TCustomTestNewCSSResolver.TearDown;
-begin
-  FreeAndNil(FDoc);
-  FreeAndNil(TDemoNode.CSSRegistry);
-  inherited TearDown;
-end;
-
-{ TTestNewCSSResolver }
-
-procedure TTestNewCSSResolver.Test_ParseAttr_Keyword;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Style:='* { direction: ltr; }';
-  Doc.ApplyStyle;
-  AssertEquals('Root.direction','ltr',Doc.Root.Direction);
-end;
-
-procedure TTestNewCSSResolver.Test_ParseAttr_Keyword_SkipInvalid;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Style:='* { direction: something ltr; }';
-  Doc.ApplyStyle;
-  AssertEquals('Root.direction','ltr',Doc.Root.Direction);
-end;
-
-procedure TTestNewCSSResolver.Test_ParseAttr_Float;
-var
-  Div1: TDemoDiv;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Style:=
-     ':root {'
-    +'  left: 10px;'
-    +'  top: .1px;'
-    +'  width: 3e2em;'
-    +'  height: 3e-2px;'
-    +'}'
-    +'div {'
-    +'  left: -4mm;'
-    +'  top: -.5pc;'
-    +'  width: .6cm;'
-    +'  height: 6E+1rem;'
-    +'}';
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Name:='Div1';
-  Div1.Parent:=Doc.Root;
-
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','10px',Doc.Root.Left);
-  AssertEquals('Root.Top','0.1px',Doc.Root.Top);
-  AssertEquals('Root.Width','300em',Doc.Root.Width);
-  AssertEquals('Root.Height','0.03px',Doc.Root.Height);
-  AssertEquals('Div1.Left','-4mm',Div1.Left);
-  AssertEquals('Div1.Top','-0.5pc',Div1.Top);
-  AssertEquals('Div1.Width','0.6cm',Div1.Width);
-  AssertEquals('Div1.Height','60rem',Div1.Height);
-end;
-
-procedure TTestNewCSSResolver.Test_ParseAttr_Float_SkipInvalid;
-begin
-  exit;
-
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Style:=
-     ':root {'
-    +'  left: something 10px;'
-    +'  top: 1 px;' // no space between number
-    +'  width: 0 px;' // the px is ignored because of the space, 0 without unit is allowed
-    +'  height: -4cm;' // no negative
-    +'}';
-
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','10px',Doc.Root.Left);
-  AssertEquals('Root.Top','invalid',Doc.Root.Top);
-  AssertEquals('Root.Width','0',Doc.Root.Width);
-  AssertEquals('Root.Height','invalid',Doc.Root.Height);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_Universal;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Style:='* { left: 10px; }';
-  Doc.ApplyStyle;
-  AssertEquals('Root.left','10px',Doc.Root.Left);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_Type;
-var
-  Button: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Button:=TDemoButton.Create(nil);
-  Button.Parent:=Doc.Root;
-  Doc.Style:='button { left: 11px; }';
-  Doc.ApplyStyle;
-  AssertEquals('Root.left','',Doc.Root.Left);
-  AssertEquals('Button.left','11px',Button.Left);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_Type_Spaces;
-var
-  Button1, Button2: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Button1:=TDemoButton.Create(nil);
-  Button1.Parent:=Doc.Root;
-
-  Button2:=TDemoButton.Create(nil);
-  Button2.Parent:=Doc.Root;
-
-  Doc.Style:='div, button ,span { left: 11px; }';
-  Doc.ApplyStyle;
-  AssertEquals('Root.left','',Doc.Root.Left);
-  AssertEquals('Button1.left','11px',Button1.Left);
-  AssertEquals('Button2.left','11px',Button2.Left);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_Id;
-var
-  Button1: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Button1:=TDemoButton.Create(nil);
-  Button1.Name:='Button1';
-  Button1.Parent:=Doc.Root;
-  Doc.Style:='#Button1 { left: 12px; }';
-  Doc.ApplyStyle;
-  AssertEquals('Root.left','',Doc.Root.Left);
-  AssertEquals('Button1.left','12px',Button1.Left);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_Class;
-var
-  Button1: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Button1:=TDemoButton.Create(nil);
-  Button1.CSSClasses.Add('west');
-  Button1.Parent:=Doc.Root;
-  Doc.Style:='.west { left: 13px; }';
-  Doc.ApplyStyle;
-  AssertEquals('Root.left','',Doc.Root.Left);
-  AssertEquals('Button1.left','13px',Button1.Left);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_ClassClass;
-var
-  Button1, Button2: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Button1:=TDemoButton.Create(nil);
-  Button1.CSSClasses.Add('west');
-  Button1.Parent:=Doc.Root;
-
-  Button2:=TDemoButton.Create(nil);
-  Button2.CSSClasses.DelimitedText:='west south';
-  AssertEquals('Button2.CSSClasses.Count',2,Button2.CSSClasses.Count);
-  Button2.Parent:=Doc.Root;
-
-  Doc.Style:='.west.south { left: 10px; }';
-  Doc.ApplyStyle;
-  AssertEquals('Root.left','',Doc.Root.Left);
-  AssertEquals('Button1.left','',Button1.Left);
-  AssertEquals('Button2.left','10px',Button2.Left);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_ClassSpaceClass;
-var
-  Button1: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Root.CSSClasses.Add('bird');
-
-  Button1:=TDemoButton.Create(nil);
-  Button1.CSSClasses.Add('west');
-  Button1.Parent:=Doc.Root;
-
-  Doc.Style:='.bird .west { left: 10px; }';
-  Doc.ApplyStyle;
-  AssertEquals('Root.left','',Doc.Root.Left);
-  AssertEquals('Button1.left','10px',Button1.Left);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_TypeCommaType;
-var
-  Button1: TDemoButton;
-  Div1: TDemoDiv;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Button1:=TDemoButton.Create(nil);
-  Button1.Parent:=Doc.Root;
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Parent:=Doc.Root;
-
-  Doc.Style:='div, button { left: 10px; }';
-  Doc.ApplyStyle;
-  AssertEquals('Root.left','',Doc.Root.Left);
-  AssertEquals('Button1.left','10px',Button1.Left);
-  AssertEquals('Div1.left','10px',Div1.Left);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_ClassGTClass;
-var
-  Div1, Div2: TDemoDiv;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Root.Name:='root';
-  Doc.Root.CSSClasses.Add('lvl1');
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Name:='Div1';
-  Div1.CSSClasses.Add('lvl2');
-  Div1.Parent:=Doc.Root;
-
-  Div2:=TDemoDiv.Create(nil);
-  Div2.Name:='Div2';
-  Div2.CSSClasses.Add('lvl3');
-  Div2.Parent:=Div1;
-
-  Doc.Style:=LinesToStr([
-  '.lvl1>.lvl2 { left: 10px; }', // set
-  '.lvl1>.lvl3 { top: 11px; }', // not set, not direct children
-  '.lvl2>.lvl3 { width: 12px; }', // set
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.left','',Doc.Root.Left);
-  AssertEquals('Root.top','',Doc.Root.Top);
-  AssertEquals('Root.width','',Doc.Root.Width);
-  AssertEquals('Div1.left','10px',Div1.Left);
-  AssertEquals('Div1.top','',Div1.Top);
-  AssertEquals('Div1.width','',Div1.Width);
-  AssertEquals('Div2.left','',Div2.Left);
-  AssertEquals('Div2.top','',Div2.Top);
-  AssertEquals('Div2.width','12px',Div2.Width);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_TypePlusType;
-var
-  Button1, Button2, Button3: TDemoButton;
-  Div1: TDemoDiv;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Root.Name:='root';
-
-  Button1:=TDemoButton.Create(nil);
-  Button1.Name:='Button1';
-  Button1.Parent:=Doc.Root;
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Name:='Div1';
-  Div1.Parent:=Doc.Root;
-
-  Button2:=TDemoButton.Create(nil);
-  Button2.Name:='Button2';
-  Button2.Parent:=Doc.Root;
-
-  Button3:=TDemoButton.Create(nil);
-  Button3.Name:='Button3';
-  Button3.Parent:=Doc.Root;
-
-  Doc.Style:='div+button { left: 10px; }'; // only Button2 has a prev sibling div
-  Doc.ApplyStyle;
-  AssertEquals('Root.left','',Doc.Root.Left);
-  AssertEquals('Button1.left','',Button1.Left);
-  AssertEquals('Div1.left','',Div1.Left);
-  AssertEquals('Button2.left','10px',Button2.Left);
-  AssertEquals('Button3.left','',Button3.Left);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_TypeTildeType;
-var
-  Button1, Button2, Button3: TDemoButton;
-  Div1: TDemoDiv;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Button1:=TDemoButton.Create(nil);
-  Button1.Parent:=Doc.Root;
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Parent:=Doc.Root;
-
-  Button2:=TDemoButton.Create(nil);
-  Button2.Parent:=Doc.Root;
-
-  Button3:=TDemoButton.Create(nil);
-  Button3.Parent:=Doc.Root;
-
-  Doc.Style:='div~button { left: 10px; }';
-  Doc.ApplyStyle;
-  AssertEquals('Root.left','',Doc.Root.Left);
-  AssertEquals('Button1.left','',Button1.Left);
-  AssertEquals('Div1.left','',Div1.Left);
-  AssertEquals('Button2.left','10px',Button2.Left);
-  AssertEquals('Button3.left','10px',Button3.Left);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_HasAttribute;
-var
-  Button1: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Root.Name:='root';
-  Doc.Root.ExplicitAttributes[naLeft]:='100px';
-
-  Button1:=TDemoButton.Create(nil);
-  Button1.Name:='Button1';
-  Button1.Parent:=Doc.Root;
-  Button1.ExplicitAttributes[naLeft]:='2px';
-  Button1.ExplicitCaption:='Click Button1';
-
-  Doc.Style:=LinesToStr([
-  '[left] { top: 3px; }',
-  '[caption] { width: 4px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Top','3px',Doc.Root.Top);
-  AssertEquals('Root.Width','',Doc.Root.Width);
-  AssertEquals('Button1.Top','3px',Button1.Top);
-  AssertEquals('Button1.Width','4px',Button1.Width);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_AttributeEquals;
-var
-  Button1: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Root.ExplicitAttributes[naLeft]:='2px';
-
-  Button1:=TDemoButton.Create(nil);
-  Button1.Parent:=Doc.Root;
-  Button1.ExplicitAttributes[naLeft]:='3px';
-  Button1.ExplicitAttributes[naColor]:='maybe black';
-
-  Doc.Style:=LinesToStr([
-  '[left=2px] { top: 4px; }',
-  '[color="maybe black"] { width: 5px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Top','4px',Doc.Root.Top);
-  AssertEquals('Button1.Top','',Button1.Top);
-  AssertEquals('Button1.Width','5px',Button1.Width);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_AttributeEqualsI;
-var
-  Button1: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Root.ExplicitAttributes[naLeft]:='2px';
-
-  Button1:=TDemoButton.Create(nil);
-  Button1.Parent:=Doc.Root;
-  Button1.ExplicitAttributes[naLeft]:='3px';
-  Button1.ExplicitAttributes[naColor]:='maybe Black';
-
-  Doc.Style:=LinesToStr([
-  '[left="2Px" i] { top: 4px; }',
-  '[color="Maybe bLack" i] { width: 5px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Top','4px',Doc.Root.Top);
-  AssertEquals('Button1.Top','',Button1.Top);
-  AssertEquals('Button1.Width','5px',Button1.Width);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_AttributeBeginsWith;
-var
-  Button1: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Root.ExplicitAttributes[naLeft]:='Foo';
-
-  Button1:=TDemoButton.Create(nil);
-  Button1.Parent:=Doc.Root;
-  Button1.ExplicitAttributes[naLeft]:='Foo Bar';
-
-  Doc.Style:=LinesToStr([
-  '[left^=Fo] { top: 4px; }',
-  '[left^="Foo B"] { width: 5px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Top','4px',Doc.Root.Top);
-  AssertEquals('Root.Width','',Doc.Root.Width);
-  AssertEquals('Button1.Top','4px',Button1.Top);
-  AssertEquals('Button1.Width','5px',Button1.Width);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_AttributeEndsWith;
-var
-  Button1: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Root.ExplicitAttributes[naLeft]:='Foo';
-
-  Button1:=TDemoButton.Create(nil);
-  Button1.Parent:=Doc.Root;
-  Button1.ExplicitAttributes[naLeft]:='Foo Bar';
-
-  Doc.Style:=LinesToStr([
-  '[left$=o] { top: 4px; }',
-  '[left$="o Bar"] { width: 5px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Top','4px',Doc.Root.Top);
-  AssertEquals('Root.Width','',Doc.Root.Width);
-  AssertEquals('Button1.Top','',Button1.Top);
-  AssertEquals('Button1.Width','5px',Button1.Width);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_AttributeBeginsWithHyphen;
-var
-  Button1: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Root.ExplicitAttributes[naLeft]:='Foo';
-
-  Button1:=TDemoButton.Create(nil);
-  Button1.Parent:=Doc.Root;
-  Button1.ExplicitAttributes[naLeft]:='Foo-Bar';
-
-  Doc.Style:=LinesToStr([
-  '[left|=Foo] { top: 4px; }',
-  '[left|="Fo"] { width: 5px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Top','4px',Doc.Root.Top);
-  AssertEquals('Root.Width','',Doc.Root.Width);
-  AssertEquals('Button1.Top','4px',Button1.Top);
-  AssertEquals('Button1.Width','',Button1.Width);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_AttributeContainsWord;
-var
-  Button1: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Root.Name:='root';
-  Doc.Root.ExplicitAttributes[naLeft]:='One Two Three';
-
-  Button1:=TDemoButton.Create(nil);
-  Button1.Name:='Button1';
-  Button1.Parent:=Doc.Root;
-  Button1.ExplicitAttributes[naLeft]:='Four Five';
-
-  Doc.Style:=LinesToStr([
-  '[left~=One] { top: 4px; }',
-  '[left~=Two] { width: 5px; }',
-  '[left~=Three] { height: 6px; }',
-  '[left~="Four Five"] { color: #123; }',  // not one word, so does not match!
-  '[left~=our] { display: none; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Top','4px',Doc.Root.Top);
-  AssertEquals('Root.Width','5px',Doc.Root.Width);
-  AssertEquals('Root.Height','6px',Doc.Root.Height);
-  AssertEquals('Root.Color','',Doc.Root.Color);
-  AssertEquals('Root.Display','',Doc.Root.Display);
-  AssertEquals('Button1.Top','',Button1.Top);
-  AssertEquals('Button1.Width','',Button1.Width);
-  AssertEquals('Button1.Height','',Button1.Height);
-  AssertEquals('Button1.Color','',Button1.Color);
-  AssertEquals('Button1.Display','inline-block',Button1.Display);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_AttributeContainsSubstring;
-var
-  Button1: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Root.ExplicitAttributes[naLeft]:='Foo';
-
-  Button1:=TDemoButton.Create(nil);
-  Button1.Parent:=Doc.Root;
-  Button1.ExplicitAttributes[naLeft]:='Foo Bar';
-
-  Doc.Style:=LinesToStr([
-  '[left*=oo] { top: 4px; }',
-  '[left*="o B"] { width: 5px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Top','4px',Doc.Root.Top);
-  AssertEquals('Root.Width','',Doc.Root.Width);
-  AssertEquals('Button1.Top','4px',Button1.Top);
-  AssertEquals('Button1.Width','5px',Button1.Width);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_Root;
-var
-  Button1: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Root.ExplicitAttributes[naLeft]:='Foo';
-
-  Button1:=TDemoButton.Create(nil);
-  Button1.Parent:=Doc.Root;
-
-  Doc.Style:=LinesToStr([
-  ':roOt { top: 4px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Top','4px',Doc.Root.Top);
-  AssertEquals('Button1.Top','',Button1.Top);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_Empty;
-var
-  Div1, Div11, Div2: TDemoDiv;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Parent:=Doc.Root;
-
-  Div11:=TDemoDiv.Create(nil);
-  Div11.Parent:=Div1;
-
-  Div2:=TDemoDiv.Create(nil);
-  Div2.Parent:=Doc.Root;
-
-  Doc.Style:=LinesToStr([
-  ':eMpty { left: 1px; }',
-  'div:emPty { top: 2px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','',Doc.Root.Left);
-  AssertEquals('Root.Top','',Doc.Root.Top);
-  AssertEquals('Div1.Left','',Div1.Left);
-  AssertEquals('Div1.Top','',Div1.Top);
-  AssertEquals('Div11.Left','1px',Div11.Left);
-  AssertEquals('Div11.Top','2px',Div11.Top);
-  AssertEquals('Div2.Left','1px',Div2.Left);
-  AssertEquals('Div2.Top','2px',Div2.Top);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_FirstChild;
-var
-  Div1, Div11, Div12, Div2: TDemoDiv;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Parent:=Doc.Root;
-
-  Div11:=TDemoDiv.Create(nil);
-  Div11.Parent:=Div1;
-
-  Div12:=TDemoDiv.Create(nil);
-  Div12.Parent:=Div1;
-
-  Div2:=TDemoDiv.Create(nil);
-  Div2.Parent:=Doc.Root;
-
-  Doc.Style:=LinesToStr([
-  ':first-child { left: 1px; }',
-  'div:first-child { top: 2px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','1px',Doc.Root.Left);
-  AssertEquals('Root.Top','',Doc.Root.Top);
-  AssertEquals('Div1.Left','1px',Div1.Left);
-  AssertEquals('Div1.Top','2px',Div1.Top);
-  AssertEquals('Div11.Left','1px',Div11.Left);
-  AssertEquals('Div11.Top','2px',Div11.Top);
-  AssertEquals('Div12.Left','',Div12.Left);
-  AssertEquals('Div12.Top','',Div12.Top);
-  AssertEquals('Div2.Left','',Div2.Left);
-  AssertEquals('Div2.Top','',Div2.Top);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_LastChild;
-var
-  Div1, Div11, Div2: TDemoDiv;
-  Button12: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Parent:=Doc.Root;
-
-  Div11:=TDemoDiv.Create(nil);
-  Div11.Parent:=Div1;
-
-  Button12:=TDemoButton.Create(nil);
-  Button12.Parent:=Div1;
-
-  Div2:=TDemoDiv.Create(nil);
-  Div2.Parent:=Doc.Root;
-
-  Doc.Style:=LinesToStr([
-  ':last-child { left: 6px; }',
-  'div:last-child { top: 7px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','6px',Doc.Root.Left);
-  AssertEquals('Root.Top','',Doc.Root.Top);
-  AssertEquals('Div1.Left','',Div1.Left);
-  AssertEquals('Div1.Top','',Div1.Top);
-  AssertEquals('Div11.Left','',Div11.Left);
-  AssertEquals('Div11.Top','',Div11.Top);
-  AssertEquals('Button12.Left','6px',Button12.Left);
-  AssertEquals('Button12.Top','',Button12.Top);
-  AssertEquals('Div2.Left','6px',Div2.Left);
-  AssertEquals('Div2.Top','7px',Div2.Top);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_OnlyChild;
-var
-  Div1, Div11, Div2: TDemoDiv;
-  Button12: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Root.Name:='root';
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Name:='Div1';
-  Div1.Parent:=Doc.Root;
-
-  Div11:=TDemoDiv.Create(nil);
-  Div11.Name:='Div11';
-  Div11.Parent:=Div1;
-
-  Div2:=TDemoDiv.Create(nil);
-  Div2.Name:='Div2';
-  Div2.Parent:=Doc.Root;
-
-  Button12:=TDemoButton.Create(nil);
-  Button12.Name:='Button12';
-  Button12.Parent:=Div2;
-
-  Doc.Style:=LinesToStr([
-  ':only-child { left: 8px; }',
-  'div:only-child { top: 9px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','8px',Doc.Root.Left);
-  AssertEquals('Root.Top','',Doc.Root.Top);
-  AssertEquals('Div1.Left','',Div1.Left);
-  AssertEquals('Div1.Top','',Div1.Top);
-  AssertEquals('Div11.Left','8px',Div11.Left);
-  AssertEquals('Div11.Top','9px',Div11.Top);
-  AssertEquals('Div2.Left','',Div2.Left);
-  AssertEquals('Div2.Top','',Div2.Top);
-  AssertEquals('Button12.Left','8px',Button12.Left);
-  AssertEquals('Button12.Top','',Button12.Top);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_Not;
-var
-  Div1, Div11, Div2: TDemoDiv;
-  Button12: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Root.Name:='root';
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Name:='Div1';
-  Div1.Parent:=Doc.Root;
-
-  Div11:=TDemoDiv.Create(nil);
-  Div11.Name:='Div11';
-  Div11.Parent:=Div1;
-
-  Div2:=TDemoDiv.Create(nil);
-  Div2.Name:='Div2';
-  Div2.Parent:=Doc.Root;
-
-  Button12:=TDemoButton.Create(nil);
-  Button12.Name:='Button12';
-  Button12.Parent:=Div2;
-
-  Doc.Style:=LinesToStr([
-  ':not(:only-child) { left: 8px; }',
-  ':not(div:only-child) { top: 9px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','',Doc.Root.Left);
-  AssertEquals('Root.Top','9px',Doc.Root.Top);
-  AssertEquals('Div1.Left','8px',Div1.Left);
-  AssertEquals('Div1.Top','9px',Div1.Top);
-  AssertEquals('Div11.Left','',Div11.Left);
-  AssertEquals('Div11.Top','',Div11.Top);
-  AssertEquals('Div2.Left','8px',Div2.Left);
-  AssertEquals('Div2.Top','9px',Div2.Top);
-  AssertEquals('Button12.Left','',Button12.Left);
-  AssertEquals('Button12.Top','9px',Button12.Top);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_NthChild;
-var
-  Div1, Div2, Div3, Div4: TDemoDiv;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Parent:=Doc.Root;
-
-  Div2:=TDemoDiv.Create(nil);
-  Div2.Parent:=Doc.Root;
-
-  Div3:=TDemoDiv.Create(nil);
-  Div3.Parent:=Doc.Root;
-
-  Div4:=TDemoDiv.Create(nil);
-  Div4.Parent:=Doc.Root;
-
-  Doc.Style:=LinesToStr([
-  'div:nth-child(2n+1) { left: 8px; }',
-  'div:nth-child(n+3) { border-width: 6px; }',
-  'div:nth-child(-n+2) { height: 7em; }',
-  'div:nth-child(even) { top: 3px; }',
-  'div:nth-child(odd) { width: 4px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','',Doc.Root.Left);
-  AssertEquals('Root.BorderWidth','',Doc.Root.BorderWidth);
-  AssertEquals('Root.Height','',Doc.Root.Height);
-  AssertEquals('Root.Top','',Doc.Root.Top);
-  AssertEquals('Root.Width','',Doc.Root.Width);
-  AssertEquals('Div1.Left','8px',Div1.Left);
-  AssertEquals('Div1.BorderWidth','',Div1.BorderWidth);
-  AssertEquals('Div1.Height','7em',Div1.Height);
-  AssertEquals('Div1.Top','',Div1.Top);
-  AssertEquals('Div1.Width','4px',Div1.Width);
-  AssertEquals('Div2.Left','',Div2.Left);
-  AssertEquals('Div2.BorderWidth','',Div2.BorderWidth);
-  AssertEquals('Div2.Height','7em',Div2.Height);
-  AssertEquals('Div2.Top','3px',Div2.Top);
-  AssertEquals('Div2.Width','',Div2.Width);
-  AssertEquals('Div3.Left','8px',Div3.Left);
-  AssertEquals('Div3.BorderWidth','6px',Div3.BorderWidth);
-  AssertEquals('Div3.Height','',Div3.Height);
-  AssertEquals('Div3.Top','',Div3.Top);
-  AssertEquals('Div3.Width','4px',Div3.Width);
-  AssertEquals('Div4.Left','',Div4.Left);
-  AssertEquals('Div4.BorderWidth','6px',Div4.BorderWidth);
-  AssertEquals('Div4.Height','',Div4.Height);
-  AssertEquals('Div4.Top','3px',Div4.Top);
-  AssertEquals('Div4.Width','',Div4.Width);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_NthLastChild;
-var
-  Div1, Div2, Div3, Div4: TDemoDiv;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Parent:=Doc.Root;
-
-  Div2:=TDemoDiv.Create(nil);
-  Div2.Parent:=Doc.Root;
-
-  Div3:=TDemoDiv.Create(nil);
-  Div3.Parent:=Doc.Root;
-
-  Div4:=TDemoDiv.Create(nil);
-  Div4.Parent:=Doc.Root;
-
-  Doc.Style:=LinesToStr([
-  ':nth-last-child(2n+1) { left: 8px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','',Doc.Root.Left);
-  AssertEquals('Div1.Left','',Div1.Left);
-  AssertEquals('Div2.Left','8px',Div2.Left);
-  AssertEquals('Div3.Left','',Div3.Left);
-  AssertEquals('Div4.Left','8px',Div4.Left);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_NthChildOf;
-var
-  Div1, Div2, Div3, Div4: TDemoDiv;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Root.Name:='root';
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Name:='Div1';
-  Div1.Parent:=Doc.Root;
-
-  Div2:=TDemoDiv.Create(nil);
-  Div2.Name:='Div2';
-  Div2.Parent:=Doc.Root;
-  Div2.ExplicitAttributes[naTop]:='3px';
-
-  Div3:=TDemoDiv.Create(nil);
-  Div3.Name:='Div3';
-  Div3.Parent:=Doc.Root;
-  Div3.ExplicitAttributes[naTop]:='3px';
-
-  Div4:=TDemoDiv.Create(nil);
-  Div4.Name:='Div4';
-  Div4.Parent:=Doc.Root;
-  Div4.ExplicitAttributes[naTop]:='3px';
-
-  Doc.Style:=LinesToStr([
-  ':nth-child(2n+1 of [top=3px]) { left: 5px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','',Doc.Root.Left);
-  AssertEquals('Div1.Left','',Div1.Left);
-  AssertEquals('Div2.Left','5px',Div2.Left);
-  AssertEquals('Div3.Left','',Div3.Left);
-  AssertEquals('Div4.Left','5px',Div4.Left);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_FirstOfType;
-var
-  Div1, Div11, Div13, Div2: TDemoDiv;
-  Button12: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Parent:=Doc.Root;
-
-  Div11:=TDemoDiv.Create(nil);
-  Div11.Parent:=Div1;
-
-  Button12:=TDemoButton.Create(nil);
-  Button12.Parent:=Div1;
-
-  Div13:=TDemoDiv.Create(nil);
-  Div13.Parent:=Div1;
-
-  Div2:=TDemoDiv.Create(nil);
-  Div2.Parent:=Doc.Root;
-
-  Doc.Style:=LinesToStr([
-  ':first-of-type { left: 6px; }',
-  'div:first-of-type { top: 7px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','6px',Doc.Root.Left);
-  AssertEquals('Root.Top','',Doc.Root.Top);
-  AssertEquals('Div1.Left','6px',Div1.Left);
-  AssertEquals('Div1.Top','7px',Div1.Top);
-  AssertEquals('Div11.Left','6px',Div11.Left);
-  AssertEquals('Div11.Top','7px',Div11.Top);
-  AssertEquals('Button12.Left','6px',Button12.Left);
-  AssertEquals('Button12.Top','',Button12.Top);
-  AssertEquals('Div13.Left','',Div13.Left);
-  AssertEquals('Div13.Top','',Div13.Top);
-  AssertEquals('Div2.Left','',Div2.Left);
-  AssertEquals('Div2.Top','',Div2.Top);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_LastOfType;
-var
-  Div1, Div11, Div13, Div2: TDemoDiv;
-  Button12: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Parent:=Doc.Root;
-
-  Div11:=TDemoDiv.Create(nil);
-  Div11.Parent:=Div1;
-
-  Button12:=TDemoButton.Create(nil);
-  Button12.Parent:=Div1;
-
-  Div13:=TDemoDiv.Create(nil);
-  Div13.Parent:=Div1;
-
-  Div2:=TDemoDiv.Create(nil);
-  Div2.Parent:=Doc.Root;
-
-  Doc.Style:=LinesToStr([
-  ':last-of-type { left: 6px; }',
-  'div:last-of-type { top: 7px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','6px',Doc.Root.Left);
-  AssertEquals('Root.Top','',Doc.Root.Top);
-  AssertEquals('Div1.Left','',Div1.Left);
-  AssertEquals('Div1.Top','',Div1.Top);
-  AssertEquals('Div11.Left','',Div11.Left);
-  AssertEquals('Div11.Top','',Div11.Top);
-  AssertEquals('Button12.Left','6px',Button12.Left);
-  AssertEquals('Button12.Top','',Button12.Top);
-  AssertEquals('Div13.Left','6px',Div13.Left);
-  AssertEquals('Div13.Top','7px',Div13.Top);
-  AssertEquals('Div2.Left','6px',Div2.Left);
-  AssertEquals('Div2.Top','7px',Div2.Top);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_OnlyOfType;
-var
-  Div1, Div11, Div2: TDemoDiv;
-  Button12: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Parent:=Doc.Root;
-
-  Div11:=TDemoDiv.Create(nil);
-  Div11.Parent:=Div1;
-
-  Button12:=TDemoButton.Create(nil);
-  Button12.Parent:=Div1;
-
-  Div2:=TDemoDiv.Create(nil);
-  Div2.Parent:=Doc.Root;
-
-  Doc.Style:=LinesToStr([
-  ':only-of-type { left: 6px; }',
-  'div:only-of-type { top: 7px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','6px',Doc.Root.Left);
-  AssertEquals('Root.Top','',Doc.Root.Top);
-  AssertEquals('Div1.Left','',Div1.Left);
-  AssertEquals('Div1.Top','',Div1.Top);
-  AssertEquals('Div11.Left','6px',Div11.Left);
-  AssertEquals('Div11.Top','7px',Div11.Top);
-  AssertEquals('Button12.Left','6px',Button12.Left);
-  AssertEquals('Button12.Top','',Button12.Top);
-  AssertEquals('Div2.Left','',Div2.Left);
-  AssertEquals('Div2.Top','',Div2.Top);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_NthOfType;
-var
-  Div1, Div2, Div3, Div4: TDemoDiv;
-  Button1, Button2: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Root.Name:='root';
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Name:='Div1';
-  Div1.Parent:=Doc.Root;
-
-  Button1:=TDemoButton.Create(nil);
-  Button1.Name:='Button1';
-  Button1.Parent:=Doc.Root;
-
-  Div2:=TDemoDiv.Create(nil);
-  Div2.Name:='Div2';
-  Div2.Parent:=Doc.Root;
-
-  Div3:=TDemoDiv.Create(nil);
-  Div3.Name:='Div3';
-  Div3.Parent:=Doc.Root;
-
-  Button2:=TDemoButton.Create(nil);
-  Button2.Name:='Button2';
-  Button2.Parent:=Doc.Root;
-
-  Div4:=TDemoDiv.Create(nil);
-  Div4.Name:='Div4';
-  Div4.Parent:=Doc.Root;
-
-  Doc.Style:=LinesToStr([
-  ':nth-of-type(2n+1) { left: 8px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','',Doc.Root.Left);
-  AssertEquals('Div1.Left','8px',Div1.Left);
-  AssertEquals('Button1.Left','8px',Button1.Left);
-  AssertEquals('Div2.Left','',Div2.Left);
-  AssertEquals('Div3.Left','8px',Div3.Left);
-  AssertEquals('Button2.Left','',Button2.Left);
-  AssertEquals('Div4.Left','',Div4.Left);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_NthLastOfType;
-var
-  Div1, Div2, Div3, Div4: TDemoDiv;
-  Button1, Button2: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Parent:=Doc.Root;
-
-  Button1:=TDemoButton.Create(nil);
-  Button1.Parent:=Doc.Root;
-
-  Div2:=TDemoDiv.Create(nil);
-  Div2.Parent:=Doc.Root;
-
-  Div3:=TDemoDiv.Create(nil);
-  Div3.Parent:=Doc.Root;
-
-  Button2:=TDemoButton.Create(nil);
-  Button2.Parent:=Doc.Root;
-
-  Div4:=TDemoDiv.Create(nil);
-  Div4.Parent:=Doc.Root;
-
-  Doc.Style:=LinesToStr([
-  ':nth-last-of-type(2n+1) { left: 8px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','',Doc.Root.Left);
-  AssertEquals('Div1.Left','',Div1.Left);
-  AssertEquals('Button1.Left','',Button1.Left);
-  AssertEquals('Div2.Left','8px',Div2.Left);
-  AssertEquals('Div3.Left','',Div3.Left);
-  AssertEquals('Button2.Left','8px',Button2.Left);
-  AssertEquals('Div4.Left','8px',Div4.Left);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_Is;
-var
-  Div1, Div2: TDemoDiv;
-  Button1, Button2: TDemoButton;
-  Span1: TDemoSpan;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Root.Name:='root';
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Name:='Div1';
-  Div1.Parent:=Doc.Root;
-  Div1.ExplicitAttributes[naTop]:='3px';
-
-  Button1:=TDemoButton.Create(nil);
-  Button1.Name:='Button1';
-  Button1.Parent:=Doc.Root;
-
-  Div2:=TDemoDiv.Create(nil);
-  Div2.Parent:=Doc.Root;
-
-  Span1:=TDemoSpan.Create(nil);
-  Span1.Parent:=Doc.Root;
-  Span1.ExplicitAttributes[naTop]:='3px';
-
-  Button2:=TDemoButton.Create(nil);
-  Button2.Parent:=Doc.Root;
-  Button2.ExplicitAttributes[naTop]:='3px';
-
-  Doc.Style:=LinesToStr([
-  ':is(div, button)[top=3px] { left: 7px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','',Doc.Root.Left);
-  AssertEquals('Div1.Left','7px',Div1.Left);
-  AssertEquals('Button1.Left','',Button1.Left);
-  AssertEquals('Div2.Left','',Div2.Left);
-  AssertEquals('Span1.Left','',Div2.Left);
-  AssertEquals('Button2.Left','7px',Button2.Left);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_Where;
-var
-  Div1, Div2: TDemoDiv;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Root.Name:='root';
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Name:='Div1';
-  Div1.Parent:=Doc.Root;
-  Div1.ExplicitAttributes[naTop]:='3px';
-
-  Div2:=TDemoDiv.Create(nil);
-  Div2.Name:='Div2';
-  Div2.Parent:=Div1;
-  Div2.ExplicitAttributes[naTop]:='3px';
-
-  Doc.Style:=LinesToStr([
-  ':where(div[top=3px]) { left: 1px; }',
-  'div div { left: 2px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','',Doc.Root.Left);
-  AssertEquals('Div1.Left','1px',Div1.Left);
-  AssertEquals('Div2.Left','2px',Div2.Left);
-end;
-
-procedure TTestNewCSSResolver.Test_Selector_Hover;
-var
-  Div1, Div11: TDemoDiv;
-  Button1: TDemoButton;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-  Doc.Root.Name:='root';
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Name:='Div1';
-  Div1.Parent:=Doc.Root;
-  Div1.Hover:=true;
-
-  Button1:=TDemoButton.Create(nil);
-  Button1.Name:='Button1';
-  Button1.Parent:=Div1;
-  Button1.Hover:=true;
-
-  Div11:=TDemoDiv.Create(nil);
-  Div11.Name:='Div11';
-  Div11.Parent:=Div1;
-
-  Doc.Style:=LinesToStr([
-  ':hover { left: 1px; }',
-  'button:hover { top: 2px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','',Doc.Root.Left);
-  AssertEquals('Root.Top','',Doc.Root.Top);
-  AssertEquals('Div1.Left','1px',Div1.Left);
-  AssertEquals('Div1.Top','',Div1.Top);
-  AssertEquals('Button1.Left','1px',Button1.Left);
-  AssertEquals('Button1.Top','2px',Button1.Top);
-  AssertEquals('Div11.Left','',Div11.Left);
-  AssertEquals('Div11.Top','',Div11.Top);
-end;
-
-procedure TTestNewCSSResolver.Test_InlineStyle;
-var
-  Div1: TDemoDiv;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Parent:=Doc.Root;
-  Div1.InlineStyle:='left: 10px; top: 5px';
-
-  Doc.Style:=LinesToStr([
-  'div { left: 6px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','',Doc.Root.Left);
-  AssertEquals('Div1.Left','10px',Div1.Left);
-  AssertEquals('Div1.Top','5px',Div1.Top);
-end;
-
-procedure TTestNewCSSResolver.Test_Specifity_Id_Class;
-var
-  Div1: TDemoDiv;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Name:='Div1';
-  Div1.Parent:=Doc.Root;
-  Div1.CSSClasses.Add('bird');
-
-  Doc.Style:=LinesToStr([
-  '.bird { left: 6px; }',
-  '#Div1 { left: 7px; top: 8px; }', // id has higher specifity, no matter if before or after a .class
-  '.bird { top: 9px; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','',Doc.Root.Left);
-  AssertEquals('Div1.Left','7px',Div1.Left);
-  AssertEquals('Div1.Top','8px',Div1.Top);
-end;
-
-procedure TTestNewCSSResolver.Test_Specifity_Important;
-var
-  Div1: TDemoDiv;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Name:='Div1';
-  Div1.Parent:=Doc.Root;
-  Div1.CSSClasses.Add('bird');
-
-  Doc.Style:=LinesToStr([
-  '.bird { left: 6px !important; }',
-  '#Div1 { left: 7px; top: 8px; }',
-  '.bird { top: 9px ! important; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Root.Left','',Doc.Root.Left);
-  AssertEquals('Div1.Left','6px',Div1.Left);
-  AssertEquals('Div1.Top','9px',Div1.Top);
-end;
-
-procedure TTestNewCSSResolver.Test_Specifity_Shorthand_OneRule;
-var
-  Div1: TDemoDiv;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Name:='Div1';
-  Div1.Parent:=Doc.Root;
-  Div1.CSSClasses.Add('bird');
-
-  Doc.Style:='.bird { border-color: blue; border: 6px red; border-width: 7px; }';
-  Doc.ApplyStyle;
-  AssertEquals('Div1.BorderColor','red',Div1.BorderColor);
-  AssertEquals('Div1.BorderWidth','7px',Div1.BorderWidth);
-end;
-
-procedure TTestNewCSSResolver.Test_Specifity_Shorthand_ClassClass;
-var
-  Div1: TDemoDiv;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Name:='Div1';
-  Div1.Parent:=Doc.Root;
-  Div1.CSSClasses.Add('bird');
-  Div1.CSSClasses.Add('eagle');
-
-  Doc.Style:=LinesToStr([
-  '.bird.eagle { border-color: blue; }',
-  '.bird { border-width: 6px; }',
-  '.bird { border: 7px red; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Div1.BorderColor','blue',Div1.BorderColor);
-  AssertEquals('Div1.BorderWidth','7px',Div1.BorderWidth);
-end;
-
-procedure TTestNewCSSResolver.Test_Specifity_Longhand_All_Longhand;
-var
-  Div1: TDemoDiv;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Name:='Div1';
-  Div1.Parent:=Doc.Root;
-  Div1.CSSClasses.Add('bird');
-  Div1.CSSClasses.Add('eagle');
-
-  Doc.Style:=LinesToStr([
-  '.bird.eagle { border-color: blue; }',
-  '.bird { border-width: 7px; }',
-  '.bird { all: initial; }',
-  '.bird { background: red; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Div1.BorderColor','blue',Div1.BorderColor);
-  AssertEquals('Div1.BorderWidth','',Div1.BorderWidth);
-  AssertEquals('Div1.Background','red',Div1.Background);
-end;
-
-procedure TTestNewCSSResolver.Test_Specifity_Shorthand_All_Shorthand;
-var
-  Div1, Div2: TDemoDiv;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Name:='Div1';
-  Div1.Parent:=Doc.Root;
-  Div1.CSSClasses.Add('bird');
-
-  Div2:=TDemoDiv.Create(nil);
-  Div2.Name:='Div2';
-  Div2.Parent:=Doc.Root;
-  Div2.CSSClasses.Add('eagle');
-
-  Doc.Style:=LinesToStr([
-  '.bird { border: 7px blue; }',
-  '.bird { all: initial; }',
-  '.eagle { all: initial; }',
-  '.eagle { border: 8px red; }',
-  '']);
-  Doc.ApplyStyle;
-  AssertEquals('Div1.BorderColor','',Div1.BorderColor);
-  AssertEquals('Div1.BorderWidth','',Div1.BorderWidth);
-  AssertEquals('Div2.BorderColor','red',Div2.BorderColor);
-  AssertEquals('Div2.BorderWidth','8px',Div2.BorderWidth);
-end;
-
-procedure TTestNewCSSResolver.Test_Origin_Id_Class;
-var
-  Div1: TDemoDiv;
-begin
-  Doc.Root:=TDemoNode.Create(nil);
-
-  Div1:=TDemoDiv.Create(nil);
-  Div1.Name:='Div1';
-  Div1.Parent:=Doc.Root;
-
-  writeln('TTestNewCSSResolver.Test_Origin_Id_Class ',Doc.CSSResolver.ClassName);
-  Doc.CSSResolver.AddStyleSheet(cssoUserAgent,'testagent',
-  '#Div1 { border-width: 2px;'
-  +' border-color: blue !important;'
-  +' background: green; }'
-  );
-
-  Doc.Style:=LinesToStr([
-  'div { border-width: 3px; ', // although class has lower spec than id, author origin wins
-  ' border-color: orange;', // former important always wins
-  '}']);
-  Doc.ApplyStyle;
-  AssertEquals('Div1.BorderColor','blue',Div1.BorderColor);
-  AssertEquals('Div1.BorderWidth','3px',Div1.BorderWidth);
-  AssertEquals('Div1.Background','green',Div1.Background);
-end;
-
 { TDemoDiv }
 
 class function TDemoDiv.CSSTypeName: TCSSString;
@@ -1881,6 +570,7 @@ begin
   // create the css resolver
   FCSSResolver:=TCSSResolver.Create(nil);
   FCSSResolver.CSSRegistry:=TDemoNode.CSSRegistry;
+  FCSSResolver.OnLog:=@OnResolverLog;
 end;
 
 destructor TDemoDocument.Destroy;
@@ -1909,6 +599,12 @@ begin
   Traverse(Root);
 end;
 
+procedure TDemoDocument.OnResolverLog(Sender: TObject; Entry: TCSSResolverLogEntry);
+begin
+  if Sender=nil then ;
+  if Entry=nil then ;
+end;
+
 procedure TDemoDocument.ApplyTypeStyles;
 var
   FoundStyles: array of TDemoNodeClass;
@@ -2576,6 +1272,1374 @@ begin
   Result:='';
 end;
 
+{ TCustomTestNewCSSResolver }
+
+procedure TCustomTestNewCSSResolver.SetUp;
+var
+  AttrDesc: TCSSAttributeDesc;
+begin
+  inherited SetUp;
+
+  TDemoNode.CSSRegistry:=TDemoCSSRegistry.Create();
+
+  // register button attribute 'caption'
+  AttrDesc:=TDemoNode.CSSRegistry.AddAttribute('caption');
+  TDemoButton.CSSCaptionID:=AttrDesc.Index;
+
+  FDoc:=TDemoDocument.Create(nil);
+end;
+
+procedure TCustomTestNewCSSResolver.TearDown;
+begin
+  FreeAndNil(FDoc);
+  FreeAndNil(TDemoNode.CSSRegistry);
+  inherited TearDown;
+end;
+
+procedure TCustomTestNewCSSResolver.ApplyStyle;
+begin
+  Doc.ApplyStyle;
+  CheckWarnings;
+end;
+
+procedure TCustomTestNewCSSResolver.CheckWarnings;
+var
+  aResolver: TCSSResolver;
+  i: Integer;
+  Entry: TCSSResolverLogEntry;
+  s: String;
+begin
+  aResolver:=FDoc.CSSResolver;
+  if aResolver.LogCount=0 then exit;
+  writeln('TCustomTestNewCSSResolver.CheckWarnings LogCount=',aResolver.LogCount);
+  for i:=0 to aResolver.LogCount-1 do
+  begin
+    Entry:=aResolver.LogEntries[i];
+    s:='';
+    if Entry.PosEl<>nil then
+      s:=' at '+Entry.PosEl.SourceFileName+'('+IntToStr(Entry.PosEl.SourceRow)+','+IntToStr(Entry.PosEl.SourceRow)+')';
+    writeln('  ',Entry.MsgType,': ',Entry.ID,' ',Entry.Msg,s);
+  end;
+end;
+
+{ TTestNewCSSResolver }
+
+procedure TTestNewCSSResolver.Test_ParseAttr_Keyword;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Style:='* { direction: ltr; }';
+  ApplyStyle;
+  AssertEquals('Root.direction','ltr',Doc.Root.Direction);
+end;
+
+procedure TTestNewCSSResolver.Test_ParseAttr_Keyword_SkipInvalid;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Style:='* { direction: something ltr; }';
+  ApplyStyle;
+  AssertEquals('Root.direction','ltr',Doc.Root.Direction);
+end;
+
+procedure TTestNewCSSResolver.Test_ParseAttr_Float;
+var
+  Div1: TDemoDiv;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Style:=
+     ':root {'
+    +'  left: 10px;'
+    +'  top: .1px;'
+    +'  width: 3e2em;'
+    +'  height: 3e-2px;'
+    +'}'
+    +'div {'
+    +'  left: -4mm;'
+    +'  top: -.5pc;'
+    +'  width: .6cm;'
+    +'  height: 6E+1rem;'
+    +'}';
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Name:='Div1';
+  Div1.Parent:=Doc.Root;
+
+  ApplyStyle;
+  AssertEquals('Root.Left','10px',Doc.Root.Left);
+  AssertEquals('Root.Top','0.1px',Doc.Root.Top);
+  AssertEquals('Root.Width','300em',Doc.Root.Width);
+  AssertEquals('Root.Height','0.03px',Doc.Root.Height);
+  AssertEquals('Div1.Left','-4mm',Div1.Left);
+  AssertEquals('Div1.Top','-0.5pc',Div1.Top);
+  AssertEquals('Div1.Width','0.6cm',Div1.Width);
+  AssertEquals('Div1.Height','60rem',Div1.Height);
+end;
+
+procedure TTestNewCSSResolver.Test_ParseAttr_Float_SkipInvalid;
+begin
+  exit;
+
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Style:=
+     ':root {'
+    +'  left: something 10px;'
+    +'  top: 1 px;' // no space between number
+    +'  width: 0 px;' // the px is ignored because of the space, 0 without unit is allowed
+    +'  height: -4cm;' // no negative
+    +'}';
+
+  ApplyStyle;
+  AssertEquals('Root.Left','10px',Doc.Root.Left);
+  AssertEquals('Root.Top','invalid',Doc.Root.Top);
+  AssertEquals('Root.Width','0',Doc.Root.Width);
+  AssertEquals('Root.Height','invalid',Doc.Root.Height);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_Universal;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Style:='* { left: 10px; }';
+  ApplyStyle;
+  AssertEquals('Root.left','10px',Doc.Root.Left);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_Type;
+var
+  Button: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Button:=TDemoButton.Create(nil);
+  Button.Parent:=Doc.Root;
+  Doc.Style:='button { left: 11px; }';
+  ApplyStyle;
+  AssertEquals('Root.left','',Doc.Root.Left);
+  AssertEquals('Button.left','11px',Button.Left);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_Type_Spaces;
+var
+  Button1, Button2: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Button1:=TDemoButton.Create(nil);
+  Button1.Parent:=Doc.Root;
+
+  Button2:=TDemoButton.Create(nil);
+  Button2.Parent:=Doc.Root;
+
+  Doc.Style:='div, button ,span { left: 11px; }';
+  ApplyStyle;
+  AssertEquals('Root.left','',Doc.Root.Left);
+  AssertEquals('Button1.left','11px',Button1.Left);
+  AssertEquals('Button2.left','11px',Button2.Left);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_Id;
+var
+  Button1: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Button1:=TDemoButton.Create(nil);
+  Button1.Name:='Button1';
+  Button1.Parent:=Doc.Root;
+  Doc.Style:='#Button1 { left: 12px; }';
+  ApplyStyle;
+  AssertEquals('Root.left','',Doc.Root.Left);
+  AssertEquals('Button1.left','12px',Button1.Left);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_Class;
+var
+  Button1: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Button1:=TDemoButton.Create(nil);
+  Button1.CSSClasses.Add('west');
+  Button1.Parent:=Doc.Root;
+  Doc.Style:='.west { left: 13px; }';
+  ApplyStyle;
+  AssertEquals('Root.left','',Doc.Root.Left);
+  AssertEquals('Button1.left','13px',Button1.Left);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_ClassClass;
+var
+  Button1, Button2: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Button1:=TDemoButton.Create(nil);
+  Button1.CSSClasses.Add('west');
+  Button1.Parent:=Doc.Root;
+
+  Button2:=TDemoButton.Create(nil);
+  Button2.CSSClasses.DelimitedText:='west south';
+  AssertEquals('Button2.CSSClasses.Count',2,Button2.CSSClasses.Count);
+  Button2.Parent:=Doc.Root;
+
+  Doc.Style:='.west.south { left: 10px; }';
+  ApplyStyle;
+  AssertEquals('Root.left','',Doc.Root.Left);
+  AssertEquals('Button1.left','',Button1.Left);
+  AssertEquals('Button2.left','10px',Button2.Left);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_ClassSpaceClass;
+var
+  Button1: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Root.CSSClasses.Add('bird');
+
+  Button1:=TDemoButton.Create(nil);
+  Button1.CSSClasses.Add('west');
+  Button1.Parent:=Doc.Root;
+
+  Doc.Style:='.bird .west { left: 10px; }';
+  ApplyStyle;
+  AssertEquals('Root.left','',Doc.Root.Left);
+  AssertEquals('Button1.left','10px',Button1.Left);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_TypeCommaType;
+var
+  Button1: TDemoButton;
+  Div1: TDemoDiv;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Button1:=TDemoButton.Create(nil);
+  Button1.Parent:=Doc.Root;
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Parent:=Doc.Root;
+
+  Doc.Style:='div, button { left: 10px; }';
+  ApplyStyle;
+  AssertEquals('Root.left','',Doc.Root.Left);
+  AssertEquals('Button1.left','10px',Button1.Left);
+  AssertEquals('Div1.left','10px',Div1.Left);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_ClassGTClass;
+var
+  Div1, Div2: TDemoDiv;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Root.Name:='root';
+  Doc.Root.CSSClasses.Add('lvl1');
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Name:='Div1';
+  Div1.CSSClasses.Add('lvl2');
+  Div1.Parent:=Doc.Root;
+
+  Div2:=TDemoDiv.Create(nil);
+  Div2.Name:='Div2';
+  Div2.CSSClasses.Add('lvl3');
+  Div2.Parent:=Div1;
+
+  Doc.Style:=LinesToStr([
+  '.lvl1>.lvl2 { left: 10px; }', // set
+  '.lvl1>.lvl3 { top: 11px; }', // not set, not direct children
+  '.lvl2>.lvl3 { width: 12px; }', // set
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.left','',Doc.Root.Left);
+  AssertEquals('Root.top','',Doc.Root.Top);
+  AssertEquals('Root.width','',Doc.Root.Width);
+  AssertEquals('Div1.left','10px',Div1.Left);
+  AssertEquals('Div1.top','',Div1.Top);
+  AssertEquals('Div1.width','',Div1.Width);
+  AssertEquals('Div2.left','',Div2.Left);
+  AssertEquals('Div2.top','',Div2.Top);
+  AssertEquals('Div2.width','12px',Div2.Width);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_TypePlusType;
+var
+  Button1, Button2, Button3: TDemoButton;
+  Div1: TDemoDiv;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Root.Name:='root';
+
+  Button1:=TDemoButton.Create(nil);
+  Button1.Name:='Button1';
+  Button1.Parent:=Doc.Root;
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Name:='Div1';
+  Div1.Parent:=Doc.Root;
+
+  Button2:=TDemoButton.Create(nil);
+  Button2.Name:='Button2';
+  Button2.Parent:=Doc.Root;
+
+  Button3:=TDemoButton.Create(nil);
+  Button3.Name:='Button3';
+  Button3.Parent:=Doc.Root;
+
+  Doc.Style:='div+button { left: 10px; }'; // only Button2 has a prev sibling div
+  ApplyStyle;
+  AssertEquals('Root.left','',Doc.Root.Left);
+  AssertEquals('Button1.left','',Button1.Left);
+  AssertEquals('Div1.left','',Div1.Left);
+  AssertEquals('Button2.left','10px',Button2.Left);
+  AssertEquals('Button3.left','',Button3.Left);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_TypeTildeType;
+var
+  Button1, Button2, Button3: TDemoButton;
+  Div1: TDemoDiv;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Button1:=TDemoButton.Create(nil);
+  Button1.Parent:=Doc.Root;
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Parent:=Doc.Root;
+
+  Button2:=TDemoButton.Create(nil);
+  Button2.Parent:=Doc.Root;
+
+  Button3:=TDemoButton.Create(nil);
+  Button3.Parent:=Doc.Root;
+
+  Doc.Style:='div~button { left: 10px; }';
+  ApplyStyle;
+  AssertEquals('Root.left','',Doc.Root.Left);
+  AssertEquals('Button1.left','',Button1.Left);
+  AssertEquals('Div1.left','',Div1.Left);
+  AssertEquals('Button2.left','10px',Button2.Left);
+  AssertEquals('Button3.left','10px',Button3.Left);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_HasAttribute;
+var
+  Button1: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Root.Name:='root';
+  Doc.Root.ExplicitAttributes[naLeft]:='100px';
+
+  Button1:=TDemoButton.Create(nil);
+  Button1.Name:='Button1';
+  Button1.Parent:=Doc.Root;
+  Button1.ExplicitAttributes[naLeft]:='2px';
+  Button1.ExplicitCaption:='Click Button1';
+
+  Doc.Style:=LinesToStr([
+  '[left] { top: 3px; }',
+  '[caption] { width: 4px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Top','3px',Doc.Root.Top);
+  AssertEquals('Root.Width','',Doc.Root.Width);
+  AssertEquals('Button1.Top','3px',Button1.Top);
+  AssertEquals('Button1.Width','4px',Button1.Width);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_AttributeEquals;
+var
+  Button1: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Root.ExplicitAttributes[naLeft]:='2px';
+
+  Button1:=TDemoButton.Create(nil);
+  Button1.Parent:=Doc.Root;
+  Button1.ExplicitAttributes[naLeft]:='3px';
+  Button1.ExplicitAttributes[naColor]:='maybe black';
+
+  Doc.Style:=LinesToStr([
+  '[left=2px] { top: 4px; }',
+  '[color="maybe black"] { width: 5px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Top','4px',Doc.Root.Top);
+  AssertEquals('Button1.Top','',Button1.Top);
+  AssertEquals('Button1.Width','5px',Button1.Width);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_AttributeEqualsI;
+var
+  Button1: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Root.ExplicitAttributes[naLeft]:='2px';
+
+  Button1:=TDemoButton.Create(nil);
+  Button1.Parent:=Doc.Root;
+  Button1.ExplicitAttributes[naLeft]:='3px';
+  Button1.ExplicitAttributes[naColor]:='maybe Black';
+
+  Doc.Style:=LinesToStr([
+  '[left="2Px" i] { top: 4px; }',
+  '[color="Maybe bLack" i] { width: 5px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Top','4px',Doc.Root.Top);
+  AssertEquals('Button1.Top','',Button1.Top);
+  AssertEquals('Button1.Width','5px',Button1.Width);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_AttributeBeginsWith;
+var
+  Button1: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Root.ExplicitAttributes[naLeft]:='Foo';
+
+  Button1:=TDemoButton.Create(nil);
+  Button1.Parent:=Doc.Root;
+  Button1.ExplicitAttributes[naLeft]:='Foo Bar';
+
+  Doc.Style:=LinesToStr([
+  '[left^=Fo] { top: 4px; }',
+  '[left^="Foo B"] { width: 5px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Top','4px',Doc.Root.Top);
+  AssertEquals('Root.Width','',Doc.Root.Width);
+  AssertEquals('Button1.Top','4px',Button1.Top);
+  AssertEquals('Button1.Width','5px',Button1.Width);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_AttributeEndsWith;
+var
+  Button1: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Root.ExplicitAttributes[naLeft]:='Foo';
+
+  Button1:=TDemoButton.Create(nil);
+  Button1.Parent:=Doc.Root;
+  Button1.ExplicitAttributes[naLeft]:='Foo Bar';
+
+  Doc.Style:=LinesToStr([
+  '[left$=o] { top: 4px; }',
+  '[left$="o Bar"] { width: 5px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Top','4px',Doc.Root.Top);
+  AssertEquals('Root.Width','',Doc.Root.Width);
+  AssertEquals('Button1.Top','',Button1.Top);
+  AssertEquals('Button1.Width','5px',Button1.Width);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_AttributeBeginsWithHyphen;
+var
+  Button1: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Root.ExplicitAttributes[naLeft]:='Foo';
+
+  Button1:=TDemoButton.Create(nil);
+  Button1.Parent:=Doc.Root;
+  Button1.ExplicitAttributes[naLeft]:='Foo-Bar';
+
+  Doc.Style:=LinesToStr([
+  '[left|=Foo] { top: 4px; }',
+  '[left|="Fo"] { width: 5px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Top','4px',Doc.Root.Top);
+  AssertEquals('Root.Width','',Doc.Root.Width);
+  AssertEquals('Button1.Top','4px',Button1.Top);
+  AssertEquals('Button1.Width','',Button1.Width);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_AttributeContainsWord;
+var
+  Button1: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Root.Name:='root';
+  Doc.Root.ExplicitAttributes[naLeft]:='One Two Three';
+
+  Button1:=TDemoButton.Create(nil);
+  Button1.Name:='Button1';
+  Button1.Parent:=Doc.Root;
+  Button1.ExplicitAttributes[naLeft]:='Four Five';
+
+  Doc.Style:=LinesToStr([
+  '[left~=One] { top: 4px; }',
+  '[left~=Two] { width: 5px; }',
+  '[left~=Three] { height: 6px; }',
+  '[left~="Four Five"] { color: #123; }',  // not one word, so does not match!
+  '[left~=our] { display: none; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Top','4px',Doc.Root.Top);
+  AssertEquals('Root.Width','5px',Doc.Root.Width);
+  AssertEquals('Root.Height','6px',Doc.Root.Height);
+  AssertEquals('Root.Color','',Doc.Root.Color);
+  AssertEquals('Root.Display','',Doc.Root.Display);
+  AssertEquals('Button1.Top','',Button1.Top);
+  AssertEquals('Button1.Width','',Button1.Width);
+  AssertEquals('Button1.Height','',Button1.Height);
+  AssertEquals('Button1.Color','',Button1.Color);
+  AssertEquals('Button1.Display','inline-block',Button1.Display);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_AttributeContainsSubstring;
+var
+  Button1: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Root.ExplicitAttributes[naLeft]:='Foo';
+
+  Button1:=TDemoButton.Create(nil);
+  Button1.Parent:=Doc.Root;
+  Button1.ExplicitAttributes[naLeft]:='Foo Bar';
+
+  Doc.Style:=LinesToStr([
+  '[left*=oo] { top: 4px; }',
+  '[left*="o B"] { width: 5px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Top','4px',Doc.Root.Top);
+  AssertEquals('Root.Width','',Doc.Root.Width);
+  AssertEquals('Button1.Top','4px',Button1.Top);
+  AssertEquals('Button1.Width','5px',Button1.Width);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_Root;
+var
+  Button1: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Root.ExplicitAttributes[naLeft]:='Foo';
+
+  Button1:=TDemoButton.Create(nil);
+  Button1.Parent:=Doc.Root;
+
+  Doc.Style:=LinesToStr([
+  ':roOt { top: 4px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Top','4px',Doc.Root.Top);
+  AssertEquals('Button1.Top','',Button1.Top);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_Empty;
+var
+  Div1, Div11, Div2: TDemoDiv;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Parent:=Doc.Root;
+
+  Div11:=TDemoDiv.Create(nil);
+  Div11.Parent:=Div1;
+
+  Div2:=TDemoDiv.Create(nil);
+  Div2.Parent:=Doc.Root;
+
+  Doc.Style:=LinesToStr([
+  ':eMpty { left: 1px; }',
+  'div:emPty { top: 2px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Left','',Doc.Root.Left);
+  AssertEquals('Root.Top','',Doc.Root.Top);
+  AssertEquals('Div1.Left','',Div1.Left);
+  AssertEquals('Div1.Top','',Div1.Top);
+  AssertEquals('Div11.Left','1px',Div11.Left);
+  AssertEquals('Div11.Top','2px',Div11.Top);
+  AssertEquals('Div2.Left','1px',Div2.Left);
+  AssertEquals('Div2.Top','2px',Div2.Top);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_FirstChild;
+var
+  Div1, Div11, Div12, Div2: TDemoDiv;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Parent:=Doc.Root;
+
+  Div11:=TDemoDiv.Create(nil);
+  Div11.Parent:=Div1;
+
+  Div12:=TDemoDiv.Create(nil);
+  Div12.Parent:=Div1;
+
+  Div2:=TDemoDiv.Create(nil);
+  Div2.Parent:=Doc.Root;
+
+  Doc.Style:=LinesToStr([
+  ':first-child { left: 1px; }',
+  'div:first-child { top: 2px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Left','1px',Doc.Root.Left);
+  AssertEquals('Root.Top','',Doc.Root.Top);
+  AssertEquals('Div1.Left','1px',Div1.Left);
+  AssertEquals('Div1.Top','2px',Div1.Top);
+  AssertEquals('Div11.Left','1px',Div11.Left);
+  AssertEquals('Div11.Top','2px',Div11.Top);
+  AssertEquals('Div12.Left','',Div12.Left);
+  AssertEquals('Div12.Top','',Div12.Top);
+  AssertEquals('Div2.Left','',Div2.Left);
+  AssertEquals('Div2.Top','',Div2.Top);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_LastChild;
+var
+  Div1, Div11, Div2: TDemoDiv;
+  Button12: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Parent:=Doc.Root;
+
+  Div11:=TDemoDiv.Create(nil);
+  Div11.Parent:=Div1;
+
+  Button12:=TDemoButton.Create(nil);
+  Button12.Parent:=Div1;
+
+  Div2:=TDemoDiv.Create(nil);
+  Div2.Parent:=Doc.Root;
+
+  Doc.Style:=LinesToStr([
+  ':last-child { left: 6px; }',
+  'div:last-child { top: 7px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Left','6px',Doc.Root.Left);
+  AssertEquals('Root.Top','',Doc.Root.Top);
+  AssertEquals('Div1.Left','',Div1.Left);
+  AssertEquals('Div1.Top','',Div1.Top);
+  AssertEquals('Div11.Left','',Div11.Left);
+  AssertEquals('Div11.Top','',Div11.Top);
+  AssertEquals('Button12.Left','6px',Button12.Left);
+  AssertEquals('Button12.Top','',Button12.Top);
+  AssertEquals('Div2.Left','6px',Div2.Left);
+  AssertEquals('Div2.Top','7px',Div2.Top);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_OnlyChild;
+var
+  Div1, Div11, Div2: TDemoDiv;
+  Button12: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Root.Name:='root';
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Name:='Div1';
+  Div1.Parent:=Doc.Root;
+
+  Div11:=TDemoDiv.Create(nil);
+  Div11.Name:='Div11';
+  Div11.Parent:=Div1;
+
+  Div2:=TDemoDiv.Create(nil);
+  Div2.Name:='Div2';
+  Div2.Parent:=Doc.Root;
+
+  Button12:=TDemoButton.Create(nil);
+  Button12.Name:='Button12';
+  Button12.Parent:=Div2;
+
+  Doc.Style:=LinesToStr([
+  ':only-child { left: 8px; }',
+  'div:only-child { top: 9px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Left','8px',Doc.Root.Left);
+  AssertEquals('Root.Top','',Doc.Root.Top);
+  AssertEquals('Div1.Left','',Div1.Left);
+  AssertEquals('Div1.Top','',Div1.Top);
+  AssertEquals('Div11.Left','8px',Div11.Left);
+  AssertEquals('Div11.Top','9px',Div11.Top);
+  AssertEquals('Div2.Left','',Div2.Left);
+  AssertEquals('Div2.Top','',Div2.Top);
+  AssertEquals('Button12.Left','8px',Button12.Left);
+  AssertEquals('Button12.Top','',Button12.Top);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_Not;
+var
+  Div1, Div11, Div2: TDemoDiv;
+  Button12: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Root.Name:='root';
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Name:='Div1';
+  Div1.Parent:=Doc.Root;
+
+  Div11:=TDemoDiv.Create(nil);
+  Div11.Name:='Div11';
+  Div11.Parent:=Div1;
+
+  Div2:=TDemoDiv.Create(nil);
+  Div2.Name:='Div2';
+  Div2.Parent:=Doc.Root;
+
+  Button12:=TDemoButton.Create(nil);
+  Button12.Name:='Button12';
+  Button12.Parent:=Div2;
+
+  Doc.Style:=LinesToStr([
+  ':not(:only-child) { left: 8px; }',
+  ':not(div:only-child) { top: 9px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Left','',Doc.Root.Left);
+  AssertEquals('Root.Top','9px',Doc.Root.Top);
+  AssertEquals('Div1.Left','8px',Div1.Left);
+  AssertEquals('Div1.Top','9px',Div1.Top);
+  AssertEquals('Div11.Left','',Div11.Left);
+  AssertEquals('Div11.Top','',Div11.Top);
+  AssertEquals('Div2.Left','8px',Div2.Left);
+  AssertEquals('Div2.Top','9px',Div2.Top);
+  AssertEquals('Button12.Left','',Button12.Left);
+  AssertEquals('Button12.Top','9px',Button12.Top);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_NthChild;
+var
+  Div1, Div2, Div3, Div4: TDemoDiv;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Parent:=Doc.Root;
+
+  Div2:=TDemoDiv.Create(nil);
+  Div2.Parent:=Doc.Root;
+
+  Div3:=TDemoDiv.Create(nil);
+  Div3.Parent:=Doc.Root;
+
+  Div4:=TDemoDiv.Create(nil);
+  Div4.Parent:=Doc.Root;
+
+  Doc.Style:=LinesToStr([
+  'div:nth-child(2n+1) { left: 8px; }',
+  'div:nth-child(n+3) { border-width: 6px; }',
+  'div:nth-child(-n+2) { height: 7em; }',
+  'div:nth-child(even) { top: 3px; }',
+  'div:nth-child(odd) { width: 4px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Left','',Doc.Root.Left);
+  AssertEquals('Root.BorderWidth','',Doc.Root.BorderWidth);
+  AssertEquals('Root.Height','',Doc.Root.Height);
+  AssertEquals('Root.Top','',Doc.Root.Top);
+  AssertEquals('Root.Width','',Doc.Root.Width);
+  AssertEquals('Div1.Left','8px',Div1.Left);
+  AssertEquals('Div1.BorderWidth','',Div1.BorderWidth);
+  AssertEquals('Div1.Height','7em',Div1.Height);
+  AssertEquals('Div1.Top','',Div1.Top);
+  AssertEquals('Div1.Width','4px',Div1.Width);
+  AssertEquals('Div2.Left','',Div2.Left);
+  AssertEquals('Div2.BorderWidth','',Div2.BorderWidth);
+  AssertEquals('Div2.Height','7em',Div2.Height);
+  AssertEquals('Div2.Top','3px',Div2.Top);
+  AssertEquals('Div2.Width','',Div2.Width);
+  AssertEquals('Div3.Left','8px',Div3.Left);
+  AssertEquals('Div3.BorderWidth','6px',Div3.BorderWidth);
+  AssertEquals('Div3.Height','',Div3.Height);
+  AssertEquals('Div3.Top','',Div3.Top);
+  AssertEquals('Div3.Width','4px',Div3.Width);
+  AssertEquals('Div4.Left','',Div4.Left);
+  AssertEquals('Div4.BorderWidth','6px',Div4.BorderWidth);
+  AssertEquals('Div4.Height','',Div4.Height);
+  AssertEquals('Div4.Top','3px',Div4.Top);
+  AssertEquals('Div4.Width','',Div4.Width);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_NthLastChild;
+var
+  Div1, Div2, Div3, Div4: TDemoDiv;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Parent:=Doc.Root;
+
+  Div2:=TDemoDiv.Create(nil);
+  Div2.Parent:=Doc.Root;
+
+  Div3:=TDemoDiv.Create(nil);
+  Div3.Parent:=Doc.Root;
+
+  Div4:=TDemoDiv.Create(nil);
+  Div4.Parent:=Doc.Root;
+
+  Doc.Style:=LinesToStr([
+  ':nth-last-child(2n+1) { left: 8px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Left','',Doc.Root.Left);
+  AssertEquals('Div1.Left','',Div1.Left);
+  AssertEquals('Div2.Left','8px',Div2.Left);
+  AssertEquals('Div3.Left','',Div3.Left);
+  AssertEquals('Div4.Left','8px',Div4.Left);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_NthChildOf;
+var
+  Div1, Div2, Div3, Div4: TDemoDiv;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Root.Name:='root';
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Name:='Div1';
+  Div1.Parent:=Doc.Root;
+
+  Div2:=TDemoDiv.Create(nil);
+  Div2.Name:='Div2';
+  Div2.Parent:=Doc.Root;
+  Div2.ExplicitAttributes[naTop]:='3px';
+
+  Div3:=TDemoDiv.Create(nil);
+  Div3.Name:='Div3';
+  Div3.Parent:=Doc.Root;
+  Div3.ExplicitAttributes[naTop]:='3px';
+
+  Div4:=TDemoDiv.Create(nil);
+  Div4.Name:='Div4';
+  Div4.Parent:=Doc.Root;
+  Div4.ExplicitAttributes[naTop]:='3px';
+
+  Doc.Style:=LinesToStr([
+  ':nth-child(2n+1 of [top=3px]) { left: 5px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Left','',Doc.Root.Left);
+  AssertEquals('Div1.Left','',Div1.Left);
+  AssertEquals('Div2.Left','5px',Div2.Left);
+  AssertEquals('Div3.Left','',Div3.Left);
+  AssertEquals('Div4.Left','5px',Div4.Left);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_FirstOfType;
+var
+  Div1, Div11, Div13, Div2: TDemoDiv;
+  Button12: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Parent:=Doc.Root;
+
+  Div11:=TDemoDiv.Create(nil);
+  Div11.Parent:=Div1;
+
+  Button12:=TDemoButton.Create(nil);
+  Button12.Parent:=Div1;
+
+  Div13:=TDemoDiv.Create(nil);
+  Div13.Parent:=Div1;
+
+  Div2:=TDemoDiv.Create(nil);
+  Div2.Parent:=Doc.Root;
+
+  Doc.Style:=LinesToStr([
+  ':first-of-type { left: 6px; }',
+  'div:first-of-type { top: 7px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Left','6px',Doc.Root.Left);
+  AssertEquals('Root.Top','',Doc.Root.Top);
+  AssertEquals('Div1.Left','6px',Div1.Left);
+  AssertEquals('Div1.Top','7px',Div1.Top);
+  AssertEquals('Div11.Left','6px',Div11.Left);
+  AssertEquals('Div11.Top','7px',Div11.Top);
+  AssertEquals('Button12.Left','6px',Button12.Left);
+  AssertEquals('Button12.Top','',Button12.Top);
+  AssertEquals('Div13.Left','',Div13.Left);
+  AssertEquals('Div13.Top','',Div13.Top);
+  AssertEquals('Div2.Left','',Div2.Left);
+  AssertEquals('Div2.Top','',Div2.Top);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_LastOfType;
+var
+  Div1, Div11, Div13, Div2: TDemoDiv;
+  Button12: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Parent:=Doc.Root;
+
+  Div11:=TDemoDiv.Create(nil);
+  Div11.Parent:=Div1;
+
+  Button12:=TDemoButton.Create(nil);
+  Button12.Parent:=Div1;
+
+  Div13:=TDemoDiv.Create(nil);
+  Div13.Parent:=Div1;
+
+  Div2:=TDemoDiv.Create(nil);
+  Div2.Parent:=Doc.Root;
+
+  Doc.Style:=LinesToStr([
+  ':last-of-type { left: 6px; }',
+  'div:last-of-type { top: 7px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Left','6px',Doc.Root.Left);
+  AssertEquals('Root.Top','',Doc.Root.Top);
+  AssertEquals('Div1.Left','',Div1.Left);
+  AssertEquals('Div1.Top','',Div1.Top);
+  AssertEquals('Div11.Left','',Div11.Left);
+  AssertEquals('Div11.Top','',Div11.Top);
+  AssertEquals('Button12.Left','6px',Button12.Left);
+  AssertEquals('Button12.Top','',Button12.Top);
+  AssertEquals('Div13.Left','6px',Div13.Left);
+  AssertEquals('Div13.Top','7px',Div13.Top);
+  AssertEquals('Div2.Left','6px',Div2.Left);
+  AssertEquals('Div2.Top','7px',Div2.Top);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_OnlyOfType;
+var
+  Div1, Div11, Div2: TDemoDiv;
+  Button12: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Parent:=Doc.Root;
+
+  Div11:=TDemoDiv.Create(nil);
+  Div11.Parent:=Div1;
+
+  Button12:=TDemoButton.Create(nil);
+  Button12.Parent:=Div1;
+
+  Div2:=TDemoDiv.Create(nil);
+  Div2.Parent:=Doc.Root;
+
+  Doc.Style:=LinesToStr([
+  ':only-of-type { left: 6px; }',
+  'div:only-of-type { top: 7px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Left','6px',Doc.Root.Left);
+  AssertEquals('Root.Top','',Doc.Root.Top);
+  AssertEquals('Div1.Left','',Div1.Left);
+  AssertEquals('Div1.Top','',Div1.Top);
+  AssertEquals('Div11.Left','6px',Div11.Left);
+  AssertEquals('Div11.Top','7px',Div11.Top);
+  AssertEquals('Button12.Left','6px',Button12.Left);
+  AssertEquals('Button12.Top','',Button12.Top);
+  AssertEquals('Div2.Left','',Div2.Left);
+  AssertEquals('Div2.Top','',Div2.Top);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_NthOfType;
+var
+  Div1, Div2, Div3, Div4: TDemoDiv;
+  Button1, Button2: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Root.Name:='root';
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Name:='Div1';
+  Div1.Parent:=Doc.Root;
+
+  Button1:=TDemoButton.Create(nil);
+  Button1.Name:='Button1';
+  Button1.Parent:=Doc.Root;
+
+  Div2:=TDemoDiv.Create(nil);
+  Div2.Name:='Div2';
+  Div2.Parent:=Doc.Root;
+
+  Div3:=TDemoDiv.Create(nil);
+  Div3.Name:='Div3';
+  Div3.Parent:=Doc.Root;
+
+  Button2:=TDemoButton.Create(nil);
+  Button2.Name:='Button2';
+  Button2.Parent:=Doc.Root;
+
+  Div4:=TDemoDiv.Create(nil);
+  Div4.Name:='Div4';
+  Div4.Parent:=Doc.Root;
+
+  Doc.Style:=LinesToStr([
+  ':nth-of-type(2n+1) { left: 8px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Left','',Doc.Root.Left);
+  AssertEquals('Div1.Left','8px',Div1.Left);
+  AssertEquals('Button1.Left','8px',Button1.Left);
+  AssertEquals('Div2.Left','',Div2.Left);
+  AssertEquals('Div3.Left','8px',Div3.Left);
+  AssertEquals('Button2.Left','',Button2.Left);
+  AssertEquals('Div4.Left','',Div4.Left);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_NthLastOfType;
+var
+  Div1, Div2, Div3, Div4: TDemoDiv;
+  Button1, Button2: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Parent:=Doc.Root;
+
+  Button1:=TDemoButton.Create(nil);
+  Button1.Parent:=Doc.Root;
+
+  Div2:=TDemoDiv.Create(nil);
+  Div2.Parent:=Doc.Root;
+
+  Div3:=TDemoDiv.Create(nil);
+  Div3.Parent:=Doc.Root;
+
+  Button2:=TDemoButton.Create(nil);
+  Button2.Parent:=Doc.Root;
+
+  Div4:=TDemoDiv.Create(nil);
+  Div4.Parent:=Doc.Root;
+
+  Doc.Style:=LinesToStr([
+  ':nth-last-of-type(2n+1) { left: 8px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Left','',Doc.Root.Left);
+  AssertEquals('Div1.Left','',Div1.Left);
+  AssertEquals('Button1.Left','',Button1.Left);
+  AssertEquals('Div2.Left','8px',Div2.Left);
+  AssertEquals('Div3.Left','',Div3.Left);
+  AssertEquals('Button2.Left','8px',Button2.Left);
+  AssertEquals('Div4.Left','8px',Div4.Left);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_Is;
+var
+  Div1, Div2: TDemoDiv;
+  Button1, Button2: TDemoButton;
+  Span1: TDemoSpan;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Root.Name:='root';
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Name:='Div1';
+  Div1.Parent:=Doc.Root;
+  Div1.ExplicitAttributes[naTop]:='3px';
+
+  Button1:=TDemoButton.Create(nil);
+  Button1.Name:='Button1';
+  Button1.Parent:=Doc.Root;
+
+  Div2:=TDemoDiv.Create(nil);
+  Div2.Parent:=Doc.Root;
+
+  Span1:=TDemoSpan.Create(nil);
+  Span1.Parent:=Doc.Root;
+  Span1.ExplicitAttributes[naTop]:='3px';
+
+  Button2:=TDemoButton.Create(nil);
+  Button2.Parent:=Doc.Root;
+  Button2.ExplicitAttributes[naTop]:='3px';
+
+  Doc.Style:=LinesToStr([
+  ':is(div, button)[top=3px] { left: 7px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Left','',Doc.Root.Left);
+  AssertEquals('Div1.Left','7px',Div1.Left);
+  AssertEquals('Button1.Left','',Button1.Left);
+  AssertEquals('Div2.Left','',Div2.Left);
+  AssertEquals('Span1.Left','',Div2.Left);
+  AssertEquals('Button2.Left','7px',Button2.Left);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_Where;
+var
+  Div1, Div2: TDemoDiv;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Root.Name:='root';
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Name:='Div1';
+  Div1.Parent:=Doc.Root;
+  Div1.ExplicitAttributes[naTop]:='3px';
+
+  Div2:=TDemoDiv.Create(nil);
+  Div2.Name:='Div2';
+  Div2.Parent:=Div1;
+  Div2.ExplicitAttributes[naTop]:='3px';
+
+  Doc.Style:=LinesToStr([
+  ':where(div[top=3px]) { left: 1px; }',
+  'div div { left: 2px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Left','',Doc.Root.Left);
+  AssertEquals('Div1.Left','1px',Div1.Left);
+  AssertEquals('Div2.Left','2px',Div2.Left);
+end;
+
+procedure TTestNewCSSResolver.Test_Selector_Hover;
+var
+  Div1, Div11: TDemoDiv;
+  Button1: TDemoButton;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+  Doc.Root.Name:='root';
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Name:='Div1';
+  Div1.Parent:=Doc.Root;
+  Div1.Hover:=true;
+
+  Button1:=TDemoButton.Create(nil);
+  Button1.Name:='Button1';
+  Button1.Parent:=Div1;
+  Button1.Hover:=true;
+
+  Div11:=TDemoDiv.Create(nil);
+  Div11.Name:='Div11';
+  Div11.Parent:=Div1;
+
+  Doc.Style:=LinesToStr([
+  ':hover { left: 1px; }',
+  'button:hover { top: 2px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Left','',Doc.Root.Left);
+  AssertEquals('Root.Top','',Doc.Root.Top);
+  AssertEquals('Div1.Left','1px',Div1.Left);
+  AssertEquals('Div1.Top','',Div1.Top);
+  AssertEquals('Button1.Left','1px',Button1.Left);
+  AssertEquals('Button1.Top','2px',Button1.Top);
+  AssertEquals('Div11.Left','',Div11.Left);
+  AssertEquals('Div11.Top','',Div11.Top);
+end;
+
+procedure TTestNewCSSResolver.Test_InlineStyle;
+var
+  Div1: TDemoDiv;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Parent:=Doc.Root;
+  Div1.InlineStyle:='left: 10px; top: 5px';
+
+  Doc.Style:=LinesToStr([
+  'div { left: 6px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Left','',Doc.Root.Left);
+  AssertEquals('Div1.Left','10px',Div1.Left);
+  AssertEquals('Div1.Top','5px',Div1.Top);
+end;
+
+procedure TTestNewCSSResolver.Test_Specifity_Id_Class;
+var
+  Div1: TDemoDiv;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Name:='Div1';
+  Div1.Parent:=Doc.Root;
+  Div1.CSSClasses.Add('bird');
+
+  Doc.Style:=LinesToStr([
+  '.bird { left: 6px; }',
+  '#Div1 { left: 7px; top: 8px; }', // id has higher specifity, no matter if before or after a .class
+  '.bird { top: 9px; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Left','',Doc.Root.Left);
+  AssertEquals('Div1.Left','7px',Div1.Left);
+  AssertEquals('Div1.Top','8px',Div1.Top);
+end;
+
+procedure TTestNewCSSResolver.Test_Specifity_Important;
+var
+  Div1: TDemoDiv;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Name:='Div1';
+  Div1.Parent:=Doc.Root;
+  Div1.CSSClasses.Add('bird');
+
+  Doc.Style:=LinesToStr([
+  '.bird { left: 6px !important; }',
+  '#Div1 { left: 7px; top: 8px; }',
+  '.bird { top: 9px ! important; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Root.Left','',Doc.Root.Left);
+  AssertEquals('Div1.Left','6px',Div1.Left);
+  AssertEquals('Div1.Top','9px',Div1.Top);
+end;
+
+procedure TTestNewCSSResolver.Test_Specifity_Shorthand_OneRule;
+var
+  Div1: TDemoDiv;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Name:='Div1';
+  Div1.Parent:=Doc.Root;
+  Div1.CSSClasses.Add('bird');
+
+  Doc.Style:='.bird { border-color: blue; border: 6px red; border-width: 7px; }';
+  ApplyStyle;
+  AssertEquals('Div1.BorderColor','red',Div1.BorderColor);
+  AssertEquals('Div1.BorderWidth','7px',Div1.BorderWidth);
+end;
+
+procedure TTestNewCSSResolver.Test_Specifity_Shorthand_ClassClass;
+var
+  Div1: TDemoDiv;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Name:='Div1';
+  Div1.Parent:=Doc.Root;
+  Div1.CSSClasses.Add('bird');
+  Div1.CSSClasses.Add('eagle');
+
+  Doc.Style:=LinesToStr([
+  '.bird.eagle { border-color: blue; }',
+  '.bird { border-width: 6px; }',
+  '.bird { border: 7px red; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Div1.BorderColor','blue',Div1.BorderColor);
+  AssertEquals('Div1.BorderWidth','7px',Div1.BorderWidth);
+end;
+
+procedure TTestNewCSSResolver.Test_Specifity_Longhand_All_Longhand;
+var
+  Div1: TDemoDiv;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Name:='Div1';
+  Div1.Parent:=Doc.Root;
+  Div1.CSSClasses.Add('bird');
+  Div1.CSSClasses.Add('eagle');
+
+  Doc.Style:=LinesToStr([
+  '.bird.eagle { border-color: blue; }',
+  '.bird { border-width: 7px; }',
+  '.bird { all: initial; }',
+  '.bird { background: red; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Div1.BorderColor','blue',Div1.BorderColor);
+  AssertEquals('Div1.BorderWidth','',Div1.BorderWidth);
+  AssertEquals('Div1.Background','red',Div1.Background);
+end;
+
+procedure TTestNewCSSResolver.Test_Specifity_Shorthand_All_Shorthand;
+var
+  Div1, Div2: TDemoDiv;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Name:='Div1';
+  Div1.Parent:=Doc.Root;
+  Div1.CSSClasses.Add('bird');
+
+  Div2:=TDemoDiv.Create(nil);
+  Div2.Name:='Div2';
+  Div2.Parent:=Doc.Root;
+  Div2.CSSClasses.Add('eagle');
+
+  Doc.Style:=LinesToStr([
+  '.bird { border: 7px blue; }',
+  '.bird { all: initial; }',
+  '.eagle { all: initial; }',
+  '.eagle { border: 8px red; }',
+  '']);
+  ApplyStyle;
+  AssertEquals('Div1.BorderColor','',Div1.BorderColor);
+  AssertEquals('Div1.BorderWidth','',Div1.BorderWidth);
+  AssertEquals('Div2.BorderColor','red',Div2.BorderColor);
+  AssertEquals('Div2.BorderWidth','8px',Div2.BorderWidth);
+end;
+
+procedure TTestNewCSSResolver.Test_Origin_Id_Class;
+var
+  Div1: TDemoDiv;
+begin
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Name:='Div1';
+  Div1.Parent:=Doc.Root;
+
+  Doc.CSSResolver.AddStyleSheet(cssoUserAgent,'testagent',
+  '#Div1 { border-width: 2px;'
+  +' border-color: blue !important;'
+  +' background: green; }'
+  );
+
+  Doc.Style:=LinesToStr([
+  'div { border-width: 3px; ', // although class has lower spec than id, author origin wins
+  ' border-color: orange;', // former important always wins
+  '}']);
+  ApplyStyle;
+  AssertEquals('Div1.BorderColor','blue',Div1.BorderColor);
+  AssertEquals('Div1.BorderWidth','3px',Div1.BorderWidth);
+  AssertEquals('Div1.Background','green',Div1.Background);
+end;
+
+procedure TTestNewCSSResolver.Test_Var;
+var
+  Div1: TDemoDiv;
+begin
+  exit;
+
+  Doc.Root:=TDemoNode.Create(nil);
+
+  Div1:=TDemoDiv.Create(nil);
+  Div1.Name:='Div1';
+  Div1.Parent:=Doc.Root;
+
+  Doc.Style:=LinesToStr([
+  'div {',
+  '  --bird-color: red;',
+  '}',
+  'div {',
+  '  border-color: var(--bird-color);',
+  '  border-width: var(--bird-width);',
+  '}',
+  'div {',
+  '  --bird-width: 3px;',
+  '}']);
+  ApplyStyle;
+  AssertEquals('Div1.BorderColor','red',Div1.BorderColor);
+end;
+
 initialization
   RegisterTests([TTestNewCSSResolver]);