Browse Source

test: Get/SetStyleAttr

mattias 11 months ago
parent
commit
849a863305
3 changed files with 195 additions and 17 deletions
  1. 15 5
      src/base/fcl-css/fpcssresparser.pas
  2. 33 12
      src/base/fresnel.dom.pas
  3. 147 0
      tests/base/TCFresnelCSS.pas

+ 15 - 5
src/base/fcl-css/fpcssresparser.pas

@@ -556,7 +556,7 @@ type
     function IsLengthOrPercentage(const ResValue: TCSSResCompValue; AllowNegative: boolean): boolean; overload;
     function IsSymbol(Token: TCSSToken): boolean; overload;
     function GetCompString: TCSSString; overload;
-    function GetCompString(const aValue: string; const ResValue: TCSSResCompValue; EndP: PCSSChar): TCSSString; overload;
+    function GetCompString(const aValue: string; const ResValue: TCSSResCompValue): TCSSString; overload;
     // low level functions to parse attribute components
     function ReadComp(var aComp: TCSSResCompValue): boolean; // true if parsing attribute can continue
     function ReadNumber(var aComp: TCSSResCompValue): boolean;
@@ -1693,6 +1693,16 @@ begin
       SetSymbol(ctkDIV);
       exit;
     end;
+  ':':
+    begin
+      SetSymbol(ctkCOLON);
+      exit;
+    end;
+  ';':
+    begin
+      SetSymbol(ctkSEMICOLON);
+      exit;
+    end;
   'a'..'z','A'..'Z':
     if ReadIdentifier(aComp) then exit;
   '#':
@@ -1963,18 +1973,18 @@ begin
     SetString(Result,StartP,CurComp.EndP-StartP);
 end;
 
-function TCSSBaseResolver.GetCompString(const aValue: string; const ResValue: TCSSResCompValue;
-  EndP: PCSSChar): TCSSString;
+function TCSSBaseResolver.GetCompString(const aValue: string; const ResValue: TCSSResCompValue
+  ): TCSSString;
 var
   Start: PCSSChar;
 begin
   if ResValue.Kind=rvkKeyword then
     exit(CSSRegistry.Keywords[ResValue.KeywordID]);
   Start:=ResValue.StartP;
-  if (Start=PCSSChar(aValue)) and (EndP-Start = length(aValue)) then
+  if (Start=PCSSChar(aValue)) and (ResValue.EndP-Start = length(aValue)) then
     Result:=aValue
   else
-    SetString(Result,Start,EndP-Start);
+    SetString(Result,Start,ResValue.EndP-Start);
 end;
 
 procedure TCSSBaseResolver.SkipToEndOfAttribute(var p: PCSSChar);

+ 33 - 12
src/base/fresnel.dom.pas

@@ -4016,7 +4016,8 @@ begin
     StartP:=nil;
     EndP:=nil;
     repeat
-      if (not Resolver.ReadComp(aComp)) or (StartP^=';')  then break;
+      if not Resolver.ReadComp(aComp)  then break;
+      if aComp.StartP^=';'  then break;
       if Found and (StartP=nil) then
         StartP:=aComp.StartP;
       EndP:=aComp.EndP;
@@ -4035,6 +4036,8 @@ begin
 end;
 
 function TFresnelElement.SetStyleAttr(const AttrName, aValue: string): boolean;
+const
+  WhiteSpace = [' ',#9,#10,#13];
 var
   l: Integer;
   NameP: PChar;
@@ -4048,33 +4051,34 @@ begin
   NameP:=PChar(AttrName);
 
   // check if aValue is valid
-  StartP:=PChar(aValue);
-  aComp.EndP:=StartP;
+  aComp.EndP:=PChar(aValue);
   ValueEmpty:=true;
   repeat
     // read attribute name
     if not Resolver.ReadComp(aComp) then
     begin
       if aComp.Kind=rvkInvalid then
-        exit;
+        exit; // syntax error in aValue
       break;
     end else
       ValueEmpty:=false;
-    if aComp.StartP^ in [':',')','}',']'] then
-      exit; // invalid token
+    if aComp.StartP^ in [':',')','}',']',';'] then
+      exit; // invalid token in aValue
   until false;
 
   // search old value
   aComp.EndP:=PChar(FStyle);
   repeat
     // read attribute name
-    if not Resolver.ReadComp(aComp) then exit;
-    if not (aComp.Kind in [rvkKeyword,rvkKeywordUnknown]) then exit;
+    if not Resolver.ReadComp(aComp) then break;
+    if not (aComp.Kind in [rvkKeyword,rvkKeywordUnknown]) then
+      exit; // syntax error in Style
     NameStartP:=aComp.StartP;
     l:=aComp.EndP-aComp.StartP;
     Found:=(l=length(AttrName)) and CompareMem(aComp.StartP,NameP,l);
     // read colon
-    if not Resolver.ReadComp(aComp) then exit;
+    if not Resolver.ReadComp(aComp) then
+      exit; // syntax error in Style
     if aComp.StartP^<>':' then exit;
     // read value
     StartP:=nil;
@@ -4097,9 +4101,14 @@ begin
     if Found then
     begin
       p:=PChar(FStyle);
+      while EndP^ in WhiteSpace do inc(EndP);
+
       if ValueEmpty then
       begin
         // unset / delete
+        while (NameStartP>p) and (NameStartP[-1] in WhiteSpace) do
+          dec(NameStartP);
+
         if EndP=nil then
           FStyle:=LeftStr(FStyle,NameStartP-p)
         else
@@ -4110,19 +4119,31 @@ begin
           StartP:=aComp.StartP;
         if EndP=nil then
           EndP:=aComp.EndP;
+        while (StartP[-1] in WhiteSpace) do
+          dec(StartP);
+
         l:=EndP-StartP;
         if (l=length(aValue)) and CompareMem(StartP,@aValue[1],l) then
           exit(true); // no change
-        FStyle:=LeftStr(FStyle,StartP-p)+aValue+copy(FStyle,EndP-p,length(FStyle));
+        FStyle:=LeftStr(FStyle,StartP-p)+aValue+copy(FStyle,EndP-p+1,length(FStyle));
       end;
       DomChanged;
       exit(true);
     end;
   until false;
 
+  // not found
+  if ValueEmpty then
+    exit(true);
+
   // append
-  if FStyle>'' then
-    FStyle+='; '+AttrName+':'+aValue
+  if (FStyle>'') then
+  begin
+    if FStyle[length(FStyle)]=';' then
+      FStyle+=' '+AttrName+':'+aValue
+    else
+      FStyle+='; '+AttrName+':'+aValue
+  end
   else
     FStyle:=AttrName+':'+aValue;
   DomChanged;

+ 147 - 0
tests/base/TCFresnelCSS.pas

@@ -103,6 +103,23 @@ type
   published
     procedure TestEmptyViewport;
     procedure TestBody;
+    procedure TestGetStyleAttr_OneValue;
+    procedure TestGetStyleAttr_TwoValues;
+    procedure TestGetStyleAttr_OneFunction;
+    procedure TestGetStyleAttr_TwoFunctions;
+    procedure TestGetStyleAttr_NestedFunctions;
+    procedure TestSetStyleAttr_NewValueEmpty;
+    procedure TestSetStyleAttr_NewValueFirst;
+    procedure TestSetStyleAttr_NewValueAppend;
+    procedure TestSetStyleAttr_NewValueAppendSemicolon;
+    procedure TestSetStyleAttr_DeleteOnlyValue;
+    procedure TestSetStyleAttr_DeleteFirstValue;
+    procedure TestSetStyleAttr_DeleteLastValue;
+    procedure TestSetStyleAttr_DeleteMiddleValue;
+    procedure TestSetStyleAttr_ReplaceOnlyValue;
+    procedure TestSetStyleAttr_ReplaceFirstValue;
+    procedure TestSetStyleAttr_ReplaceLastValue;
+    procedure TestSetStyleAttr_ReplaceMiddleValue;
   end;
 
 implementation
@@ -517,6 +534,136 @@ begin
 
 end;
 
+procedure TTestFresnelCSS.TestGetStyleAttr_OneValue;
+begin
+  if Viewport.Style<>'' then
+    Fail('20240820190117');
+  Viewport.Style:='padding:3px';
+  AssertEquals('padding:3px',Viewport.Style);
+  AssertEquals('3px',Viewport.GetStyleAttr('padding'));
+end;
+
+procedure TTestFresnelCSS.TestGetStyleAttr_TwoValues;
+begin
+  Viewport.Style:='padding-left:3px; padding-top: 4px';
+  AssertEquals('3px',Viewport.GetStyleAttr('padding-left'));
+  AssertEquals('4px',Viewport.GetStyleAttr('padding-top'));
+end;
+
+procedure TTestFresnelCSS.TestGetStyleAttr_OneFunction;
+begin
+  Viewport.Style:='padding-left:var(--bird)';
+  AssertEquals('var(--bird)',Viewport.GetStyleAttr('padding-left'));
+end;
+
+procedure TTestFresnelCSS.TestGetStyleAttr_TwoFunctions;
+begin
+  Viewport.Style:='padding-left:var(--bird); padding-right: min(10px, 20%) ';
+  AssertEquals('var(--bird)',Viewport.GetStyleAttr('padding-left'));
+  AssertEquals('min(10px, 20%)',Viewport.GetStyleAttr('padding-right'));
+end;
+
+procedure TTestFresnelCSS.TestGetStyleAttr_NestedFunctions;
+begin
+  Viewport.Style:='padding-left: calc(var(--bird)*10%) ; padding-right: min(max(10%,3em), 20%) min(3px,5ch)';
+  AssertEquals('calc(var(--bird)*10%)',Viewport.GetStyleAttr('padding-left'));
+  AssertEquals('min(max(10%,3em), 20%) min(3px,5ch)',Viewport.GetStyleAttr('padding-right'));
+end;
+
+procedure TTestFresnelCSS.TestSetStyleAttr_NewValueEmpty;
+begin
+  if not Viewport.SetStyleAttr('padding-left','') then
+    Fail('20240820193346');
+  AssertEquals('',Viewport.Style);
+end;
+
+procedure TTestFresnelCSS.TestSetStyleAttr_NewValueFirst;
+begin
+  if not Viewport.SetStyleAttr('padding-left','3px') then
+    Fail('20240820193354');
+  AssertEquals('padding-left:3px',Viewport.Style);
+end;
+
+procedure TTestFresnelCSS.TestSetStyleAttr_NewValueAppend;
+begin
+  Viewport.Style:='padding-left:4px';
+  if not Viewport.SetStyleAttr('padding-right','7px') then
+    Fail('20240820193401');
+  AssertEquals('padding-left:4px; padding-right:7px',Viewport.Style);
+end;
+
+procedure TTestFresnelCSS.TestSetStyleAttr_NewValueAppendSemicolon;
+begin
+  Viewport.Style:='padding-left:4px ;';
+  if not Viewport.SetStyleAttr('padding-right','7px') then
+    Fail('20240820194710');
+  AssertEquals('padding-left:4px ; padding-right:7px',Viewport.Style);
+end;
+
+procedure TTestFresnelCSS.TestSetStyleAttr_DeleteOnlyValue;
+begin
+  Viewport.Style:='padding-left:4px';
+  if not Viewport.SetStyleAttr('padding-left','') then
+    Fail('20240820193844');
+  AssertEquals('',Viewport.Style);
+end;
+
+procedure TTestFresnelCSS.TestSetStyleAttr_DeleteFirstValue;
+begin
+  Viewport.Style:='padding-left:4px; padding-top:3px';
+  if not Viewport.SetStyleAttr('padding-left','') then
+    Fail('20240820193847');
+  AssertEquals('padding-top:3px',Viewport.Style);
+end;
+
+procedure TTestFresnelCSS.TestSetStyleAttr_DeleteLastValue;
+begin
+  Viewport.Style:='padding-left:4px ; padding-top:3px';
+  if not Viewport.SetStyleAttr('padding-top','') then
+    Fail('20240820194509');
+  AssertEquals('padding-left:4px ;',Viewport.Style);
+end;
+
+procedure TTestFresnelCSS.TestSetStyleAttr_DeleteMiddleValue;
+begin
+  Viewport.Style:='padding-left:4px ; padding-top:3px; padding-right: 2px';
+  if not Viewport.SetStyleAttr('padding-top','') then
+    Fail('20240820195100');
+  AssertEquals('padding-left:4px ;padding-right: 2px',Viewport.Style);
+end;
+
+procedure TTestFresnelCSS.TestSetStyleAttr_ReplaceOnlyValue;
+begin
+  Viewport.Style:='padding-left: 4px;';
+  if not Viewport.SetStyleAttr('padding-left','5em') then
+    Fail('20240820195245');
+  AssertEquals('padding-left:5em;',Viewport.Style);
+end;
+
+procedure TTestFresnelCSS.TestSetStyleAttr_ReplaceFirstValue;
+begin
+  Viewport.Style:='padding-left: 4px ; padding-top:3px';
+  if not Viewport.SetStyleAttr('padding-left','7em') then
+    Fail('20240820195924');
+  AssertEquals('padding-left:7em; padding-top:3px',Viewport.Style);
+end;
+
+procedure TTestFresnelCSS.TestSetStyleAttr_ReplaceLastValue;
+begin
+  Viewport.Style:='padding-left: 4px ; padding-top: 3px ';
+  if not Viewport.SetStyleAttr('padding-top','7em') then
+    Fail('20240820200021');
+  AssertEquals('padding-left: 4px ; padding-top:7em',Viewport.Style);
+end;
+
+procedure TTestFresnelCSS.TestSetStyleAttr_ReplaceMiddleValue;
+begin
+  Viewport.Style:='padding-left:4px ; padding-top: 3px ; padding-right: 2px';
+  if not Viewport.SetStyleAttr('padding-top','7em') then
+    Fail('20240820200135');
+  AssertEquals('padding-left:4px ; padding-top:7em; padding-right: 2px',Viewport.Style);
+end;
+
 Initialization
   RegisterTests([TTestFresnelCSS]);
 end.