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]);