Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ internal class AndroidNativeWebViewDialog(Action<WebViewEnvironmentRequestedEven

public IWebViewAdapter? TryGetAdapter() => AndroidWebViewDialogActivity.WebViewRegistry.Get(_webViewId);

public Color DefaultBackground { get; set; }
public Color DefaultBackground { get; set; } = Colors.White;
public string? Title { get; set; }

public bool CanUserResize { get => false; set { } }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ internal class BrowserWindowNativeWebViewDialog(Action<WebViewEnvironmentRequest
private IWebViewAdapter? _adapter;
private Action? _unsubClose;
private string? _title;
private Color _defaultBackground;
private Color _defaultBackground = Colors.White;
private bool _disposed;

public IWebViewAdapter? TryGetAdapter() => _adapter;
Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Controls.WebView.Core/Gtk/GtkInterop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ internal static partial IntPtr webkit_website_data_manager_new(
public static extern nint webkit_web_view_run_javascript_finish(IntPtr webView, IntPtr result, GError** error);

[DllImport(LibWebKit)]
public static extern void webkit_web_view_set_background_color(IntPtr webView, GdkRGBA color);
public static extern void webkit_web_view_set_background_color(IntPtr webView, GdkRGBA* color);

[DllImport(LibWebKit)]
public static extern void webkit_javascript_result_unref(IntPtr jsResult);
Expand Down
28 changes: 27 additions & 1 deletion src/Avalonia.Controls.WebView.Core/Gtk/GtkNativeWebViewDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,33 @@ internal sealed class GtkNativeWebViewDialog : INativeWebViewDialog, IGtkWebView
private bool _isShown;

private sealed class DialogGtkWebViewAdapter(GtkWebViewEnvironmentRequestedEventArgs args)
: GtkWebViewAdapter(args);
: GtkWebViewAdapter(args)
{
public override Color DefaultBackground
{
set
{
var rgba = new GdkRGBA
{
red = value.R / 255.0,
green = value.G / 255.0,
blue = value.B / 255.0,
alpha = value.A / 255.0
};
RunOnGlibThreadAsync(() =>
{
if (WebViewHandle == IntPtr.Zero)
return;

unsafe
{
var color = rgba;
webkit_web_view_set_background_color(WebViewHandle, &color);
}
});
}
}
}

private GtkNativeWebViewDialog(GtkWebViewEnvironmentRequestedEventArgs args)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,14 @@ public Uri Source

public Color DefaultBackground
{
set
{
}
set => LastDefaultBackground = value;
}

/// <summary>
/// Last value assigned to <see cref="DefaultBackground"/>, useful with unit testing.
/// </summary>
internal Color? LastDefaultBackground { get; private set; }

public string? UserAgent { get => null; set { } }

public void SizeChanged(PixelSize containerSize)
Expand Down
41 changes: 41 additions & 0 deletions src/Avalonia.Controls.WebView/NativeWebDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using AvPlatform = Avalonia.Platform;
using Core = Avalonia.Controls;
using IPlatformHandle = Avalonia.Platform.IPlatformHandle;
using Color = Avalonia.Media.Color;
using Colors = Avalonia.Media.Colors;
#if WPF
using AvaloniaUI.Xpf.WpfAbstractions;
using Window = System.Windows.Window;
Expand Down Expand Up @@ -38,6 +40,7 @@ public class NativeWebDialog : Core.IWebView, Core.IWebViewHolder, IDisposable
private bool? _initialCanUserResize;
private PixelSize? _initialSize;
private PixelPoint? _initialPosition;
private Color? _initialDefaultBackground;
private bool _disposed;
private bool _dialogInitialized;

Expand Down Expand Up @@ -276,6 +279,24 @@ public bool CanUserResize
}
}

/// <summary>
/// Gets or sets the background color of the dialog and of the webview hosted inside of it.
/// If null, the owner background is used, falling back to white.
/// </summary>
public Color? DefaultBackground
{
get => _initialDefaultBackground;
set
{
_initialDefaultBackground = value;
if (value is { } background
&& TryGetImpl() is { } impl)
{
impl.DefaultBackground = background;
}
}
}

/// <inheritdoc cref="Core.INativeWebViewDialog.Closing"/>
public event EventHandler? Closing;
/// <inheritdoc cref="Core.INativeWebViewDialog.Show()"/>
Expand All @@ -295,6 +316,12 @@ public async void Show(TopLevel owner)
{
var impl = await GetOrInitialize();

// Not stored in _initialDefaultBackground, so the owner is still resolved again on the next Show call.
if (_initialDefaultBackground is null)
{
impl.DefaultBackground = GetOwnerBackground(owner);
}

#if WPF
var avTopLevel = XpfWpfAbstraction.GetAvaloniaTopLevelForWindow(owner);
#elif AVALONIA
Expand All @@ -317,6 +344,18 @@ public async void Show(TopLevel owner)
}
}

#if WPF
private static Color GetOwnerBackground(Window owner) =>
owner.Background is System.Windows.Media.SolidColorBrush solid ?
new Color(solid.Color.A, solid.Color.R, solid.Color.G, solid.Color.B) :
Colors.White;
#elif AVALONIA
private static Color GetOwnerBackground(TopLevel owner) =>
owner.Background is Media.ISolidColorBrush solid ?
solid.Color :
Colors.White;
#endif

private async Task<Core.INativeWebViewDialog> GetOrInitialize()
{
if (TryGetImpl() is { } impl)
Expand Down Expand Up @@ -439,6 +478,8 @@ private async Task Initialize()
dialogImpl.Move(position.X, position.Y);
if (_initialSize is { } size)
dialogImpl.Resize(size.Width, size.Height);
if (_initialDefaultBackground is { } background)
dialogImpl.DefaultBackground = background;

_implTcs.SetResult(dialogImpl);

Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Controls.WebView/WebAuthenticationBroker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ private static NativeWebDialog DefaultFactory()
{
var dialog = new NativeWebDialog();
dialog.Title = "Authentication";
dialog.CanUserResize = false;
dialog.CanUserResize = true;
dialog.Resize(600, 700);
return dialog;
}
Expand Down
22 changes: 17 additions & 5 deletions src/Avalonia.Controls.WebView/WindowNativeWebViewDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ async void CompleteAdapter()

_controlHostImpl.AdapterCreated += (_, adapter) =>
{
adapter.DefaultBackground = _initialDefaultBackground ?? Colors.Transparent;
// Opaque by default, so web pages without their own background stay readable with a dark theme.
var background = _initialDefaultBackground ?? Colors.White;
adapter.DefaultBackground = background;
ApplyWindowBackground(background);
AdapterCreated?.Invoke(this, new Core.WebViewAdapterEventArgs(adapter));
};
_controlHostImpl.AdapterDestroyed += (_, adapter) => AdapterDestroyed?.Invoke(this, new Core.WebViewAdapterEventArgs(adapter));
Expand All @@ -77,17 +80,26 @@ public Color DefaultBackground
{
set
{
_initialDefaultBackground = value;
if (_controlHostImpl?.TryGetAdapter() is { } adapter)
{
adapter.DefaultBackground = value;
}
else
{
_initialDefaultBackground = value;
}
ApplyWindowBackground(value);
}
}

// The webview may be composited into the window (or transparent on macOS),
// so the window itself has to use the same color.
private void ApplyWindowBackground(Color color)
{
#if WPF
Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(color.A, color.R, color.G, color.B));
#elif AVALONIA
Background = new Media.SolidColorBrush(color);
#endif
}

public void Dispose() => Close();

event EventHandler? Core.INativeWebViewDialog.Closing
Expand Down
70 changes: 69 additions & 1 deletion tests/Avalonia.Controls.WebView.Tests/NativeWebDialogTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Avalonia.Headless.XUnit;
using Avalonia.Controls.Headless;
using Avalonia.Headless.XUnit;
using Avalonia.Media;
using System;
using System.Threading.Tasks;
using Xunit;
Expand Down Expand Up @@ -56,6 +58,72 @@ public void Should_Set_And_Reflect_Position_And_Size()
Assert.Equal(60, underlying.Position.Y);
}

[AvaloniaFact]
public async Task Should_Use_White_Background_Without_Owner()
{
var dialog = new NativeWebDialog();
dialog.Show();

await WaitForAdapterCreation(dialog);

Assert.Null(dialog.DefaultBackground);
Assert.Equal(Colors.White, GetAdapterBackground(dialog));
Assert.Equal(Colors.White, GetWindowBackground(dialog));
}

[AvaloniaFact]
public async Task Should_Use_Owner_Background_When_Not_Set()
{
var owner = new Window { Background = new SolidColorBrush(Colors.Blue) };
owner.Show();

var dialog = new NativeWebDialog();
dialog.Show(owner);

await WaitForAdapterCreation(dialog);

Assert.Null(dialog.DefaultBackground);
Assert.Equal(Colors.Blue, GetAdapterBackground(dialog));
Assert.Equal(Colors.Blue, GetWindowBackground(dialog));
}

[AvaloniaFact]
public async Task Should_Set_DefaultBackground_Before_Show()
{
var owner = new Window { Background = new SolidColorBrush(Colors.Blue) };
owner.Show();

var dialog = new NativeWebDialog();
dialog.DefaultBackground = Colors.Red;
dialog.Show(owner);

await WaitForAdapterCreation(dialog);

// Explicit value wins over the owner background.
Assert.Equal(Colors.Red, dialog.DefaultBackground);
Assert.Equal(Colors.Red, GetAdapterBackground(dialog));
Assert.Equal(Colors.Red, GetWindowBackground(dialog));
}

[AvaloniaFact]
public async Task Should_Set_DefaultBackground_After_Show()
{
var dialog = new NativeWebDialog();
dialog.Show();

await WaitForAdapterCreation(dialog);

dialog.DefaultBackground = Colors.Red;
Assert.Equal(Colors.Red, GetAdapterBackground(dialog));
Assert.Equal(Colors.Red, GetWindowBackground(dialog));
}

private static Color? GetAdapterBackground(NativeWebDialog dialog) =>
((HeadlessWebViewAdapter)dialog.TryGetWebViewPlatformHandle()!).LastDefaultBackground;

private static Color? GetWindowBackground(NativeWebDialog dialog) =>
(dialog.TryGetWindow()!.Background as ISolidColorBrush)?.Color;

[AvaloniaFact]
public void Should_Expose_Platform_Handle()
{
Expand Down
Loading