|
@@ -14,14 +14,15 @@
|
|
**********************************************************************
|
|
**********************************************************************
|
|
|
|
|
|
ToDo:
|
|
ToDo:
|
|
-- descendant combinator
|
|
|
|
-- and combinator
|
|
|
|
|
|
+- replace parser invalidtoken for relational operators ctkStar, Tile, Pipe
|
|
- 'all' attribute
|
|
- 'all' attribute
|
|
|
|
+- :has()
|
|
|
|
+- surpress duplicate warnings
|
|
|
|
+- cache list of nth-of-type
|
|
- TCSSResolver.FindComputedAttribute use binary search for >8 elements
|
|
- TCSSResolver.FindComputedAttribute use binary search for >8 elements
|
|
-- CSSSpecifityInline
|
|
|
|
- namespaces
|
|
- namespaces
|
|
- layers
|
|
- layers
|
|
-
|
|
|
|
|
|
+- @rules: @media, @font-face
|
|
}
|
|
}
|
|
|
|
|
|
unit fpCSSResolver;
|
|
unit fpCSSResolver;
|
|
@@ -32,7 +33,7 @@ unit fpCSSResolver;
|
|
interface
|
|
interface
|
|
|
|
|
|
uses
|
|
uses
|
|
- Classes, SysUtils, Contnrs, StrUtils, fpCSSTree;
|
|
|
|
|
|
+ Classes, SysUtils, types, Contnrs, StrUtils, fpCSSTree;
|
|
|
|
|
|
const
|
|
const
|
|
CSSSpecifityInvalid = -2;
|
|
CSSSpecifityInvalid = -2;
|
|
@@ -51,14 +52,43 @@ const
|
|
CSSAttributeID_All = 3; // id of attribute key 'all'
|
|
CSSAttributeID_All = 3; // id of attribute key 'all'
|
|
// pseudo attribute IDs
|
|
// pseudo attribute IDs
|
|
CSSPseudoID_Root = 1; // :root
|
|
CSSPseudoID_Root = 1; // :root
|
|
- CSSPseudoID_Empty = 2; // :empty
|
|
|
|
- CSSPseudoID_FirstChild = 3; // :first-child
|
|
|
|
- CSSPseudoID_LastChild = 4; // :last-child
|
|
|
|
- CSSPseudoID_OnlyChild = 5; // :only-child
|
|
|
|
- CSSPseudoID_FirstOfType = 6; // :first-of-type
|
|
|
|
- CSSPseudoID_LastOfType = 7; // :last-of-type
|
|
|
|
- CSSPseudoID_OnlyOfType = 8; // :only-of-type
|
|
|
|
- CSSCallID_NthChild = 9; // :nth-child
|
|
|
|
|
|
+ CSSPseudoID_Empty = CSSPseudoID_Root+1; // :empty
|
|
|
|
+ CSSPseudoID_FirstChild = CSSPseudoID_Empty+1; // :first-child
|
|
|
|
+ CSSPseudoID_LastChild = CSSPseudoID_FirstChild+1; // :last-child
|
|
|
|
+ CSSPseudoID_OnlyChild = CSSPseudoID_LastChild+1; // :only-child
|
|
|
|
+ CSSPseudoID_FirstOfType = CSSPseudoID_OnlyChild+1; // :first-of-type
|
|
|
|
+ CSSPseudoID_LastOfType = CSSPseudoID_FirstOfType+1; // :last-of-type
|
|
|
|
+ CSSPseudoID_OnlyOfType = CSSPseudoID_LastOfType+1; // :only-of-type
|
|
|
|
+ CSSCallID_Not = CSSPseudoID_OnlyOfType+1; // :nth-child
|
|
|
|
+ CSSCallID_Is = CSSCallID_Not+1; // :nth-child
|
|
|
|
+ CSSCallID_Where = CSSCallID_Is+1; // :nth-child
|
|
|
|
+ CSSCallID_Has = CSSCallID_Where+1; // :nth-child
|
|
|
|
+ CSSCallID_NthChild = CSSCallID_Has+1; // :nth-child
|
|
|
|
+ CSSCallID_NthLastChild = CSSCallID_NthChild+1; // :nth-child
|
|
|
|
+ CSSCallID_NthOfType = CSSCallID_NthLastChild+1; // :nth-child
|
|
|
|
+ CSSCallID_NthLastOfType = CSSCallID_NthOfType+1; // :nth-child
|
|
|
|
+ CSSIDLast = CSSCallID_NthLastOfType;
|
|
|
|
+
|
|
|
|
+const
|
|
|
|
+ CSSPseudoNames: array[0..CSSIDLast] of string = (
|
|
|
|
+ '?',
|
|
|
|
+ ':root',
|
|
|
|
+ ':empty',
|
|
|
|
+ ':first-child',
|
|
|
|
+ ':last-child',
|
|
|
|
+ ':only-child',
|
|
|
|
+ ':first-of-type',
|
|
|
|
+ ':last-of-type',
|
|
|
|
+ ':only-of-type',
|
|
|
|
+ ':not()',
|
|
|
|
+ ':is()',
|
|
|
|
+ ':where()',
|
|
|
|
+ ':has()',
|
|
|
|
+ ':nth-child(n)',
|
|
|
|
+ ':nth-last-child(n)',
|
|
|
|
+ ':nth-of-type(n)',
|
|
|
|
+ ':nth-last-of-type(n)'
|
|
|
|
+ );
|
|
|
|
|
|
type
|
|
type
|
|
TCSSMsgID = int64;
|
|
TCSSMsgID = int64;
|
|
@@ -168,9 +198,15 @@ type
|
|
destructor Destroy; override;
|
|
destructor Destroy; override;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+ { TCSSCallNthChildParams }
|
|
|
|
+
|
|
TCSSCallNthChildParams = class
|
|
TCSSCallNthChildParams = class
|
|
Modulo: integer;
|
|
Modulo: integer;
|
|
Start: integer;
|
|
Start: integer;
|
|
|
|
+ HasOf: boolean;
|
|
|
|
+ OfSelector: TCSSElement;
|
|
|
|
+ ChildIDs: TIntegerDynArray;
|
|
|
|
+ destructor Destroy; override;
|
|
end;
|
|
end;
|
|
|
|
|
|
TCSSResolverOption = (
|
|
TCSSResolverOption = (
|
|
@@ -223,22 +259,29 @@ type
|
|
procedure ComputeRule(aRule: TCSSRuleElement); virtual;
|
|
procedure ComputeRule(aRule: TCSSRuleElement); virtual;
|
|
procedure ComputeInline(El: TCSSElement); virtual;
|
|
procedure ComputeInline(El: TCSSElement); virtual;
|
|
procedure ComputeInlineRule(aRule: TCSSRuleElement); virtual;
|
|
procedure ComputeInlineRule(aRule: TCSSRuleElement); virtual;
|
|
- function SelectorMatches(aSelector: TCSSElement; const TestNode: TCSSNode): TCSSSpecifity; virtual;
|
|
|
|
- function SelectorIdentifierMatches(Identifier: TCSSIdentifierElement; const TestNode: TCSSNode): TCSSSpecifity; virtual;
|
|
|
|
- function SelectorHashIdentifierMatches(Identifier: TCSSHashIdentifierElement; const TestNode: TCSSNode): TCSSSpecifity; virtual;
|
|
|
|
- function SelectorClassNameMatches(aClassName: TCSSClassNameElement; const TestNode: TCSSNode): TCSSSpecifity; virtual;
|
|
|
|
- function SelectorPseudoClassMatches(aPseudoClass: TCSSPseudoClassElement; var TestNode: TCSSNode): TCSSSpecifity; virtual;
|
|
|
|
- function SelectorListMatches(aList: TCSSListElement; const TestNode: TCSSNode): TCSSSpecifity; virtual;
|
|
|
|
- function SelectorBinaryMatches(aBinary: TCSSBinaryElement; const TestNode: TCSSNode): TCSSSpecifity; virtual;
|
|
|
|
- function SelectorArrayMatches(anArray: TCSSArrayElement; const TestNode: TCSSNode): TCSSSpecifity; virtual;
|
|
|
|
|
|
+ function SelectorMatches(aSelector: TCSSElement; const TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity; virtual;
|
|
|
|
+ function SelectorIdentifierMatches(Identifier: TCSSIdentifierElement; const TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity; virtual;
|
|
|
|
+ function SelectorHashIdentifierMatches(Identifier: TCSSHashIdentifierElement; const TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity; virtual;
|
|
|
|
+ function SelectorClassNameMatches(aClassName: TCSSClassNameElement; const TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity; virtual;
|
|
|
|
+ function SelectorPseudoClassMatches(aPseudoClass: TCSSPseudoClassElement; var TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity; virtual;
|
|
|
|
+ function SelectorListMatches(aList: TCSSListElement; const TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity; virtual;
|
|
|
|
+ function SelectorBinaryMatches(aBinary: TCSSBinaryElement; const TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity; virtual;
|
|
|
|
+ function SelectorArrayMatches(anArray: TCSSArrayElement; const TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity; virtual;
|
|
function SelectorArrayBinaryMatches(aBinary: TCSSBinaryElement; const TestNode: TCSSNode): TCSSSpecifity; virtual;
|
|
function SelectorArrayBinaryMatches(aBinary: TCSSBinaryElement; const TestNode: TCSSNode): TCSSSpecifity; virtual;
|
|
- function SelectorCallMatches(aCall: TCSSCallElement; const TestNode: TCSSNode): TCSSSpecifity; virtual;
|
|
|
|
- function Call_NthChild(aCall: TCSSCallElement; const TestNode: TCSSNode): TCSSSpecifity; virtual;
|
|
|
|
|
|
+ function SelectorCallMatches(aCall: TCSSCallElement; const TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity; virtual;
|
|
|
|
+ function Call_Not(aCall: TCSSCallElement; const TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity; virtual;
|
|
|
|
+ function Call_Is(aCall: TCSSCallElement; const TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity; virtual;
|
|
|
|
+ function Call_Where(aCall: TCSSCallElement; const TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity; virtual;
|
|
|
|
+ function Call_NthChild(CallID: TCSSNumericalID; aCall: TCSSCallElement; const TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity; virtual;
|
|
|
|
+ procedure CollectSiblingsOf(CallID: TCSSNumericalID; TestNode: TCSSNode;
|
|
|
|
+ Params: TCSSCallNthChildParams); virtual;
|
|
|
|
+ function GetSiblingOfIndex(SiblingIDs: TIntegerDynArray; Index: integer): integer; virtual;
|
|
function ComputeValue(El: TCSSElement): TCSSString; virtual;
|
|
function ComputeValue(El: TCSSElement): TCSSString; virtual;
|
|
function SameValueText(const A, B: TCSSString): boolean; virtual;
|
|
function SameValueText(const A, B: TCSSString): boolean; virtual;
|
|
function SameValueText(A: PChar; ALen: integer; B: PChar; BLen: integer): boolean; virtual;
|
|
function SameValueText(A: PChar; ALen: integer; B: PChar; BLen: integer): boolean; virtual;
|
|
function PosSubString(const SearchStr, Str: TCSSString): integer; virtual;
|
|
function PosSubString(const SearchStr, Str: TCSSString): integer; virtual;
|
|
function PosWord(const SearchWord, Words: TCSSString): integer; virtual;
|
|
function PosWord(const SearchWord, Words: TCSSString): integer; virtual;
|
|
|
|
+ function GetSiblingCount(aNode: TCSSNode): integer; virtual;
|
|
procedure MergeProperty(El: TCSSElement; Specifity: TCSSSpecifity); virtual;
|
|
procedure MergeProperty(El: TCSSElement; Specifity: TCSSSpecifity); virtual;
|
|
function ResolveIdentifier(El: TCSSIdentifierElement; Kind: TCSSNumericalIDKind): TCSSNumericalID; virtual;
|
|
function ResolveIdentifier(El: TCSSIdentifierElement; Kind: TCSSNumericalIDKind): TCSSNumericalID; virtual;
|
|
function ResolveCall(El: TCSSCallElement): TCSSNumericalID; virtual;
|
|
function ResolveCall(El: TCSSCallElement): TCSSNumericalID; virtual;
|
|
@@ -269,6 +312,13 @@ type
|
|
|
|
|
|
implementation
|
|
implementation
|
|
|
|
|
|
|
|
+{ TCSSCallNthChildParams }
|
|
|
|
+
|
|
|
|
+destructor TCSSCallNthChildParams.Destroy;
|
|
|
|
+begin
|
|
|
|
+ inherited Destroy;
|
|
|
|
+end;
|
|
|
|
+
|
|
{ TCSSCallData }
|
|
{ TCSSCallData }
|
|
|
|
|
|
destructor TCSSCallData.Destroy;
|
|
destructor TCSSCallData.Destroy;
|
|
@@ -385,7 +435,7 @@ begin
|
|
for i:=0 to aRule.SelectorCount-1 do
|
|
for i:=0 to aRule.SelectorCount-1 do
|
|
begin
|
|
begin
|
|
aSelector:=aRule.Selectors[i];
|
|
aSelector:=aRule.Selectors[i];
|
|
- Specifity:=SelectorMatches(aSelector,FNode);
|
|
|
|
|
|
+ Specifity:=SelectorMatches(aSelector,FNode,false);
|
|
if Specifity>BestSpecifity then
|
|
if Specifity>BestSpecifity then
|
|
BestSpecifity:=Specifity;
|
|
BestSpecifity:=Specifity;
|
|
end;
|
|
end;
|
|
@@ -420,14 +470,14 @@ begin
|
|
end;
|
|
end;
|
|
|
|
|
|
function TCSSResolver.SelectorMatches(aSelector: TCSSElement;
|
|
function TCSSResolver.SelectorMatches(aSelector: TCSSElement;
|
|
- const TestNode: TCSSNode): TCSSSpecifity;
|
|
|
|
|
|
+ const TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity;
|
|
|
|
|
|
procedure MatchPseudo;
|
|
procedure MatchPseudo;
|
|
var
|
|
var
|
|
aNode: TCSSNode;
|
|
aNode: TCSSNode;
|
|
begin
|
|
begin
|
|
aNode:=TestNode;
|
|
aNode:=TestNode;
|
|
- Result:=SelectorPseudoClassMatches(TCSSPseudoClassElement(aSelector),aNode);
|
|
|
|
|
|
+ Result:=SelectorPseudoClassMatches(TCSSPseudoClassElement(aSelector),aNode,OnlySpecifity);
|
|
end;
|
|
end;
|
|
|
|
|
|
var
|
|
var
|
|
@@ -436,27 +486,28 @@ begin
|
|
Result:=CSSSpecifityInvalid;
|
|
Result:=CSSSpecifityInvalid;
|
|
C:=aSelector.ClassType;
|
|
C:=aSelector.ClassType;
|
|
if C=TCSSIdentifierElement then
|
|
if C=TCSSIdentifierElement then
|
|
- Result:=SelectorIdentifierMatches(TCSSIdentifierElement(aSelector),TestNode)
|
|
|
|
|
|
+ Result:=SelectorIdentifierMatches(TCSSIdentifierElement(aSelector),TestNode,OnlySpecifity)
|
|
else if C=TCSSHashIdentifierElement then
|
|
else if C=TCSSHashIdentifierElement then
|
|
- Result:=SelectorHashIdentifierMatches(TCSSHashIdentifierElement(aSelector),TestNode)
|
|
|
|
|
|
+ Result:=SelectorHashIdentifierMatches(TCSSHashIdentifierElement(aSelector),TestNode,OnlySpecifity)
|
|
else if C=TCSSClassNameElement then
|
|
else if C=TCSSClassNameElement then
|
|
- Result:=SelectorClassNameMatches(TCSSClassNameElement(aSelector),TestNode)
|
|
|
|
|
|
+ Result:=SelectorClassNameMatches(TCSSClassNameElement(aSelector),TestNode,OnlySpecifity)
|
|
else if C=TCSSPseudoClassElement then
|
|
else if C=TCSSPseudoClassElement then
|
|
MatchPseudo
|
|
MatchPseudo
|
|
else if C=TCSSBinaryElement then
|
|
else if C=TCSSBinaryElement then
|
|
- Result:=SelectorBinaryMatches(TCSSBinaryElement(aSelector),TestNode)
|
|
|
|
|
|
+ Result:=SelectorBinaryMatches(TCSSBinaryElement(aSelector),TestNode,OnlySpecifity)
|
|
else if C=TCSSArrayElement then
|
|
else if C=TCSSArrayElement then
|
|
- Result:=SelectorArrayMatches(TCSSArrayElement(aSelector),TestNode)
|
|
|
|
|
|
+ Result:=SelectorArrayMatches(TCSSArrayElement(aSelector),TestNode,OnlySpecifity)
|
|
else if C=TCSSListElement then
|
|
else if C=TCSSListElement then
|
|
- Result:=SelectorListMatches(TCSSListElement(aSelector),TestNode)
|
|
|
|
|
|
+ Result:=SelectorListMatches(TCSSListElement(aSelector),TestNode,OnlySpecifity)
|
|
else if C=TCSSCallElement then
|
|
else if C=TCSSCallElement then
|
|
- Result:=SelectorCallMatches(TCSSCallElement(aSelector),TestNode)
|
|
|
|
|
|
+ Result:=SelectorCallMatches(TCSSCallElement(aSelector),TestNode,OnlySpecifity)
|
|
else
|
|
else
|
|
Log(etError,20220908230152,'Unknown CSS selector element',aSelector);
|
|
Log(etError,20220908230152,'Unknown CSS selector element',aSelector);
|
|
end;
|
|
end;
|
|
|
|
|
|
function TCSSResolver.SelectorIdentifierMatches(
|
|
function TCSSResolver.SelectorIdentifierMatches(
|
|
- Identifier: TCSSIdentifierElement; const TestNode: TCSSNode): TCSSSpecifity;
|
|
|
|
|
|
+ Identifier: TCSSIdentifierElement; const TestNode: TCSSNode;
|
|
|
|
+ OnlySpecifity: boolean): TCSSSpecifity;
|
|
var
|
|
var
|
|
TypeID: TCSSNumericalID;
|
|
TypeID: TCSSNumericalID;
|
|
begin
|
|
begin
|
|
@@ -469,7 +520,9 @@ begin
|
|
begin
|
|
begin
|
|
// universal selector
|
|
// universal selector
|
|
Result:=CSSSpecifityUniversal;
|
|
Result:=CSSSpecifityUniversal;
|
|
- end else if TypeID=CSSIDNone then
|
|
|
|
|
|
+ end else if OnlySpecifity then
|
|
|
|
+ Result:=CSSSpecifityType
|
|
|
|
+ else if TypeID=CSSIDNone then
|
|
begin
|
|
begin
|
|
if croErrorOnUnknownName in Options then
|
|
if croErrorOnUnknownName in Options then
|
|
Log(etError,20220911230224,'Unknown CSS selector type name "'+Identifier.Name+'"',Identifier);
|
|
Log(etError,20220911230224,'Unknown CSS selector type name "'+Identifier.Name+'"',Identifier);
|
|
@@ -479,11 +532,13 @@ begin
|
|
end;
|
|
end;
|
|
|
|
|
|
function TCSSResolver.SelectorHashIdentifierMatches(
|
|
function TCSSResolver.SelectorHashIdentifierMatches(
|
|
- Identifier: TCSSHashIdentifierElement; const TestNode: TCSSNode
|
|
|
|
- ): TCSSSpecifity;
|
|
|
|
|
|
+ Identifier: TCSSHashIdentifierElement; const TestNode: TCSSNode;
|
|
|
|
+ OnlySpecifity: boolean): TCSSSpecifity;
|
|
var
|
|
var
|
|
aValue: TCSSString;
|
|
aValue: TCSSString;
|
|
begin
|
|
begin
|
|
|
|
+ if OnlySpecifity then
|
|
|
|
+ exit(CSSSpecifityIdentifier);
|
|
Result:=CSSSpecifityNoMatch;
|
|
Result:=CSSSpecifityNoMatch;
|
|
aValue:=Identifier.Value;
|
|
aValue:=Identifier.Value;
|
|
if TestNode.GetCSSID=aValue then
|
|
if TestNode.GetCSSID=aValue then
|
|
@@ -491,10 +546,13 @@ begin
|
|
end;
|
|
end;
|
|
|
|
|
|
function TCSSResolver.SelectorClassNameMatches(
|
|
function TCSSResolver.SelectorClassNameMatches(
|
|
- aClassName: TCSSClassNameElement; const TestNode: TCSSNode): TCSSSpecifity;
|
|
|
|
|
|
+ aClassName: TCSSClassNameElement; const TestNode: TCSSNode;
|
|
|
|
+ OnlySpecifity: boolean): TCSSSpecifity;
|
|
var
|
|
var
|
|
aValue: TCSSString;
|
|
aValue: TCSSString;
|
|
begin
|
|
begin
|
|
|
|
+ if OnlySpecifity then
|
|
|
|
+ exit(CSSSpecifityClass);
|
|
aValue:=aClassName.Name;
|
|
aValue:=aClassName.Name;
|
|
if TestNode.HasCSSClass(aValue) then
|
|
if TestNode.HasCSSClass(aValue) then
|
|
Result:=CSSSpecifityClass
|
|
Result:=CSSSpecifityClass
|
|
@@ -504,10 +562,13 @@ begin
|
|
end;
|
|
end;
|
|
|
|
|
|
function TCSSResolver.SelectorPseudoClassMatches(
|
|
function TCSSResolver.SelectorPseudoClassMatches(
|
|
- aPseudoClass: TCSSPseudoClassElement; var TestNode: TCSSNode): TCSSSpecifity;
|
|
|
|
|
|
+ aPseudoClass: TCSSPseudoClassElement; var TestNode: TCSSNode;
|
|
|
|
+ OnlySpecifity: boolean): TCSSSpecifity;
|
|
var
|
|
var
|
|
PseudoID: TCSSNumericalID;
|
|
PseudoID: TCSSNumericalID;
|
|
begin
|
|
begin
|
|
|
|
+ if OnlySpecifity then
|
|
|
|
+ exit(CSSSpecifityClass);
|
|
Result:=CSSSpecifityNoMatch;
|
|
Result:=CSSSpecifityNoMatch;
|
|
PseudoID:=ResolveIdentifier(aPseudoClass,nikPseudoAttribute);
|
|
PseudoID:=ResolveIdentifier(aPseudoClass,nikPseudoAttribute);
|
|
case PseudoID of
|
|
case PseudoID of
|
|
@@ -547,7 +608,7 @@ begin
|
|
end;
|
|
end;
|
|
|
|
|
|
function TCSSResolver.SelectorListMatches(aList: TCSSListElement;
|
|
function TCSSResolver.SelectorListMatches(aList: TCSSListElement;
|
|
- const TestNode: TCSSNode): TCSSSpecifity;
|
|
|
|
|
|
+ const TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity;
|
|
var
|
|
var
|
|
i: Integer;
|
|
i: Integer;
|
|
El: TCSSElement;
|
|
El: TCSSElement;
|
|
@@ -568,12 +629,17 @@ begin
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
C:=El.ClassType;
|
|
C:=El.ClassType;
|
|
if (C=TCSSIdentifierElement) and (i>0) then
|
|
if (C=TCSSIdentifierElement) and (i>0) then
|
|
- Log(etError,20220914163218,'Type selector must be first',aList)
|
|
|
|
|
|
+ begin
|
|
|
|
+ if OnlySpecifity then
|
|
|
|
+ exit(0);
|
|
|
|
+ Log(etWarning,20220914163218,'Type selector must be first',aList);
|
|
|
|
+ exit(CSSSpecifityInvalid);
|
|
|
|
+ end
|
|
else if C=TCSSPseudoClassElement then
|
|
else if C=TCSSPseudoClassElement then
|
|
begin
|
|
begin
|
|
- Specifity:=SelectorPseudoClassMatches(TCSSPseudoClassElement(El),aNode);
|
|
|
|
|
|
+ Specifity:=SelectorPseudoClassMatches(TCSSPseudoClassElement(El),aNode,OnlySpecifity);
|
|
end else
|
|
end else
|
|
- Specifity:=SelectorMatches(El,aNode);
|
|
|
|
|
|
+ Specifity:=SelectorMatches(El,aNode,OnlySpecifity);
|
|
if Specifity<0 then
|
|
if Specifity<0 then
|
|
exit(Specifity);
|
|
exit(Specifity);
|
|
inc(Result,Specifity);
|
|
inc(Result,Specifity);
|
|
@@ -581,22 +647,29 @@ begin
|
|
end;
|
|
end;
|
|
|
|
|
|
function TCSSResolver.SelectorBinaryMatches(aBinary: TCSSBinaryElement;
|
|
function TCSSResolver.SelectorBinaryMatches(aBinary: TCSSBinaryElement;
|
|
- const TestNode: TCSSNode): TCSSSpecifity;
|
|
|
|
|
|
+ const TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity;
|
|
var
|
|
var
|
|
aParent, Sibling: TCSSNode;
|
|
aParent, Sibling: TCSSNode;
|
|
aSpecifity: TCSSSpecifity;
|
|
aSpecifity: TCSSSpecifity;
|
|
begin
|
|
begin
|
|
|
|
+ if OnlySpecifity then
|
|
|
|
+ begin
|
|
|
|
+ Result:=SelectorMatches(aBinary.Left,TestNode,true);
|
|
|
|
+ inc(Result,SelectorMatches(aBinary.Right,TestNode,true));
|
|
|
|
+ exit;
|
|
|
|
+ end;
|
|
|
|
+
|
|
Result:=CSSSpecifityInvalid;
|
|
Result:=CSSSpecifityInvalid;
|
|
case aBinary.Operation of
|
|
case aBinary.Operation of
|
|
boGT:
|
|
boGT:
|
|
begin
|
|
begin
|
|
// child combinator >
|
|
// child combinator >
|
|
- Result:=SelectorMatches(aBinary.Right,TestNode);
|
|
|
|
|
|
+ Result:=SelectorMatches(aBinary.Right,TestNode,false);
|
|
if Result<0 then exit;
|
|
if Result<0 then exit;
|
|
aParent:=TestNode.GetCSSParent;
|
|
aParent:=TestNode.GetCSSParent;
|
|
if aParent=nil then
|
|
if aParent=nil then
|
|
exit(CSSSpecifityNoMatch);
|
|
exit(CSSSpecifityNoMatch);
|
|
- aSpecifity:=SelectorMatches(aBinary.Left,aParent);
|
|
|
|
|
|
+ aSpecifity:=SelectorMatches(aBinary.Left,aParent,false);
|
|
if aSpecifity<0 then
|
|
if aSpecifity<0 then
|
|
exit(aSpecifity);
|
|
exit(aSpecifity);
|
|
inc(Result,aSpecifity);
|
|
inc(Result,aSpecifity);
|
|
@@ -604,12 +677,12 @@ begin
|
|
boPlus:
|
|
boPlus:
|
|
begin
|
|
begin
|
|
// adjacent sibling combinator +
|
|
// adjacent sibling combinator +
|
|
- Result:=SelectorMatches(aBinary.Right,TestNode);
|
|
|
|
|
|
+ Result:=SelectorMatches(aBinary.Right,TestNode,false);
|
|
if Result<0 then exit;
|
|
if Result<0 then exit;
|
|
Sibling:=TestNode.GetCSSPreviousSibling;
|
|
Sibling:=TestNode.GetCSSPreviousSibling;
|
|
if Sibling=nil then
|
|
if Sibling=nil then
|
|
exit(CSSSpecifityNoMatch);
|
|
exit(CSSSpecifityNoMatch);
|
|
- aSpecifity:=SelectorMatches(aBinary.Left,Sibling);
|
|
|
|
|
|
+ aSpecifity:=SelectorMatches(aBinary.Left,Sibling,false);
|
|
if aSpecifity<0 then
|
|
if aSpecifity<0 then
|
|
exit(aSpecifity);
|
|
exit(aSpecifity);
|
|
inc(Result,aSpecifity);
|
|
inc(Result,aSpecifity);
|
|
@@ -617,12 +690,12 @@ begin
|
|
boTilde:
|
|
boTilde:
|
|
begin
|
|
begin
|
|
// general sibling combinator ~
|
|
// general sibling combinator ~
|
|
- Result:=SelectorMatches(aBinary.Right,TestNode);
|
|
|
|
|
|
+ Result:=SelectorMatches(aBinary.Right,TestNode,false);
|
|
if Result<0 then exit;
|
|
if Result<0 then exit;
|
|
Sibling:=TestNode.GetCSSPreviousSibling;
|
|
Sibling:=TestNode.GetCSSPreviousSibling;
|
|
while Sibling<>nil do
|
|
while Sibling<>nil do
|
|
begin
|
|
begin
|
|
- aSpecifity:=SelectorMatches(aBinary.Left,Sibling);
|
|
|
|
|
|
+ aSpecifity:=SelectorMatches(aBinary.Left,Sibling,false);
|
|
if aSpecifity=CSSSpecifityInvalid then
|
|
if aSpecifity=CSSSpecifityInvalid then
|
|
exit(aSpecifity)
|
|
exit(aSpecifity)
|
|
else if aSpecifity>=0 then
|
|
else if aSpecifity>=0 then
|
|
@@ -637,14 +710,14 @@ begin
|
|
boWhiteSpace:
|
|
boWhiteSpace:
|
|
begin
|
|
begin
|
|
// descendant combinator
|
|
// descendant combinator
|
|
- Result:=SelectorMatches(aBinary.Right,TestNode);
|
|
|
|
|
|
+ Result:=SelectorMatches(aBinary.Right,TestNode,false);
|
|
if Result<0 then exit;
|
|
if Result<0 then exit;
|
|
aParent:=TestNode;
|
|
aParent:=TestNode;
|
|
repeat
|
|
repeat
|
|
aParent:=aParent.GetCSSParent;
|
|
aParent:=aParent.GetCSSParent;
|
|
if aParent=nil then
|
|
if aParent=nil then
|
|
exit(CSSSpecifityNoMatch);
|
|
exit(CSSSpecifityNoMatch);
|
|
- aSpecifity:=SelectorMatches(aBinary.Left,aParent);
|
|
|
|
|
|
+ aSpecifity:=SelectorMatches(aBinary.Left,aParent,false);
|
|
if aSpecifity>=0 then
|
|
if aSpecifity>=0 then
|
|
begin
|
|
begin
|
|
inc(Result,aSpecifity);
|
|
inc(Result,aSpecifity);
|
|
@@ -661,7 +734,7 @@ begin
|
|
end;
|
|
end;
|
|
|
|
|
|
function TCSSResolver.SelectorArrayMatches(anArray: TCSSArrayElement;
|
|
function TCSSResolver.SelectorArrayMatches(anArray: TCSSArrayElement;
|
|
- const TestNode: TCSSNode): TCSSSpecifity;
|
|
|
|
|
|
+ const TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity;
|
|
var
|
|
var
|
|
{$IFDEF VerboseCSSResolver}
|
|
{$IFDEF VerboseCSSResolver}
|
|
i: integer;
|
|
i: integer;
|
|
@@ -672,20 +745,29 @@ var
|
|
OldStringComparison: TCSSResStringComparison;
|
|
OldStringComparison: TCSSResStringComparison;
|
|
aValue: TCSSString;
|
|
aValue: TCSSString;
|
|
begin
|
|
begin
|
|
|
|
+ if OnlySpecifity then
|
|
|
|
+ exit(CSSSpecifityClass);
|
|
|
|
+
|
|
Result:=CSSSpecifityInvalid;
|
|
Result:=CSSSpecifityInvalid;
|
|
if anArray.Prefix<>nil then
|
|
if anArray.Prefix<>nil then
|
|
- Log(etError,20220910154004,'Invalid CSS attribute selector prefix',anArray.Prefix);
|
|
|
|
|
|
+ begin
|
|
|
|
+ Log(etWarning,20220910154004,'Invalid CSS attribute selector prefix',anArray.Prefix);
|
|
|
|
+ exit;
|
|
|
|
+ end;
|
|
{$IFDEF VerboseCSSResolver}
|
|
{$IFDEF VerboseCSSResolver}
|
|
writeln('TCSSResolver.SelectorArrayMatches Prefix=',GetCSSObj(anArray.Prefix),' ChildCount=',anArray.ChildCount);
|
|
writeln('TCSSResolver.SelectorArrayMatches Prefix=',GetCSSObj(anArray.Prefix),' ChildCount=',anArray.ChildCount);
|
|
for i:=0 to anArray.ChildCount-1 do
|
|
for i:=0 to anArray.ChildCount-1 do
|
|
writeln('TCSSResolver.SelectorArrayMatches ',i,' ',GetCSSObj(anArray.Children[i]));
|
|
writeln('TCSSResolver.SelectorArrayMatches ',i,' ',GetCSSObj(anArray.Children[i]));
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
if anArray.ChildCount<1 then
|
|
if anArray.ChildCount<1 then
|
|
- Log(etError,20220910154033,'Invalid CSS attribute selector',anArray);
|
|
|
|
|
|
+ begin
|
|
|
|
+ Log(etWarning,20220910154033,'Invalid CSS attribute selector',anArray);
|
|
|
|
+ exit;
|
|
|
|
+ end;
|
|
OldStringComparison:=StringComparison;
|
|
OldStringComparison:=StringComparison;
|
|
try
|
|
try
|
|
if anArray.ChildCount>1 then
|
|
if anArray.ChildCount>1 then
|
|
- begin
|
|
|
|
|
|
+ begin
|
|
El:=anArray.Children[1];
|
|
El:=anArray.Children[1];
|
|
C:=El.ClassType;
|
|
C:=El.ClassType;
|
|
if C=TCSSIdentifierElement then
|
|
if C=TCSSIdentifierElement then
|
|
@@ -696,13 +778,17 @@ begin
|
|
's': FStringComparison:=crscCaseSensitive;
|
|
's': FStringComparison:=crscCaseSensitive;
|
|
else
|
|
else
|
|
if croErrorOnUnknownName in Options then
|
|
if croErrorOnUnknownName in Options then
|
|
- Log(etError,20220914174409,'Invalid attribute modifier "'+aValue+'"',El);
|
|
|
|
|
|
+ Log(etError,20220914174409,'Invalid attribute modifier "'+aValue+'"',El)
|
|
|
|
+ else
|
|
|
|
+ Log(etWarning,20220918084203,'Invalid attribute modifier "'+aValue+'"',El);
|
|
end;
|
|
end;
|
|
- end else
|
|
|
|
- Log(etError,20220914173643,'Invalid CSS attribute modifier',El);
|
|
|
|
|
|
+ end else begin
|
|
|
|
+ Log(etWarning,20220914173643,'Invalid CSS attribute modifier',El);
|
|
|
|
+ exit;
|
|
end;
|
|
end;
|
|
- if (anArray.ChildCount>2) and (croErrorOnUnknownName in Options) then
|
|
|
|
- Log(etError,20220914174550,'Invalid CSS attribute modifier',anArray.Children[2]);
|
|
|
|
|
|
+ end;
|
|
|
|
+ if (anArray.ChildCount>2) then
|
|
|
|
+ Log(etWarning,20220914174550,'Invalid CSS attribute modifier',anArray.Children[2]);
|
|
|
|
|
|
El:=anArray.Children[0];
|
|
El:=anArray.Children[0];
|
|
C:=El.ClassType;
|
|
C:=El.ClassType;
|
|
@@ -728,8 +814,9 @@ begin
|
|
end;
|
|
end;
|
|
end else if C=TCSSBinaryElement then
|
|
end else if C=TCSSBinaryElement then
|
|
Result:=SelectorArrayBinaryMatches(TCSSBinaryElement(El),TestNode)
|
|
Result:=SelectorArrayBinaryMatches(TCSSBinaryElement(El),TestNode)
|
|
- else
|
|
|
|
|
|
+ else begin
|
|
Log(etError,20220910153725,'Invalid CSS array selector',El);
|
|
Log(etError,20220910153725,'Invalid CSS array selector',El);
|
|
|
|
+ end;
|
|
finally
|
|
finally
|
|
FStringComparison:=OldStringComparison;
|
|
FStringComparison:=OldStringComparison;
|
|
end;
|
|
end;
|
|
@@ -811,31 +898,121 @@ begin
|
|
end;
|
|
end;
|
|
|
|
|
|
function TCSSResolver.SelectorCallMatches(aCall: TCSSCallElement;
|
|
function TCSSResolver.SelectorCallMatches(aCall: TCSSCallElement;
|
|
- const TestNode: TCSSNode): TCSSSpecifity;
|
|
|
|
|
|
+ const TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity;
|
|
var
|
|
var
|
|
CallID: TCSSNumericalID;
|
|
CallID: TCSSNumericalID;
|
|
begin
|
|
begin
|
|
Result:=CSSSpecifityNoMatch;
|
|
Result:=CSSSpecifityNoMatch;
|
|
CallID:=ResolveCall(aCall);
|
|
CallID:=ResolveCall(aCall);
|
|
case CallID of
|
|
case CallID of
|
|
- CSSCallID_NthChild:
|
|
|
|
- Result:=Call_NthChild(aCall,TestNode);
|
|
|
|
|
|
+ CSSCallID_Not:
|
|
|
|
+ Result:=Call_Not(aCall,TestNode,OnlySpecifity);
|
|
|
|
+ CSSCallID_Is:
|
|
|
|
+ Result:=Call_Is(aCall,TestNode,OnlySpecifity);
|
|
|
|
+ CSSCallID_Where:
|
|
|
|
+ Result:=Call_Where(aCall,TestNode,OnlySpecifity);
|
|
|
|
+ CSSCallID_NthChild,CSSCallID_NthLastChild,CSSCallID_NthOfType, CSSCallID_NthLastOfType:
|
|
|
|
+ Result:=Call_NthChild(CallID,aCall,TestNode,OnlySpecifity);
|
|
else
|
|
else
|
|
- Result:=CSSSpecifityInvalid;
|
|
|
|
|
|
+ if OnlySpecifity then
|
|
|
|
+ Result:=0
|
|
|
|
+ else
|
|
|
|
+ Result:=CSSSpecifityInvalid;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
-function TCSSResolver.Call_NthChild(aCall: TCSSCallElement;
|
|
|
|
- const TestNode: TCSSNode): TCSSSpecifity;
|
|
|
|
|
|
+function TCSSResolver.Call_Not(aCall: TCSSCallElement;
|
|
|
|
+ const TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity;
|
|
|
|
+// :not(arg1, arg2, ...)
|
|
|
|
+// :not(args) has the same specifity as :not(:is(args))
|
|
|
|
+var
|
|
|
|
+ i: Integer;
|
|
|
|
+ Specifity: TCSSSpecifity;
|
|
|
|
+ HasMatch: Boolean;
|
|
|
|
+begin
|
|
|
|
+ Result:=0;
|
|
|
|
+ HasMatch:=false;
|
|
|
|
+ for i:=0 to aCall.ArgCount-1 do
|
|
|
|
+ begin
|
|
|
|
+ Specifity:=SelectorMatches(aCall.Args[i],TestNode,OnlySpecifity);
|
|
|
|
+ if Specifity>=0 then
|
|
|
|
+ HasMatch:=true
|
|
|
|
+ else begin
|
|
|
|
+ // the specifity of :is is the highest, independent of matching (forgiving)
|
|
|
|
+ Specifity:=SelectorMatches(aCall.Args[i],TestNode,true);
|
|
|
|
+ end;
|
|
|
|
+ if Specifity>Result then
|
|
|
|
+ Result:=Specifity;
|
|
|
|
+ end;
|
|
|
|
+ if OnlySpecifity then
|
|
|
|
+ // return best
|
|
|
|
+ else if HasMatch then
|
|
|
|
+ Result:=CSSSpecifityNoMatch;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TCSSResolver.Call_Is(aCall: TCSSCallElement; const TestNode: TCSSNode;
|
|
|
|
+ OnlySpecifity: boolean): TCSSSpecifity;
|
|
|
|
+var
|
|
|
|
+ i: Integer;
|
|
|
|
+ Specifity: TCSSSpecifity;
|
|
|
|
+ ok: Boolean;
|
|
|
|
+begin
|
|
|
|
+ Result:=0;
|
|
|
|
+ ok:=false;
|
|
|
|
+ for i:=0 to aCall.ArgCount-1 do
|
|
|
|
+ begin
|
|
|
|
+ Specifity:=SelectorMatches(aCall.Args[i],TestNode,OnlySpecifity);
|
|
|
|
+ if Specifity>=0 then
|
|
|
|
+ ok:=true
|
|
|
|
+ else begin
|
|
|
|
+ // the specifity of :is is the highest, independent of matching (forgiving)
|
|
|
|
+ Specifity:=SelectorMatches(aCall.Args[i],TestNode,true);
|
|
|
|
+ end;
|
|
|
|
+ if Specifity>Result then
|
|
|
|
+ Result:=Specifity;
|
|
|
|
+ end;
|
|
|
|
+ if (not ok) and (not OnlySpecifity) then
|
|
|
|
+ Result:=CSSSpecifityNoMatch;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TCSSResolver.Call_Where(aCall: TCSSCallElement;
|
|
|
|
+ const TestNode: TCSSNode; OnlySpecifity: boolean): TCSSSpecifity;
|
|
|
|
+var
|
|
|
|
+ i: Integer;
|
|
|
|
+begin
|
|
|
|
+ Result:=0;
|
|
|
|
+ if OnlySpecifity then
|
|
|
|
+ exit;
|
|
|
|
+ for i:=0 to aCall.ArgCount-1 do
|
|
|
|
+ begin
|
|
|
|
+ if SelectorMatches(aCall.Args[i],TestNode,false)>=0 then
|
|
|
|
+ // Note: :where is forgiving, so invalid arguments are ignored
|
|
|
|
+ exit;
|
|
|
|
+ end;
|
|
|
|
+ Result:=CSSSpecifityNoMatch;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TCSSResolver.Call_NthChild(CallID: TCSSNumericalID;
|
|
|
|
+ aCall: TCSSCallElement; const TestNode: TCSSNode; OnlySpecifity: boolean
|
|
|
|
+ ): TCSSSpecifity;
|
|
|
|
+
|
|
|
|
+ procedure NthWarn(const ID: TCSSMsgID; const Msg: string; PosEl: TCSSElement);
|
|
|
|
+ begin
|
|
|
|
+ Log(etWarning,ID,CSSPseudoNames[CallID]+' '+Msg,PosEl);
|
|
|
|
+ end;
|
|
|
|
+
|
|
var
|
|
var
|
|
i, ArgCount, aModulo, aStart: Integer;
|
|
i, ArgCount, aModulo, aStart: Integer;
|
|
Arg, OffsetEl: TCSSElement;
|
|
Arg, OffsetEl: TCSSElement;
|
|
Str: TCSSString;
|
|
Str: TCSSString;
|
|
- UnaryEl: TCSSUnaryElement;
|
|
|
|
|
|
+ UnaryEl, anUnary: TCSSUnaryElement;
|
|
Params: TCSSCallNthChildParams;
|
|
Params: TCSSCallNthChildParams;
|
|
CallData: TCSSCallData;
|
|
CallData: TCSSCallData;
|
|
begin
|
|
begin
|
|
- Result:=CSSSpecifityInvalid;
|
|
|
|
|
|
+ if OnlySpecifity then
|
|
|
|
+ Result:=CSSSpecifityClass
|
|
|
|
+ else
|
|
|
|
+ Result:=CSSSpecifityInvalid;
|
|
CallData:=TCSSCallData(aCall.CustomData);
|
|
CallData:=TCSSCallData(aCall.CustomData);
|
|
Params:=TCSSCallNthChildParams(CallData.Params);
|
|
Params:=TCSSCallNthChildParams(CallData.Params);
|
|
if Params=nil then
|
|
if Params=nil then
|
|
@@ -850,11 +1027,11 @@ begin
|
|
|
|
|
|
i:=0;
|
|
i:=0;
|
|
aModulo:=0;
|
|
aModulo:=0;
|
|
- aStart:=0;
|
|
|
|
|
|
+ aStart:=1;
|
|
// check step
|
|
// check step
|
|
if ArgCount<=i then
|
|
if ArgCount<=i then
|
|
begin
|
|
begin
|
|
- Log(etWarning,20220915143843,':nth-child missing arguments',aCall);
|
|
|
|
|
|
+ NthWarn(20220915143843,'missing arguments',aCall);
|
|
exit;
|
|
exit;
|
|
end;
|
|
end;
|
|
Arg:=aCall.Args[i];
|
|
Arg:=aCall.Args[i];
|
|
@@ -865,18 +1042,18 @@ begin
|
|
// check n
|
|
// check n
|
|
if ArgCount<=i then
|
|
if ArgCount<=i then
|
|
begin
|
|
begin
|
|
- Log(etWarning,20220915143843,':nth-child missing arguments',aCall);
|
|
|
|
|
|
+ NthWarn(20220915143843,'missing arguments',aCall);
|
|
exit;
|
|
exit;
|
|
end;
|
|
end;
|
|
Arg:=aCall.Args[i];
|
|
Arg:=aCall.Args[i];
|
|
if Arg.ClassType<>TCSSIdentifierElement then
|
|
if Arg.ClassType<>TCSSIdentifierElement then
|
|
begin
|
|
begin
|
|
- Log(etWarning,20220915144312,':nth-child expected n',Arg);
|
|
|
|
|
|
+ NthWarn(20220915144312,'expected n',Arg);
|
|
exit;
|
|
exit;
|
|
end;
|
|
end;
|
|
if TCSSIdentifierElement(Arg).Value<>'n' then
|
|
if TCSSIdentifierElement(Arg).Value<>'n' then
|
|
begin
|
|
begin
|
|
- Log(etWarning,20220915144359,':nth-child expected n',Arg);
|
|
|
|
|
|
+ NthWarn(20220915144359,'expected n',Arg);
|
|
exit;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
|
|
@@ -887,29 +1064,45 @@ begin
|
|
case lowercase(Str) of
|
|
case lowercase(Str) of
|
|
'even':
|
|
'even':
|
|
begin
|
|
begin
|
|
- writeln('TCSSResolver.Call_NthChild EVEN');
|
|
|
|
|
|
+ //writeln('TCSSResolver.Call_NthChild EVEN');
|
|
aModulo:=2;
|
|
aModulo:=2;
|
|
aStart:=2;
|
|
aStart:=2;
|
|
end;
|
|
end;
|
|
'odd':
|
|
'odd':
|
|
begin
|
|
begin
|
|
- writeln('TCSSResolver.Call_NthChild ODD');
|
|
|
|
|
|
+ //writeln('TCSSResolver.Call_NthChild ODD');
|
|
aModulo:=2;
|
|
aModulo:=2;
|
|
- aStart:=1;
|
|
|
|
end;
|
|
end;
|
|
'n':
|
|
'n':
|
|
begin
|
|
begin
|
|
- writeln('TCSSResolver.Call_NthChild N');
|
|
|
|
|
|
+ //writeln('TCSSResolver.Call_NthChild N');
|
|
aModulo:=1;
|
|
aModulo:=1;
|
|
- aStart:=1;
|
|
|
|
end;
|
|
end;
|
|
else
|
|
else
|
|
- Log(etWarning,20220915150332,':nth-child expected multiplier',Arg);
|
|
|
|
|
|
+ NthWarn(20220915150332,'expected multiplier',Arg);
|
|
|
|
+ exit;
|
|
|
|
+ end
|
|
|
|
+ end else if Arg.ClassType=TCSSUnaryElement then
|
|
|
|
+ begin
|
|
|
|
+ anUnary:=TCSSUnaryElement(Arg);
|
|
|
|
+ case anUnary.Operation of
|
|
|
|
+ uoMinus: aModulo:=-1;
|
|
|
|
+ uoPlus: aModulo:=1;
|
|
|
|
+ else
|
|
|
|
+ NthWarn(20220917080309,'expected multiplier',Arg);
|
|
|
|
+ exit;
|
|
|
|
+ end;
|
|
|
|
+ if (anUnary.Right.ClassType=TCSSIdentifierElement)
|
|
|
|
+ and (SameText(TCSSIdentifierElement(anUnary.Right).Value,'n')) then
|
|
|
|
+ begin
|
|
|
|
+ // ok
|
|
|
|
+ end else begin
|
|
|
|
+ NthWarn(20220917080154,'expected multiplier',Arg);
|
|
exit;
|
|
exit;
|
|
end;
|
|
end;
|
|
end else
|
|
end else
|
|
begin
|
|
begin
|
|
- Log(etWarning,20220915144056,':nth-child expected multiplier',Arg);
|
|
|
|
|
|
+ NthWarn(20220915144056,'expected multiplier',Arg);
|
|
exit;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
|
|
@@ -923,18 +1116,18 @@ begin
|
|
//writeln('TCSSResolver.Call_NthChild UNARY ',UnaryEl.AsString);
|
|
//writeln('TCSSResolver.Call_NthChild UNARY ',UnaryEl.AsString);
|
|
if not (UnaryEl.Operation in [uoMinus,uoPlus]) then
|
|
if not (UnaryEl.Operation in [uoMinus,uoPlus]) then
|
|
begin
|
|
begin
|
|
- Log(etWarning,20220915151422,':nth-child unexpected offset',UnaryEl);
|
|
|
|
|
|
+ NthWarn(20220915151422,'unexpected offset',UnaryEl);
|
|
exit;
|
|
exit;
|
|
end;
|
|
end;
|
|
OffsetEl:=UnaryEl.Right;
|
|
OffsetEl:=UnaryEl.Right;
|
|
if OffsetEl=nil then
|
|
if OffsetEl=nil then
|
|
begin
|
|
begin
|
|
- Log(etWarning,20220915151511,':nth-child unexpected offset',UnaryEl);
|
|
|
|
|
|
+ NthWarn(20220915151511,'unexpected offset',UnaryEl);
|
|
exit;
|
|
exit;
|
|
end;
|
|
end;
|
|
if OffsetEl.ClassType<>TCSSIntegerElement then
|
|
if OffsetEl.ClassType<>TCSSIntegerElement then
|
|
begin
|
|
begin
|
|
- Log(etWarning,20220915151718,':nth-child unexpected offset',OffsetEl);
|
|
|
|
|
|
+ NthWarn(20220915151718,'unexpected offset',OffsetEl);
|
|
exit;
|
|
exit;
|
|
end;
|
|
end;
|
|
aStart:=TCSSIntegerElement(OffsetEl).Value;
|
|
aStart:=TCSSIntegerElement(OffsetEl).Value;
|
|
@@ -942,7 +1135,7 @@ begin
|
|
aStart:=-aStart;
|
|
aStart:=-aStart;
|
|
end else
|
|
end else
|
|
begin
|
|
begin
|
|
- Log(etWarning,20220915150851,':nth-child expected offset',Arg);
|
|
|
|
|
|
+ NthWarn(20220915150851,'expected offset',Arg);
|
|
exit;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
@@ -951,27 +1144,139 @@ begin
|
|
CallData.Params:=Params;
|
|
CallData.Params:=Params;
|
|
Params.Modulo:=aModulo;
|
|
Params.Modulo:=aModulo;
|
|
Params.Start:=aStart;
|
|
Params.Start:=aStart;
|
|
|
|
+
|
|
|
|
+ inc(i);
|
|
|
|
+ if (i<ArgCount) then
|
|
|
|
+ begin
|
|
|
|
+ Arg:=aCall.Args[i];
|
|
|
|
+ if (Arg.ClassType=TCSSIdentifierElement)
|
|
|
|
+ and (SameText(TCSSIdentifierElement(Arg).Value,'of')) then
|
|
|
|
+ begin
|
|
|
|
+ // An+B of Selector
|
|
|
|
+ inc(i);
|
|
|
|
+ if i=ArgCount then
|
|
|
|
+ begin
|
|
|
|
+ NthWarn(20220915150851,'expected selector',Arg);
|
|
|
|
+ exit;
|
|
|
|
+ end;
|
|
|
|
+ Arg:=aCall.Args[i];
|
|
|
|
+ Params.HasOf:=true;
|
|
|
|
+ Params.OfSelector:=Arg;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ if (CallID in [CSSCallID_NthOfType,CSSCallID_NthLastOfType]) then
|
|
|
|
+ Params.HasOf:=true;
|
|
end else begin
|
|
end else begin
|
|
aModulo:=Params.Modulo;
|
|
aModulo:=Params.Modulo;
|
|
aStart:=Params.Start;
|
|
aStart:=Params.Start;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+ if OnlySpecifity then
|
|
|
|
+ begin
|
|
|
|
+ if Params.OfSelector<>nil then
|
|
|
|
+ inc(Result,SelectorMatches(Params.OfSelector,TestNode,true));
|
|
|
|
+ exit;
|
|
|
|
+ end;
|
|
|
|
+
|
|
Result:=CSSSpecifityNoMatch;
|
|
Result:=CSSSpecifityNoMatch;
|
|
- if aModulo<1 then
|
|
|
|
|
|
+ if aModulo=0 then
|
|
exit;
|
|
exit;
|
|
i:=TestNode.GetCSSIndex;
|
|
i:=TestNode.GetCSSIndex;
|
|
|
|
+ if Params.HasOf then
|
|
|
|
+ begin
|
|
|
|
+ // ToDo: cache
|
|
|
|
+ CollectSiblingsOf(CallID,TestNode,Params);
|
|
|
|
+ i:=GetSiblingOfIndex(Params.ChildIDs,i);
|
|
|
|
+ end;
|
|
|
|
+ {$IFDEF VerboseCSSResolver}
|
|
|
|
+ //writeln('TCSSResolver.Call_NthChild CallID=',CallID,' ',aModulo,' * N + ',aStart,' Index=',TestNode.GetCSSIndex,' i=',i,' HasOf=',Params.HasOf,' OfChildCount=',length(Params.ChildIDs));
|
|
|
|
+ {$ENDIF}
|
|
if i<0 then
|
|
if i<0 then
|
|
exit;
|
|
exit;
|
|
- i:=i+1-aStart;
|
|
|
|
|
|
+ if CallID in [CSSCallID_NthLastChild,CSSCallID_NthLastOfType] then
|
|
|
|
+ begin
|
|
|
|
+ if Params.HasOf then
|
|
|
|
+ i:=length(Params.ChildIDs)-i
|
|
|
|
+ else
|
|
|
|
+ i:=GetSiblingCount(TestNode)-i;
|
|
|
|
+ end else
|
|
|
|
+ begin
|
|
|
|
+ i:=i+1;
|
|
|
|
+ end;
|
|
|
|
+ dec(i,aStart);
|
|
if i mod aModulo = 0 then
|
|
if i mod aModulo = 0 then
|
|
begin
|
|
begin
|
|
i:=i div aModulo;
|
|
i:=i div aModulo;
|
|
if i>=0 then
|
|
if i>=0 then
|
|
- Result:=CSSSpecifityClass
|
|
|
|
|
|
+ Result:=CSSSpecifityClass;
|
|
end;
|
|
end;
|
|
{$IFDEF VerboseCSSResolver}
|
|
{$IFDEF VerboseCSSResolver}
|
|
- writeln('TCSSResolver.Call_NthChild ',aModulo,' * N + ',aStart,' Index=',TestNode.GetCSSIndex+1,' Result=',Result);
|
|
|
|
|
|
+ //writeln('TCSSResolver.Call_NthChild ',aModulo,' * N + ',aStart,' Index=',TestNode.GetCSSIndex+1,' Result=',Result);
|
|
|
|
+ {$ENDIF}
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TCSSResolver.CollectSiblingsOf(CallID: TCSSNumericalID;
|
|
|
|
+ TestNode: TCSSNode; Params: TCSSCallNthChildParams);
|
|
|
|
+var
|
|
|
|
+ Cnt, i: Integer;
|
|
|
|
+ TestID: TCSSNumericalID;
|
|
|
|
+ aParent, aNode: TCSSNode;
|
|
|
|
+ aSelector: TCSSElement;
|
|
|
|
+begin
|
|
|
|
+ Params.HasOf:=true;
|
|
|
|
+ aParent:=TestNode.GetCSSParent;
|
|
|
|
+ {$IFDEF VerboseCSSResolver}
|
|
|
|
+ //writeln('TCSSResolver.CollectSiblingsOf HasParent=',aParent<>nil);
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
|
|
+ if aParent=nil then exit;
|
|
|
|
+ Cnt:=aParent.GetCSSChildCount;
|
|
|
|
+ SetLength(Params.ChildIDs,Cnt);
|
|
|
|
+ TestID:=CSSIDNone;
|
|
|
|
+ if CallID in [CSSCallID_NthOfType,CSSCallID_NthLastOfType] then
|
|
|
|
+ TestID:=TestNode.GetCSSTypeID;
|
|
|
|
+ Cnt:=0;
|
|
|
|
+ {$IFDEF VerboseCSSResolver}
|
|
|
|
+ //writeln('TCSSResolver.CollectSiblingsOf Candidates=',length(Params.ChildIDs),' TestID=',TestID);
|
|
|
|
+ {$ENDIF}
|
|
|
|
+ aSelector:=Params.OfSelector;
|
|
|
|
+ for i:=0 to length(Params.ChildIDs)-1 do
|
|
|
|
+ begin
|
|
|
|
+ aNode:=aParent.GetCSSChild(i);
|
|
|
|
+ if (Testid<>CSSIDNone)
|
|
|
|
+ and (TestID<>aNode.GetCSSTypeID) then
|
|
|
|
+ continue;
|
|
|
|
+ if (aSelector<>nil) and (SelectorMatches(aSelector,aNode,false)<0) then
|
|
|
|
+ continue;
|
|
|
|
+ Params.ChildIDs[Cnt]:=i;
|
|
|
|
+ {$IFDEF VerboseCSSResolver}
|
|
|
|
+ //writeln('TCSSResolver.CollectSiblingsOf ',Cnt,'=>',i,' CSSTypeID=',aNode.GetCSSTypeID,' Sel=',GetCSSObj(aSelector));
|
|
|
|
+ {$ENDIF}
|
|
|
|
+ inc(Cnt);
|
|
|
|
+ end;
|
|
|
|
+ SetLength(Params.ChildIDs,Cnt);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TCSSResolver.GetSiblingOfIndex(SiblingIDs: TIntegerDynArray;
|
|
|
|
+ Index: integer): integer;
|
|
|
|
+// searches the position of Index in a sorted array
|
|
|
|
+var
|
|
|
|
+ l, r, m: Integer;
|
|
|
|
+begin
|
|
|
|
+ l:=0;
|
|
|
|
+ r:=length(SiblingIDs)-1;
|
|
|
|
+ while l<=r do
|
|
|
|
+ begin
|
|
|
|
+ m:=(l+r) div 2;
|
|
|
|
+ Result:=SiblingIDs[m];
|
|
|
|
+ if Index<Result then
|
|
|
|
+ r:=m-1
|
|
|
|
+ else if Index>Result then
|
|
|
|
+ l:=m+1
|
|
|
|
+ else
|
|
|
|
+ exit(m);
|
|
|
|
+ end;
|
|
|
|
+ Result:=-1;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TCSSResolver.ComputeValue(El: TCSSElement): TCSSString;
|
|
function TCSSResolver.ComputeValue(El: TCSSElement): TCSSString;
|
|
@@ -1112,6 +1417,30 @@ begin
|
|
until p>WordsLen;
|
|
until p>WordsLen;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+function TCSSResolver.GetSiblingCount(aNode: TCSSNode): integer;
|
|
|
|
+var
|
|
|
|
+ aParent, CurNode: TCSSNode;
|
|
|
|
+begin
|
|
|
|
+ if aNode=nil then
|
|
|
|
+ exit(0);
|
|
|
|
+ aParent:=aNode.GetCSSParent;
|
|
|
|
+ if aParent<>nil then
|
|
|
|
+ exit(aParent.GetCSSChildCount);
|
|
|
|
+ Result:=0;
|
|
|
|
+ CurNode:=aNode;
|
|
|
|
+ while CurNode<>nil do
|
|
|
|
+ begin
|
|
|
|
+ inc(Result);
|
|
|
|
+ CurNode:=CurNode.GetCSSPreviousSibling;
|
|
|
|
+ end;
|
|
|
|
+ CurNode:=aNode.GetCSSNextSibling;
|
|
|
|
+ while CurNode<>nil do
|
|
|
|
+ begin
|
|
|
|
+ inc(Result);
|
|
|
|
+ CurNode:=CurNode.GetCSSNextSibling;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
procedure TCSSResolver.MergeProperty(El: TCSSElement; Specifity: TCSSSpecifity);
|
|
procedure TCSSResolver.MergeProperty(El: TCSSElement; Specifity: TCSSSpecifity);
|
|
var
|
|
var
|
|
C: TClass;
|
|
C: TClass;
|
|
@@ -1244,7 +1573,14 @@ begin
|
|
Result:=CSSIDNone;
|
|
Result:=CSSIDNone;
|
|
|
|
|
|
case aName of
|
|
case aName of
|
|
|
|
+ ':not': Result:=CSSCallID_Not;
|
|
|
|
+ ':is': Result:=CSSCallID_Is;
|
|
|
|
+ ':where': Result:=CSSCallID_Where;
|
|
|
|
+ ':has': Result:=CSSCallID_Has;
|
|
':nth-child': Result:=CSSCallID_NthChild;
|
|
':nth-child': Result:=CSSCallID_NthChild;
|
|
|
|
+ ':nth-last-child': Result:=CSSCallID_NthLastChild;
|
|
|
|
+ ':nth-of-type': Result:=CSSCallID_NthOfType;
|
|
|
|
+ ':nth-last-of-type': Result:=CSSCallID_NthLastOfType;
|
|
else
|
|
else
|
|
if croErrorOnUnknownName in FOptions then
|
|
if croErrorOnUnknownName in FOptions then
|
|
Log(etError,20220914193946,'TCSSResolver.ResolveCall unknown "'+El.Name+'"',El);
|
|
Log(etError,20220914193946,'TCSSResolver.ResolveCall unknown "'+El.Name+'"',El);
|