Skip to content

Commit

Permalink
WinForms/WPF/OffScreen - Update Load(string url) behaviour
Browse files Browse the repository at this point in the history
- All three implementations updated to include an IsDisposed check which
  will simply return immediately
- OffScreen updated to match the WinForms behaviour
- WPF is more complex because of the initialAddress and Address properties
  initialAddress will be set if the browser hasn't been created, if the browserCreated
  has been created and OnAfterBrowserCreated hasn't been called then we'll set the Address
  dependency property.

NOTE: For WinForms/OffScreen there's a small window
here between CreateBrowser and OnAfterBrowserCreated
where the Address prop will be updated, though LoadUrl
won't be called.
  • Loading branch information
amaitland committed Oct 8, 2021
1 parent c226d34 commit b82fa05
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 15 deletions.
20 changes: 16 additions & 4 deletions CefSharp.OffScreen/ChromiumWebBrowser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -513,12 +513,24 @@ public Task<Bitmap> ScreenshotAsync(bool ignoreExistingScreenshot = false, Popup
/// <inheritdoc/>
public void Load(string url)
{
Address = url;
if (IsDisposed)
{
return;
}

//Destroy the frame wrapper when we're done
using (var frame = this.GetMainFrame())
//There's a small window here between CreateBrowser
//and OnAfterBrowserCreated where the Address prop
//will be updated, though LoadUrl won't be called.
if (IsBrowserInitialized)
{
using (var frame = this.GetMainFrame())
{
frame.LoadUrl(url);
}
}
else
{
frame.LoadUrl(url);
Address = url;
}
}

Expand Down
37 changes: 37 additions & 0 deletions CefSharp.Test/Wpf/WpfBrowserBasicFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,43 @@ public async Task CanLoadGoogle()
}
}

[WpfFact]
public async Task CanCallLoadUrlAsyncImmediately()
{
using (var browser = new ChromiumWebBrowser(null, string.Empty, new Size(1024, 786)))
{
var response = await browser.LoadUrlAsync("www.google.com");

Assert.True(response.Success);

var mainFrame = browser.GetMainFrame();
Assert.True(mainFrame.IsValid);
Assert.Contains("www.google", mainFrame.Url);

output.WriteLine("Url {0}", mainFrame.Url);
}
}

[WpfFact]
public async Task CanCallLoadUrlImmediately()
{
using (var browser = new ChromiumWebBrowser())
{
browser.Load("www.google.com");
browser.CreateBrowser(null, new Size(1024, 786));

var response = await browser.LoadUrlAsync();

Assert.True(response.Success);

var mainFrame = browser.GetMainFrame();
Assert.True(mainFrame.IsValid);
Assert.Contains("www.google", mainFrame.Url);

output.WriteLine("Url {0}", mainFrame.Url);
}
}

[WpfFact]
public async Task CanSetRequestContext()
{
Expand Down
8 changes: 8 additions & 0 deletions CefSharp.WinForms/ChromiumWebBrowser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,14 @@ private void InternalDispose(bool disposing)
/// <inheritdoc/>
public void Load(string url)
{
if (IsDisposed)
{
return;
}

//There's a small window here between CreateBrowser
//and OnAfterBrowserCreated where the Address prop
//will be updated, though LoadUrl won't be called.
if (IsBrowserInitialized)
{
using (var frame = this.GetMainFrame())
Expand Down
43 changes: 32 additions & 11 deletions CefSharp.Wpf/ChromiumWebBrowser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public partial class ChromiumWebBrowser : Control, IRenderWebBrowser, IWpfWebBro
/// <summary>
/// Initial address
/// </summary>
private readonly string initialAddress;
private string initialAddress;
/// <summary>
/// Has the underlying Cef Browser been created (slightly different to initialized in that
/// the browser is initialized in an async fashion)
Expand Down Expand Up @@ -1794,8 +1794,9 @@ protected virtual bool CreateOffscreenBrowser(Size size)
if (!webBrowserInternal.HasParent)
{
var windowInfo = CreateOffscreenBrowserWindowInfo(source == null ? IntPtr.Zero : source.Handle);
//Pass null in for Address and rely on Load being called in OnAfterBrowserCreated
//Workaround for issue https://github.com/cefsharp/CefSharp/issues/2300
//If initialAddress is set then we use that value, later in OnAfterBrowserCreated then we will
//call Load(url) if initial address was empty.
//Issue https://github.com/cefsharp/CefSharp/issues/2300
managedCefBrowserAdapter.CreateBrowser(windowInfo, browserSettings, requestContext, address: initialAddress);

//Dispose of BrowserSettings if we created it, if user created then they're responsible
Expand Down Expand Up @@ -2417,21 +2418,41 @@ protected override AutomationPeer OnCreateAutomationPeer()
/// <inheritdoc/>
public void Load(string url)
{
if (!InternalIsBrowserInitialized())
if(IsDisposed)
{
throw new Exception("The browser has not been initialized. Load can only be called " +
"after the underlying CEF browser is initialized (CefLifeSpanHandler::OnAfterCreated).");
return;
}

// Added null check -> binding-triggered changes of Address will lead to a nullref after Dispose has been called
// or before OnApplyTemplate has been called
if (browser != null)
//If the browser is already initialized then we can call LoadUrl directly
if (InternalIsBrowserInitialized())
{
using (var frame = browser.MainFrame)
// Added null check -> binding-triggered changes of Address will lead to a nullref after Dispose has been called
// or before OnApplyTemplate has been called
if (browser != null)
{
frame.LoadUrl(url);
using (var frame = browser.MainFrame)
{
frame.LoadUrl(url);
}
}
}
//If CreateBrowser was called and InternalIsBrowserInitialized() == false then we need to set the Address
//property so in OnAfterBrowserCreated the Url is loaded. If initialAddress was
//set then the Url set here will be ignored. If we called Load(url) then historically
//an aborted error would be raised as per https://github.com/cefsharp/CefSharp/issues/2300
//So we ignore the call for now.
else if (browserCreated)
{
UiThreadRunAsync(() =>
{
Address = url;
});
}
//Before browser created, set the intialAddress
else
{
initialAddress = url;
}
}

/// <summary>
Expand Down
1 change: 1 addition & 0 deletions CefSharp/IWebBrowser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public interface IWebBrowser : IDisposable

/// <summary>
/// Loads the specified <paramref name="url"/> in the Main Frame.
/// If <see cref="IsDisposed"/> is true then the method call will be ignored.
/// Same as calling <see cref="LoadUrl(string)"/>
/// </summary>
/// <param name="url">The URL to be loaded.</param>
Expand Down

0 comments on commit b82fa05

Please sign in to comment.