Skip to content

Commit

Permalink
Merge pull request #6916 from unoplatform/dev/cdb/bugs/wasm-getbbox
Browse files Browse the repository at this point in the history
Fixed a potential crash with GetBBox() on WASM
  • Loading branch information
carldebilly authored Aug 31, 2021
2 parents 6a91873 + bb0812a commit 39e3836
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -755,5 +755,22 @@ public void ValidateShape(string shapeName, PixelTolerance? tolerance = null)
"Rectangle_Uniform_MinWidthSmall",
"Rectangle_Uniform_Unconstrained",
};

[Test]
[AutoRetry]
public void Validate_Offscreen_Shapes()
{
Run("UITests.Windows_UI_Xaml_Shapes.Offscreen_Shapes");

_app.WaitForElement("deferredShape6");

using var screensnot = TakeScreenshot("offscreen_shapes", ignoreInSnapshotCompare: true);

var xamlShape6 = _app.GetPhysicalRect("xamlShape6");
var deferredShape6 = _app.GetPhysicalRect("deferredShape6");

ImageAssert.HasColorAt(screensnot, xamlShape6.CenterX, xamlShape6.CenterY, Color.Yellow, tolerance: 5);
ImageAssert.HasColorAt(screensnot, deferredShape6.CenterX, xamlShape6.CenterY, Color.Yellow, tolerance: 5);
}
}
}
7 changes: 7 additions & 0 deletions src/SamplesApp/UITests.Shared/UITests.Shared.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -3985,6 +3985,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Shapes\Offscreen_Shapes.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Shapes\PathStretchModes.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
Expand Down Expand Up @@ -6651,6 +6655,9 @@
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Shapes\MeasurePage.xaml.cs">
<DependentUpon>MeasurePage.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Shapes\Offscreen_Shapes.xaml.cs">
<DependentUpon>Offscreen_Shapes.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Shapes\PathStretchModes.xaml.cs">
<DependentUpon>PathStretchModes.xaml</DependentUpon>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<Page
x:Class="UITests.Windows_UI_Xaml_Shapes.Offscreen_Shapes"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UITests.Windows_UI_Xaml_Shapes"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<Grid RowSpacing="6" ColumnSpacing="6" x:Name="grid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>

<TextBlock FontSize="20" HorizontalAlignment="Center" Grid.Row="0" Grid.Column="1">XAML</TextBlock>
<TextBlock FontSize="20" HorizontalAlignment="Center" Grid.Row="0" Grid.Column="2" FontStyle="Italic">Deferred</TextBlock>

<TextBlock FontSize="20" VerticalAlignment="Center" Grid.Row="1" Grid.Column="0">Ellipse</TextBlock>
<TextBlock FontSize="20" VerticalAlignment="Center" Grid.Row="2" Grid.Column="0">Line</TextBlock>
<TextBlock FontSize="20" VerticalAlignment="Center" Grid.Row="3" Grid.Column="0">Path</TextBlock>
<TextBlock FontSize="20" VerticalAlignment="Center" Grid.Row="4" Grid.Column="0">Polygon</TextBlock>
<TextBlock FontSize="20" VerticalAlignment="Center" Grid.Row="5" Grid.Column="0">Polyline</TextBlock>
<TextBlock FontSize="20" VerticalAlignment="Center" Grid.Row="6" Grid.Column="0">Rectangle</TextBlock>

<Ellipse Grid.Row="1" Grid.Column="1" Fill="Yellow" Stroke="Green" StrokeThickness="2" Width="60" Height="40" x:Name="xamlShape1" />
<Line Grid.Row="2" Grid.Column="1" Fill="Yellow" Stroke="Green" StrokeThickness="2" X2="60" Y2="30" x:Name="xamlShape2" />
<Path Grid.Row="3" Grid.Column="1" Fill="Yellow" Stroke="Green" StrokeThickness="2" Width="60" Height="40" x:Name="xamlShape3" Data="M0,0L60,40 0,40 60,0Z" />
<Polygon Grid.Row="4" Grid.Column="1" Fill="Yellow" Stroke="Green" StrokeThickness="2" Width="60" Height="40" x:Name="xamlShape4" Points="0,0 60,40 0,40" />
<Polyline Grid.Row="5" Grid.Column="1" Fill="Yellow" Stroke="Green" StrokeThickness="2" Width="60" Height="40" x:Name="xamlShape5" Points="0,0 60,40 20,30 50,10" />
<Rectangle Grid.Row="6" Grid.Column="1" Fill="Yellow" Stroke="Green" StrokeThickness="2" Width="60" Height="40" x:Name="xamlShape6" />

<TextBlock FontSize="20" Grid.Row="7" Grid.ColumnSpan="4" TextWrapping="Wrap"> The 2 columns should appear identical</TextBlock>
</Grid>
</Page>
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Markup;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;
using Uno.Extensions;
using Uno.UI.Samples.Controls;

namespace UITests.Windows_UI_Xaml_Shapes
{
[Sample("Shapes")]
public sealed partial class Offscreen_Shapes : Page
{
public Offscreen_Shapes()
{
this.InitializeComponent();

Loaded += OnLoaded;
}

private async void OnLoaded(object sender, RoutedEventArgs e)
{
Loaded -= OnLoaded;

await Task.Delay(200);

var sw = xamlShape1.Width;
var sh = xamlShape1.Height;
var sf = xamlShape1.Fill;
var ss = xamlShape1.Stroke;
var sst = xamlShape1.StrokeThickness;

await SetShape(
1,
new Ellipse
{
Width = sw,
Height = sh,
Fill = sf,
Stroke = ss,
StrokeThickness = sst
});

await SetShape(
2,
new Line
{
Fill = sf,
Stroke = ss,
StrokeThickness = sst,
X2=xamlShape2.X2,
Y2=xamlShape2.Y2
});

var p = await SetShape(
3,
new Path
{
Width = sw,
Height = sh,
Fill = sf,
Stroke = ss,
StrokeThickness = sst,
});

#if NETFX_CORE
var geometry = XamlReader.Load("<Geometry xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>M0,0L60,40 0,40 60,0Z</Geometry>") as Geometry;
#else
var geometry = (Geometry)"M0,0L60,40 0,40 60,0Z";
#endif
p.Data = geometry;

var polygon = await SetShape(
4,
new Polygon
{
Width = sw,
Height = sh,
Fill = sf,
Stroke = ss,
StrokeThickness = sst
});

polygon.Points.AddRange(xamlShape4.Points);

var polyline = await SetShape(
5,
new Polyline
{
Width = sw,
Height = sh,
Fill = sf,
Stroke = ss,
StrokeThickness = sst,
});

polyline.Points.AddRange(xamlShape5.Points);

await SetShape(
6,
new Rectangle
{
Width = sw,
Height = sh,
Fill = sf,
Stroke = ss,
StrokeThickness = sst
});

async Task<T> SetShape<T>(int row, T element)
where T : FrameworkElement
{
await Task.Yield();
element.Measure(new Size(100, 100));
element.Arrange(new Rect(0, 0, 200, 200));
await Task.Yield();
Grid.SetColumn(element, 2);
Grid.SetRow(element, row);
element.Name = $"deferredShape{row}";

grid.Children.Add(element);

return element;
}
}
}
}
49 changes: 25 additions & 24 deletions src/Uno.UI/WasmScripts/Uno.UI.js
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ var Uno;
//
// If the image fails to load, setup the splashScreen anyways with the
// proper sample.
let canvas = document.createElement('canvas');
let canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
let ctx = canvas.getContext("2d");
Expand All @@ -412,7 +412,7 @@ var Uno;
const UNO_BOOTSTRAP_WEBAPP_BASE_PATH = config.environmentVariables["UNO_BOOTSTRAP_WEBAPP_BASE_PATH"] || "";
let fullImagePath = String(UnoAppManifest.splashScreenImage);
// If the splashScreenImage image already points to the app base path, use it, otherwise we build it.
if (UNO_BOOTSTRAP_APP_BASE !== '' && fullImagePath.indexOf(UNO_BOOTSTRAP_APP_BASE) == -1) {
if (UNO_BOOTSTRAP_APP_BASE !== "" && fullImagePath.indexOf(UNO_BOOTSTRAP_APP_BASE) == -1) {
fullImagePath = `${UNO_BOOTSTRAP_WEBAPP_BASE_PATH}${UNO_BOOTSTRAP_APP_BASE}/${UnoAppManifest.splashScreenImage}`;
}
img.src = fullImagePath;
Expand Down Expand Up @@ -464,7 +464,7 @@ var Uno;
return new URLSearchParams(window.location.search).toString();
}
else {
const queryIndex = document.location.search.indexOf('?');
const queryIndex = document.location.search.indexOf("?");
if (queryIndex !== -1) {
return document.location.search.substring(queryIndex + 1);
}
Expand Down Expand Up @@ -680,7 +680,7 @@ var Uno;
const element = this.getView(elementId);
for (const name in properties) {
if (properties.hasOwnProperty(name)) {
var setVal = properties[name];
const setVal = properties[name];
if (setVal === "true") {
element[name] = true;
}
Expand All @@ -701,7 +701,7 @@ var Uno;
const params = WindowManagerSetPropertyParams.unmarshal(pParams);
const element = this.getView(params.HtmlId);
for (let i = 0; i < params.Pairs_Length; i += 2) {
var setVal = params.Pairs[i + 1];
const setVal = params.Pairs[i + 1];
if (setVal === "true") {
element[params.Pairs[i]] = true;
}
Expand Down Expand Up @@ -928,7 +928,7 @@ var Uno;
setElementTransformNative(pParams) {
const params = WindowManagerSetElementTransformParams.unmarshal(pParams);
const element = this.getView(params.HtmlId);
var style = element.style;
const style = element.style;
const matrix = `matrix(${params.M11},${params.M12},${params.M21},${params.M22},${params.M31},${params.M32})`;
style.transform = matrix;
this.setAsArranged(element);
Expand Down Expand Up @@ -1069,7 +1069,7 @@ var Uno;
// We achieve that by buffering it until the next few 'pointermove' on document for which we validate the new pointer location.
// It's common to get a move right after the leave with the same pointer's location,
// so we wait up to 3 pointer move before dropping the leave event.
var attempt = 3;
let attempt = 3;
WindowManager.current.ensurePendingLeaveEventProcessing();
WindowManager.current.processPendingLeaveEvent = (move) => {
if (!move.isOverDeep(element)) {
Expand Down Expand Up @@ -1198,9 +1198,9 @@ var Uno;
// defined in the browser settings.
// https://stackoverflow.com/questions/20110224/what-is-the-height-of-a-line-in-a-wheel-event-deltamode-dom-delta-line
if (this._wheelLineSize == undefined) {
const el = document.createElement('div');
el.style.fontSize = 'initial';
el.style.display = 'none';
const el = document.createElement("div");
el.style.fontSize = "initial";
el.style.display = "none";
document.body.appendChild(el);
const fontSize = window.getComputedStyle(el).fontSize;
document.body.removeChild(el);
Expand Down Expand Up @@ -1442,7 +1442,7 @@ var Uno;
getBBoxInternal(elementId) {
const element = this.getView(elementId);
let unconnectedRoot = null;
let cleanupUnconnectedRoot = (owner) => {
const cleanupUnconnectedRoot = (owner) => {
if (unconnectedRoot !== null) {
owner.removeChild(unconnectedRoot);
}
Expand All @@ -1451,6 +1451,7 @@ var Uno;
// On FireFox, the element needs to be connected to the DOM
// or the getBBox() will crash.
if (!element.isConnected) {
unconnectedRoot = element;
while (unconnectedRoot.parentElement) {
// Need to find the top most "unconnected" parent
// of this element
Expand Down Expand Up @@ -1515,7 +1516,7 @@ var Uno;
let parentElement = null;
let parentElementWidthHeight = null;
let unconnectedRoot = null;
let cleanupUnconnectedRoot = (owner) => {
const cleanupUnconnectedRoot = (owner) => {
if (unconnectedRoot !== null) {
owner.removeChild(unconnectedRoot);
}
Expand All @@ -1542,33 +1543,33 @@ var Uno;
const inputElement = element;
cleanupUnconnectedRoot(this.containerElement);
// Create a temporary element that will contain the input's content
var textOnlyElement = document.createElement("p");
const textOnlyElement = document.createElement("p");
textOnlyElement.style.cssText = unconstrainedStyleCssText;
textOnlyElement.innerText = inputElement.value;
textOnlyElement.className = elementClasses;
unconnectedRoot = textOnlyElement;
this.containerElement.appendChild(unconnectedRoot);
var textSize = this.measureElement(textOnlyElement);
var inputSize = this.measureElement(element);
const textSize = this.measureElement(textOnlyElement);
const inputSize = this.measureElement(element);
// Take the width of the inner text, but keep the height of the input element.
return [textSize[0], inputSize[1]];
}
else if (element instanceof HTMLTextAreaElement) {
const inputElement = element;
cleanupUnconnectedRoot(this.containerElement);
// Create a temporary element that will contain the input's content
var textOnlyElement = document.createElement("p");
const textOnlyElement = document.createElement("p");
textOnlyElement.style.cssText = unconstrainedStyleCssText;
// If the input is null or empty, add a no-width character to force the paragraph to take up one line height
// The trailing new lines are going to be ignored for measure, so we also append no-width char at the end.
textOnlyElement.innerText = inputElement.value ? (inputElement.value + "\u200B") : "\u200B";
textOnlyElement.className = elementClasses; // Note: Here we will have the uno-textBoxView class name
unconnectedRoot = textOnlyElement;
this.containerElement.appendChild(unconnectedRoot);
var textSize = this.measureElement(textOnlyElement);
const textSize = this.measureElement(textOnlyElement);
// For TextAreas, take the width and height of the inner text
const width = Math.min(textSize[0], maxWidth);
var height = Math.min(textSize[1], maxHeight);
const height = Math.min(textSize[1], maxHeight);
return [width, height];
}
else {
Expand Down Expand Up @@ -1663,20 +1664,20 @@ var Uno;
const element = this.getView(viewId);
if (element.tagName.toUpperCase() === "IMG") {
const imgElement = element;
var img = new Image();
const img = new Image();
img.onload = buildMonochromeImage;
img.src = url;
function buildMonochromeImage() {
// create a colored version of img
const c = document.createElement('canvas');
const ctx = c.getContext('2d');
const c = document.createElement("canvas");
const ctx = c.getContext("2d");
c.width = img.width;
c.height = img.height;
ctx.drawImage(img, 0, 0);
ctx.globalCompositeOperation = 'source-atop';
ctx.globalCompositeOperation = "source-atop";
ctx.fillStyle = color;
ctx.fillRect(0, 0, img.width, img.height);
ctx.globalCompositeOperation = 'source-over';
ctx.globalCompositeOperation = "source-over";
imgElement.src = c.toDataURL();
}
return "ok";
Expand Down Expand Up @@ -1897,7 +1898,7 @@ var Uno;
return handle + "";
}
numberToCssColor(color) {
return "#" + color.toString(16).padStart(8, '0');
return "#" + color.toString(16).padStart(8, "0");
}
setCursor(cssCursor) {
const unoBody = document.getElementById(this.containerElementId);
Expand Down
Loading

0 comments on commit 39e3836

Please sign in to comment.