Browse Source

Validate layoutable Thickness properties to block NaN or infinite values (#20899)

* Validate layoutable Thickness properties to block NaN or infinite values

* Use double.IsFinite
pull/17825/merge
Tom Edwards 1 week ago
committed by GitHub
parent
commit
80b8509bfc
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      src/Avalonia.Base/Layout/Layoutable.cs
  2. 2
      src/Avalonia.Controls/Border.cs
  3. 4
      src/Avalonia.Controls/Chrome/WindowDrawnDecorations.cs
  4. 2
      src/Avalonia.Controls/Decorator.cs
  5. 2
      src/Avalonia.Controls/Page/Page.cs
  6. 28
      tests/Avalonia.Controls.UnitTests/BorderTests.cs

4
src/Avalonia.Base/Layout/Layoutable.cs

@ -113,7 +113,7 @@ namespace Avalonia.Layout
/// Defines the <see cref="Margin"/> property.
/// </summary>
public static readonly StyledProperty<Thickness> MarginProperty =
AvaloniaProperty.Register<Layoutable, Thickness>(nameof(Margin));
AvaloniaProperty.Register<Layoutable, Thickness>(nameof(Margin), validate: ValidateThickness);
/// <summary>
/// Defines the <see cref="HorizontalAlignment"/> property.
@ -161,6 +161,8 @@ namespace Avalonia.Layout
private static bool ValidateMinimumDimension(double value) => !double.IsPositiveInfinity(value) && ValidateMaximumDimension(value);
private static bool ValidateMaximumDimension(double value) => value >= 0;
private static bool ValidateThickness(Thickness value) => double.IsFinite(value.Left) && double.IsFinite(value.Top) && double.IsFinite(value.Right) && double.IsFinite(value.Bottom);
/// <summary>
/// Occurs when the element's effective viewport changes.
/// </summary>

2
src/Avalonia.Controls/Border.cs

@ -38,7 +38,7 @@ namespace Avalonia.Controls
/// Defines the <see cref="BorderThickness"/> property.
/// </summary>
public static readonly StyledProperty<Thickness> BorderThicknessProperty =
AvaloniaProperty.Register<Border, Thickness>(nameof(BorderThickness));
AvaloniaProperty.Register<Border, Thickness>(nameof(BorderThickness), validate: MarginProperty.ValidateValue);
/// <summary>
/// Defines the <see cref="CornerRadius"/> property.

4
src/Avalonia.Controls/Chrome/WindowDrawnDecorations.cs

@ -58,13 +58,13 @@ public class WindowDrawnDecorations : StyledElement
/// Defines the <see cref="DefaultFrameThickness"/> property.
/// </summary>
public static readonly StyledProperty<Thickness> DefaultFrameThicknessProperty =
AvaloniaProperty.Register<WindowDrawnDecorations, Thickness>(nameof(DefaultFrameThickness));
AvaloniaProperty.Register<WindowDrawnDecorations, Thickness>(nameof(DefaultFrameThickness), validate: Border.BorderThicknessProperty.ValidateValue);
/// <summary>
/// Defines the <see cref="DefaultShadowThickness"/> property.
/// </summary>
public static readonly StyledProperty<Thickness> DefaultShadowThicknessProperty =
AvaloniaProperty.Register<WindowDrawnDecorations, Thickness>(nameof(DefaultShadowThickness));
AvaloniaProperty.Register<WindowDrawnDecorations, Thickness>(nameof(DefaultShadowThickness), validate: Border.BorderThicknessProperty.ValidateValue);
/// <summary>
/// Defines the <see cref="TitleBarHeight"/> property.

2
src/Avalonia.Controls/Decorator.cs

@ -20,7 +20,7 @@ namespace Avalonia.Controls
/// Defines the <see cref="Padding"/> property.
/// </summary>
public static readonly StyledProperty<Thickness> PaddingProperty =
AvaloniaProperty.Register<Decorator, Thickness>(nameof(Padding));
AvaloniaProperty.Register<Decorator, Thickness>(nameof(Padding), validate: MarginProperty.ValidateValue);
/// <summary>
/// Initializes static members of the <see cref="Decorator"/> class.

2
src/Avalonia.Controls/Page/Page.cs

@ -17,7 +17,7 @@ namespace Avalonia.Controls
/// Defines the <see cref="SafeAreaPadding"/> property.
/// </summary>
public static readonly StyledProperty<Thickness> SafeAreaPaddingProperty =
AvaloniaProperty.Register<Page, Thickness>(nameof(SafeAreaPadding));
AvaloniaProperty.Register<Page, Thickness>(nameof(SafeAreaPadding), validate: PaddingProperty.ValidateValue);
/// <summary>
/// Defines the <see cref="Header"/> property.

28
tests/Avalonia.Controls.UnitTests/BorderTests.cs

@ -1,9 +1,6 @@
using System;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Rendering;
using Avalonia.UnitTests;
using Avalonia.VisualTree;
using Moq;
using Xunit;
namespace Avalonia.Controls.UnitTests
@ -45,14 +42,31 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new Rect(6, 6, 0, 0), content.Bounds);
}
[Fact]
public void Should_Reject_NaN_Or_Infinite_Thicknesses()
{
var target = new Border();
SetValues(target, Layoutable.MarginProperty);
SetValues(target, Decorator.PaddingProperty);
SetValues(target, Border.BorderThicknessProperty);
static void SetValues(Border target, AvaloniaProperty<Thickness> property)
{
Assert.Throws<ArgumentException>(() => target.SetValue(property, new Thickness(0, 0, 0, double.NaN)));
Assert.Throws<ArgumentException>(() => target.SetValue(property, new Thickness(0, 0, 0, double.PositiveInfinity)));
Assert.Throws<ArgumentException>(() => target.SetValue(property, new Thickness(0, 0, 0, double.NegativeInfinity)));
}
}
public class UseLayoutRounding : ScopedTestBase
{
[Fact]
public void Measure_Rounds_Padding()
{
var target = new Border
{
var target = new Border
{
Padding = new Thickness(1),
Child = new Canvas
{

Loading…
Cancel
Save