|
@@ -35,6 +35,9 @@ uses
|
|
Classes, SysUtils, Contnrs, StrUtils, fpCSSTree;
|
|
Classes, SysUtils, Contnrs, StrUtils, fpCSSTree;
|
|
|
|
|
|
const
|
|
const
|
|
|
|
+ CSSSpecifityInvalid = -2;
|
|
|
|
+ CSSSpecifityNoMatch = -1;
|
|
|
|
+ CSSSpecifityUniversal = 0;
|
|
CSSSpecifityType = 1;
|
|
CSSSpecifityType = 1;
|
|
CSSSpecifityClass = 10; // includes attribute selectors e.g. [href]
|
|
CSSSpecifityClass = 10; // includes attribute selectors e.g. [href]
|
|
CSSSpecifityIdentifier = 100;
|
|
CSSSpecifityIdentifier = 100;
|
|
@@ -153,7 +156,7 @@ type
|
|
end;
|
|
end;
|
|
|
|
|
|
TCSSResolverOption = (
|
|
TCSSResolverOption = (
|
|
- roErrorOnUnknownName
|
|
|
|
|
|
+ croErrorOnUnknownName
|
|
);
|
|
);
|
|
TCSSResolverOptions = set of TCSSResolverOption;
|
|
TCSSResolverOptions = set of TCSSResolverOption;
|
|
|
|
|
|
@@ -346,7 +349,7 @@ function TCSSResolver.SelectorMatches(aSelector: TCSSElement;
|
|
var
|
|
var
|
|
C: TClass;
|
|
C: TClass;
|
|
begin
|
|
begin
|
|
- Result:=-1;
|
|
|
|
|
|
+ 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)
|
|
@@ -371,18 +374,22 @@ function TCSSResolver.SelectorIdentifierMatches(
|
|
var
|
|
var
|
|
TypeID: TCSSNumericalID;
|
|
TypeID: TCSSNumericalID;
|
|
begin
|
|
begin
|
|
- Result:=-1;
|
|
|
|
|
|
+ Result:=CSSSpecifityNoMatch;
|
|
TypeID:=ResolveIdentifier(Identifier,nikType);
|
|
TypeID:=ResolveIdentifier(Identifier,nikType);
|
|
if TypeID=CSSTypeID_Universal then
|
|
if TypeID=CSSTypeID_Universal then
|
|
begin
|
|
begin
|
|
// universal selector
|
|
// universal selector
|
|
- Result:=0;
|
|
|
|
- end else if TypeID<>CSSIDNone then
|
|
|
|
|
|
+ Result:=CSSSpecifityUniversal;
|
|
|
|
+ end else if TypeID=CSSIDNone then
|
|
|
|
+ begin
|
|
|
|
+ if croErrorOnUnknownName in Options then
|
|
|
|
+ DoError(20220911230224,'Unknown CSS selector type name "'+Identifier.Name+'"',Identifier);
|
|
|
|
+ Result:=CSSSpecifityInvalid;
|
|
|
|
+ end else
|
|
begin
|
|
begin
|
|
if TypeID=TestNode.GetCSSTypeID then
|
|
if TypeID=TestNode.GetCSSTypeID then
|
|
Result:=CSSSpecifityType;
|
|
Result:=CSSSpecifityType;
|
|
- end else
|
|
|
|
- DoError(20220908230426,'Unknown CSS selector type name "'+Identifier.Name+'"',Identifier);
|
|
|
|
|
|
+ end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TCSSResolver.SelectorClassNameMatches(
|
|
function TCSSResolver.SelectorClassNameMatches(
|
|
@@ -394,7 +401,7 @@ begin
|
|
if TestNode.HasCSSClass(aValue) then
|
|
if TestNode.HasCSSClass(aValue) then
|
|
Result:=CSSSpecifityClass
|
|
Result:=CSSSpecifityClass
|
|
else
|
|
else
|
|
- Result:=-1;
|
|
|
|
|
|
+ Result:=CSSSpecifityNoMatch;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TCSSResolver.SelectorPseudoClassMatches(
|
|
function TCSSResolver.SelectorPseudoClassMatches(
|
|
@@ -403,11 +410,11 @@ function TCSSResolver.SelectorPseudoClassMatches(
|
|
var
|
|
var
|
|
PseudoID: TCSSNumericalID;
|
|
PseudoID: TCSSNumericalID;
|
|
begin
|
|
begin
|
|
- Result:=-1;
|
|
|
|
|
|
+ Result:=CSSSpecifityNoMatch;
|
|
PseudoID:=ResolveIdentifier(aPseudoClass,nikPseudoAttribute);
|
|
PseudoID:=ResolveIdentifier(aPseudoClass,nikPseudoAttribute);
|
|
case PseudoID of
|
|
case PseudoID of
|
|
CSSIDNone:
|
|
CSSIDNone:
|
|
- if roErrorOnUnknownName in Options then
|
|
|
|
|
|
+ if croErrorOnUnknownName in Options then
|
|
DoError(20220911205605,'Unknown CSS selector pseudo attribute name "'+aPseudoClass.Name+'"',aPseudoClass);
|
|
DoError(20220911205605,'Unknown CSS selector pseudo attribute name "'+aPseudoClass.Name+'"',aPseudoClass);
|
|
CSSPseudoID_Root:
|
|
CSSPseudoID_Root:
|
|
if TestNode.GetCSSParent=nil then
|
|
if TestNode.GetCSSParent=nil then
|
|
@@ -436,16 +443,18 @@ begin
|
|
and (TestNode.GetCSSPreviousOfType=nil) then
|
|
and (TestNode.GetCSSPreviousOfType=nil) then
|
|
Result:=CSSSpecifityClass;
|
|
Result:=CSSSpecifityClass;
|
|
else
|
|
else
|
|
- TestNode.GetCSSPseudoAttribute(PseudoID);
|
|
|
|
|
|
+ if TestNode.GetCSSPseudoAttribute(PseudoID)<>'' then
|
|
|
|
+ Result:=CSSSpecifityClass;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TCSSResolver.SelectorStringMatches(aString: TCSSStringElement;
|
|
function TCSSResolver.SelectorStringMatches(aString: TCSSStringElement;
|
|
const TestNode: TCSSNode): TCSSSpecifity;
|
|
const TestNode: TCSSNode): TCSSSpecifity;
|
|
|
|
+// id selector #name
|
|
var
|
|
var
|
|
aValue: TCSSString;
|
|
aValue: TCSSString;
|
|
begin
|
|
begin
|
|
- Result:=-1;
|
|
|
|
|
|
+ Result:=CSSSpecifityNoMatch;
|
|
if aString.Children.Count>0 then
|
|
if aString.Children.Count>0 then
|
|
DoError(20220910113909,'Invalid CSS string selector',aString.Children[0]);
|
|
DoError(20220910113909,'Invalid CSS string selector',aString.Children[0]);
|
|
aValue:=aString.Value;
|
|
aValue:=aString.Value;
|
|
@@ -463,7 +472,7 @@ function TCSSResolver.SelectorListMatches(aList: TCSSListElement;
|
|
var
|
|
var
|
|
i: Integer;
|
|
i: Integer;
|
|
begin
|
|
begin
|
|
- Result:=-1;
|
|
|
|
|
|
+ Result:=CSSSpecifityInvalid;
|
|
writeln('TCSSResolver.SelectorListMatches ChildCount=',aList.ChildCount);
|
|
writeln('TCSSResolver.SelectorListMatches ChildCount=',aList.ChildCount);
|
|
for i:=0 to aList.ChildCount-1 do
|
|
for i:=0 to aList.ChildCount-1 do
|
|
writeln('TCSSResolver.SelectorListMatches ',i,' ',GetCSSObj(aList.Children[i]),' AsString=',aList.Children[i].AsString);
|
|
writeln('TCSSResolver.SelectorListMatches ',i,' ',GetCSSObj(aList.Children[i]),' AsString=',aList.Children[i].AsString);
|
|
@@ -476,7 +485,7 @@ var
|
|
aParent, Sibling: TCSSNode;
|
|
aParent, Sibling: TCSSNode;
|
|
aSpecifity: TCSSSpecifity;
|
|
aSpecifity: TCSSSpecifity;
|
|
begin
|
|
begin
|
|
- Result:=-1;
|
|
|
|
|
|
+ Result:=CSSSpecifityInvalid;
|
|
case aBinary.Operation of
|
|
case aBinary.Operation of
|
|
boGT:
|
|
boGT:
|
|
begin
|
|
begin
|
|
@@ -485,10 +494,10 @@ begin
|
|
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(-1);
|
|
|
|
|
|
+ exit(CSSSpecifityNoMatch);
|
|
aSpecifity:=SelectorMatches(aBinary.Left,aParent);
|
|
aSpecifity:=SelectorMatches(aBinary.Left,aParent);
|
|
if aSpecifity<0 then
|
|
if aSpecifity<0 then
|
|
- exit(-1);
|
|
|
|
|
|
+ exit(aSpecifity);
|
|
inc(Result,aSpecifity);
|
|
inc(Result,aSpecifity);
|
|
end;
|
|
end;
|
|
boPlus:
|
|
boPlus:
|
|
@@ -498,10 +507,10 @@ begin
|
|
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(-1);
|
|
|
|
|
|
+ exit(CSSSpecifityNoMatch);
|
|
aSpecifity:=SelectorMatches(aBinary.Left,Sibling);
|
|
aSpecifity:=SelectorMatches(aBinary.Left,Sibling);
|
|
if aSpecifity<0 then
|
|
if aSpecifity<0 then
|
|
- exit(-1);
|
|
|
|
|
|
+ exit(aSpecifity);
|
|
inc(Result,aSpecifity);
|
|
inc(Result,aSpecifity);
|
|
end;
|
|
end;
|
|
boTilde:
|
|
boTilde:
|
|
@@ -513,17 +522,20 @@ begin
|
|
while Sibling<>nil do
|
|
while Sibling<>nil do
|
|
begin
|
|
begin
|
|
aSpecifity:=SelectorMatches(aBinary.Left,Sibling);
|
|
aSpecifity:=SelectorMatches(aBinary.Left,Sibling);
|
|
- if aSpecifity>=0 then
|
|
|
|
|
|
+ if aSpecifity=CSSSpecifityInvalid then
|
|
|
|
+ exit(aSpecifity)
|
|
|
|
+ else if aSpecifity>=0 then
|
|
begin
|
|
begin
|
|
inc(Result,aSpecifity);
|
|
inc(Result,aSpecifity);
|
|
exit;
|
|
exit;
|
|
end;
|
|
end;
|
|
Sibling:=Sibling.GetCSSPreviousSibling;
|
|
Sibling:=Sibling.GetCSSPreviousSibling;
|
|
end;
|
|
end;
|
|
- Result:=-1;
|
|
|
|
|
|
+ Result:=CSSSpecifityNoMatch;
|
|
end;
|
|
end;
|
|
else
|
|
else
|
|
- DoError(20220910123724,'Invalid CSS binary selector '+BinaryOperators[aBinary.Operation],aBinary);
|
|
|
|
|
|
+ if croErrorOnUnknownName in Options then
|
|
|
|
+ DoError(20220910123724,'Invalid CSS binary selector '+BinaryOperators[aBinary.Operation],aBinary);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
@@ -533,15 +545,20 @@ var
|
|
El: TCSSElement;
|
|
El: TCSSElement;
|
|
C: TClass;
|
|
C: TClass;
|
|
AttrID: TCSSNumericalID;
|
|
AttrID: TCSSNumericalID;
|
|
|
|
+ {$IFDEF VerboseCSSResolver}
|
|
|
|
+ i: integer;
|
|
|
|
+ {$ENDIF}
|
|
begin
|
|
begin
|
|
- Result:=-1;
|
|
|
|
|
|
+ Result:=CSSSpecifityInvalid;
|
|
if anArray.Prefix<>nil then
|
|
if anArray.Prefix<>nil then
|
|
DoError(20220910154004,'Invalid CSS array selector prefix',anArray.Prefix);
|
|
DoError(20220910154004,'Invalid CSS array selector prefix',anArray.Prefix);
|
|
|
|
+ {$IFDEF VerboseCSSResolver}
|
|
|
|
+ writeln('TCSSResolver.SelectorArrayMatches Prefix=',GetCSSObj(anArray.Prefix),' ChildCount=',anArray.ChildCount);
|
|
|
|
+ for i:=0 to anArray.ChildCount-1 do
|
|
|
|
+ writeln('TCSSResolver.SelectorArrayMatches ',i,' ',GetCSSObj(anArray.Children[i]));
|
|
|
|
+ {$ENDIF}
|
|
if anArray.ChildCount<>1 then
|
|
if anArray.ChildCount<>1 then
|
|
DoError(20220910154033,'Invalid CSS array selector',anArray);
|
|
DoError(20220910154033,'Invalid CSS array selector',anArray);
|
|
- //writeln('TCSSResolver.SelectorArrayMatches Prefix=',GetCSSObj(anArray.Prefix),' ChildCount=',anArray.ChildCount);
|
|
|
|
- //for i:=0 to anArray.ChildCount-1 do
|
|
|
|
- // writeln('TCSSResolver.SelectorArrayMatches ',i,' ',GetCSSObj(anArray.Children[i]));
|
|
|
|
El:=anArray.Children[0];
|
|
El:=anArray.Children[0];
|
|
C:=El.ClassType;
|
|
C:=El.ClassType;
|
|
if C=TCSSIdentifierElement then
|
|
if C=TCSSIdentifierElement then
|
|
@@ -550,12 +567,14 @@ begin
|
|
AttrID:=ResolveIdentifier(TCSSIdentifierElement(El),nikAttribute);
|
|
AttrID:=ResolveIdentifier(TCSSIdentifierElement(El),nikAttribute);
|
|
case AttrID of
|
|
case AttrID of
|
|
CSSIDNone,
|
|
CSSIDNone,
|
|
- CSSAttributeID_All: ;
|
|
|
|
|
|
+ CSSAttributeID_All: Result:=CSSSpecifityNoMatch;
|
|
CSSAttributeID_ID:
|
|
CSSAttributeID_ID:
|
|
Result:=CSSSpecifityClass;
|
|
Result:=CSSSpecifityClass;
|
|
else
|
|
else
|
|
if TestNode.HasCSSAttribute(AttrID) then
|
|
if TestNode.HasCSSAttribute(AttrID) then
|
|
- Result:=CSSSpecifityClass;
|
|
|
|
|
|
+ Result:=CSSSpecifityClass
|
|
|
|
+ else
|
|
|
|
+ Result:=CSSSpecifityNoMatch;
|
|
end;
|
|
end;
|
|
end else if C=TCSSBinaryElement then
|
|
end else if C=TCSSBinaryElement then
|
|
Result:=SelectorArrayBinaryMatches(TCSSBinaryElement(El),TestNode)
|
|
Result:=SelectorArrayBinaryMatches(TCSSBinaryElement(El),TestNode)
|
|
@@ -571,15 +590,17 @@ var
|
|
LeftValue, RightValue: TCSSString;
|
|
LeftValue, RightValue: TCSSString;
|
|
C: TClass;
|
|
C: TClass;
|
|
begin
|
|
begin
|
|
- Result:=-1;
|
|
|
|
|
|
+ Result:=CSSSpecifityNoMatch;
|
|
Left:=aBinary.Left;
|
|
Left:=aBinary.Left;
|
|
if Left.ClassType<>TCSSIdentifierElement then
|
|
if Left.ClassType<>TCSSIdentifierElement then
|
|
DoError(20220910164353,'Invalid CSS array selector, expected attribute',Left);
|
|
DoError(20220910164353,'Invalid CSS array selector, expected attribute',Left);
|
|
AttrID:=ResolveIdentifier(TCSSIdentifierElement(Left),nikAttribute);
|
|
AttrID:=ResolveIdentifier(TCSSIdentifierElement(Left),nikAttribute);
|
|
|
|
+ {$IFDEF VerboseCSSResolver}
|
|
writeln('TCSSResolver.SelectorArrayBinaryMatches AttrID=',AttrID,' Value=',TCSSIdentifierElement(Left).Value);
|
|
writeln('TCSSResolver.SelectorArrayBinaryMatches AttrID=',AttrID,' Value=',TCSSIdentifierElement(Left).Value);
|
|
|
|
+ {$ENDIF}
|
|
case AttrID of
|
|
case AttrID of
|
|
CSSIDNone,
|
|
CSSIDNone,
|
|
- CSSAttributeID_All: exit;
|
|
|
|
|
|
+ CSSAttributeID_All: exit(CSSSpecifityNoMatch);
|
|
CSSAttributeID_ID:
|
|
CSSAttributeID_ID:
|
|
LeftValue:=TestNode.GetCSSID;
|
|
LeftValue:=TestNode.GetCSSID;
|
|
else
|
|
else
|
|
@@ -595,23 +616,25 @@ begin
|
|
DoError(20220910164921,'Invalid CSS array selector, expected string',Right);
|
|
DoError(20220910164921,'Invalid CSS array selector, expected string',Right);
|
|
RightValue:=ComputeValue(Right);
|
|
RightValue:=ComputeValue(Right);
|
|
|
|
|
|
|
|
+ {$IFDEF VerboseCSSResolver}
|
|
writeln('TCSSResolver.SelectorArrayBinaryMatches Left="',LeftValue,'" Right="',RightValue,'" Op=',aBinary.Operation);
|
|
writeln('TCSSResolver.SelectorArrayBinaryMatches Left="',LeftValue,'" Right="',RightValue,'" Op=',aBinary.Operation);
|
|
|
|
+ {$ENDIF}
|
|
case aBinary.Operation of
|
|
case aBinary.Operation of
|
|
boEquals:
|
|
boEquals:
|
|
- if AnsiCompareStr(LeftValue,RightValue)=0 then
|
|
|
|
|
|
+ if LeftValue=RightValue then
|
|
Result:=CSSSpecifityClass;
|
|
Result:=CSSSpecifityClass;
|
|
boSquaredEqual:
|
|
boSquaredEqual:
|
|
// begins with
|
|
// begins with
|
|
- if AnsiCompareStr(LeftStr(LeftValue,length(RightValue)),RightValue)=0 then
|
|
|
|
|
|
+ if LeftStr(LeftValue,length(RightValue))=RightValue then
|
|
Result:=CSSSpecifityClass;
|
|
Result:=CSSSpecifityClass;
|
|
boDollarEqual:
|
|
boDollarEqual:
|
|
// ends with
|
|
// ends with
|
|
- if AnsiCompareStr(RightStr(LeftValue,length(RightValue)),RightValue)=0 then
|
|
|
|
|
|
+ if RightStr(LeftValue,length(RightValue))=RightValue then
|
|
Result:=CSSSpecifityClass;
|
|
Result:=CSSSpecifityClass;
|
|
boPipeEqual:
|
|
boPipeEqual:
|
|
// equal to or starts with name-hyphen
|
|
// equal to or starts with name-hyphen
|
|
- if (AnsiCompareStr(LeftValue,RightValue)=0)
|
|
|
|
- or (AnsiCompareStr(LeftStr(LeftValue,length(RightValue)+1),RightValue+'-')=0) then
|
|
|
|
|
|
+ if (LeftValue=RightValue)
|
|
|
|
+ or (LeftStr(LeftValue,length(RightValue)+1)=RightValue+'-') then
|
|
Result:=CSSSpecifityClass;
|
|
Result:=CSSSpecifityClass;
|
|
boStarEqual:
|
|
boStarEqual:
|
|
// contains substring
|
|
// contains substring
|
|
@@ -622,9 +645,13 @@ begin
|
|
if PosWord(RightValue,LeftValue)>0 then
|
|
if PosWord(RightValue,LeftValue)>0 then
|
|
Result:=CSSSpecifityClass;
|
|
Result:=CSSSpecifityClass;
|
|
else
|
|
else
|
|
- DoError(20220910164356,'Invalid CSS array selector operator',aBinary);
|
|
|
|
|
|
+ if croErrorOnUnknownName in Options then
|
|
|
|
+ DoError(20220910164356,'Invalid CSS array selector operator',aBinary);
|
|
|
|
+ Result:=CSSSpecifityInvalid;
|
|
end;
|
|
end;
|
|
|
|
+ {$IFDEF VerboseCSSResolver}
|
|
writeln('TCSSResolver.SelectorArrayBinaryMatches Result=',Result);
|
|
writeln('TCSSResolver.SelectorArrayBinaryMatches Result=',Result);
|
|
|
|
+ {$ENDIF}
|
|
end;
|
|
end;
|
|
|
|
|
|
function TCSSResolver.ComputeValue(El: TCSSElement): TCSSString;
|
|
function TCSSResolver.ComputeValue(El: TCSSElement): TCSSString;
|
|
@@ -787,6 +814,8 @@ begin
|
|
'all': Result:=CSSAttributeID_All;
|
|
'all': Result:=CSSAttributeID_All;
|
|
end;
|
|
end;
|
|
nikPseudoAttribute:
|
|
nikPseudoAttribute:
|
|
|
|
+ begin
|
|
|
|
+ aName:=lowercase(aName); // pseudo attributes are ASCII case insensitive
|
|
case aName of
|
|
case aName of
|
|
':root': Result:=CSSPseudoID_Root;
|
|
':root': Result:=CSSPseudoID_Root;
|
|
':empty': Result:=CSSPseudoID_Empty;
|
|
':empty': Result:=CSSPseudoID_Empty;
|
|
@@ -797,6 +826,7 @@ begin
|
|
':last-of-type': Result:=CSSPseudoID_LastOfType;
|
|
':last-of-type': Result:=CSSPseudoID_LastOfType;
|
|
':only-of-type': Result:=CSSPseudoID_OnlyOfType;
|
|
':only-of-type': Result:=CSSPseudoID_OnlyOfType;
|
|
end;
|
|
end;
|
|
|
|
+ end;
|
|
end;
|
|
end;
|
|
|
|
|
|
// resolve user defined names
|
|
// resolve user defined names
|
|
@@ -805,7 +835,7 @@ begin
|
|
|
|
|
|
if Result=CSSIDNone then
|
|
if Result=CSSIDNone then
|
|
begin
|
|
begin
|
|
- if roErrorOnUnknownName in FOptions then
|
|
|
|
|
|
+ if croErrorOnUnknownName in FOptions then
|
|
DoError(20220908235919,'TCSSResolver.ResolveTypeIdentifier unknown '+CSSNumericalIDKindNames[Kind]+' "'+El.Name+'"',El);
|
|
DoError(20220908235919,'TCSSResolver.ResolveTypeIdentifier unknown '+CSSNumericalIDKindNames[Kind]+' "'+El.Name+'"',El);
|
|
end;
|
|
end;
|
|
IdentData:=TCSSIdentifierData.Create;
|
|
IdentData:=TCSSIdentifierData.Create;
|