Skip to content

Commit a83c8b3

Browse files
committed
WPF - Resize Hack for when browser switches from hidden to visible (#2779)
- Add 1 to the width/height rather than subtracts like the hack in the previous versions - Delay between resizes is now configurable via ResizeHackForIssue2279DelayInMs
1 parent 0f11331 commit a83c8b3

File tree

1 file changed

+184
-22
lines changed

1 file changed

+184
-22
lines changed

CefSharp.Wpf/ChromiumWebBrowser.cs

Lines changed: 184 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -138,12 +138,29 @@ public partial class ChromiumWebBrowser : Control, IRenderWebBrowser, IWpfWebBro
138138
/// </summary>
139139
private static bool DesignMode;
140140

141+
private bool resizeHackForIssue2779Enabled;
142+
private CefSharp.Structs.Size? resizeHackForIssue2779Size;
143+
141144
/// <summary>
142145
/// The value for disposal, if it's 1 (one) then this instance is either disposed
143146
/// or in the process of getting disposed
144147
/// </summary>
145148
private int disposeSignaled;
146149

150+
/// <summary>
151+
/// Hack to work around issue https://github.com/cefsharp/CefSharp/issues/2779
152+
/// Enabled by default
153+
/// </summary>
154+
public bool EnableResizeHackForIssue2779 { get; set; }
155+
156+
/// <summary>
157+
/// Number of miliseconds to wait after resizing the browser when it first
158+
/// becomes visible. After the delay the browser will revert to it's
159+
/// original size.
160+
/// Hack to work around issue https://github.com/cefsharp/CefSharp/issues/2779
161+
/// </summary>
162+
public int ResizeHackForIssue2279DelayInMs { get; set; }
163+
147164
/// <summary>
148165
/// Gets a value indicating whether this instance is disposed.
149166
/// </summary>
@@ -493,6 +510,9 @@ public ChromiumWebBrowser(string initialAddress)
493510
[MethodImpl(MethodImplOptions.NoInlining)]
494511
private void NoInliningConstructor()
495512
{
513+
EnableResizeHackForIssue2779 = true;
514+
ResizeHackForIssue2279DelayInMs = 50;
515+
496516
//Initialize CEF if it hasn't already been initialized
497517
if (!Cef.IsInitialized)
498518
{
@@ -759,7 +779,18 @@ Rect IRenderWebBrowser.GetViewRect()
759779
/// <returns>View Rectangle</returns>
760780
protected virtual Rect GetViewRect()
761781
{
762-
return viewRect;
782+
//Take a local copy as the value is set on a different thread,
783+
//Its possible the struct is set to null after our initial check.
784+
var resizeRect = resizeHackForIssue2779Size;
785+
786+
if (resizeRect == null)
787+
{
788+
return viewRect;
789+
}
790+
791+
var size = resizeRect.Value;
792+
793+
return new Rect(0, 0, size.Width, size.Height);
763794
}
764795

765796
bool IRenderWebBrowser.GetScreenPoint(int viewX, int viewY, out int screenX, out int screenY)
@@ -904,6 +935,11 @@ void IRenderWebBrowser.OnPaint(PaintElementType type, Rect dirtyRect, IntPtr buf
904935
/// <param name="height">height</param>
905936
protected virtual void OnPaint(bool isPopup, Rect dirtyRect, IntPtr buffer, int width, int height)
906937
{
938+
if (resizeHackForIssue2779Enabled)
939+
{
940+
return;
941+
}
942+
907943
var paint = Paint;
908944
if (paint != null)
909945
{
@@ -1632,7 +1668,7 @@ private void PresentationSourceChangedHandler(object sender, SourceChangedEventA
16321668
}
16331669
}
16341670

1635-
private void OnWindowStateChanged(object sender, EventArgs e)
1671+
private async void OnWindowStateChanged(object sender, EventArgs e)
16361672
{
16371673
var window = (Window)sender;
16381674

@@ -1643,16 +1679,54 @@ private void OnWindowStateChanged(object sender, EventArgs e)
16431679
{
16441680
if (previousWindowState == WindowState.Minimized && browser != null)
16451681
{
1646-
browser.GetHost().WasHidden(false);
1682+
await CefUiThreadRunAsync(async () =>
1683+
{
1684+
var host = browser?.GetHost();
1685+
if (host != null && !host.IsDisposed)
1686+
{
1687+
try
1688+
{
1689+
host.WasHidden(false);
1690+
1691+
await ResizeHackFor2779();
1692+
}
1693+
catch (ObjectDisposedException)
1694+
{
1695+
// Because Dispose runs in another thread there's a race condition between
1696+
// that and this code running on the CEF UI thread, so the host could be disposed
1697+
// between the check and using it. We can either synchronize access using locking
1698+
// (potentially blocking the UI thread in Dispose) or catch the extremely rare
1699+
// exception, which is what we do here
1700+
}
1701+
}
1702+
});
16471703
}
1704+
16481705
break;
16491706
}
16501707
case WindowState.Minimized:
16511708
{
1652-
if (browser != null)
1709+
await CefUiThreadRunAsync(() =>
16531710
{
1654-
browser.GetHost().WasHidden(true);
1655-
}
1711+
var host = browser?.GetHost();
1712+
if (host != null && !host.IsDisposed)
1713+
{
1714+
if (EnableResizeHackForIssue2779)
1715+
{
1716+
resizeHackForIssue2779Enabled = true;
1717+
}
1718+
1719+
try
1720+
{
1721+
host.WasHidden(true);
1722+
}
1723+
catch (ObjectDisposedException)
1724+
{
1725+
// See comment in catch in OnWindowStateChanged
1726+
}
1727+
}
1728+
});
1729+
16561730
break;
16571731
}
16581732
}
@@ -1795,6 +1869,24 @@ internal void UiThreadRunAsync(Action action, DispatcherPriority priority = Disp
17951869
}
17961870
}
17971871

1872+
protected async Task CefUiThreadRunAsync(Action action)
1873+
{
1874+
if (!IsDisposed && InternalIsBrowserInitialized())
1875+
{
1876+
if (Cef.CurrentlyOnThread(CefThreadIds.TID_UI))
1877+
{
1878+
action();
1879+
}
1880+
else
1881+
{
1882+
await Cef.UIThreadTaskFactory.StartNew(delegate
1883+
{
1884+
action();
1885+
});
1886+
}
1887+
}
1888+
}
1889+
17981890
/// <summary>
17991891
/// Runs the specific Action on the Dispatcher in an sync fashion
18001892
/// </summary>
@@ -1817,52 +1909,97 @@ private void UiThreadRunSync(Action action, DispatcherPriority priority = Dispat
18171909
/// </summary>
18181910
/// <param name="sender">The sender.</param>
18191911
/// <param name="e">The <see cref="SizeChangedEventArgs"/> instance containing the event data.</param>
1820-
private void OnActualSizeChanged(object sender, SizeChangedEventArgs e)
1912+
private async void OnActualSizeChanged(object sender, SizeChangedEventArgs e)
18211913
{
18221914
// Initialize RenderClientAdapter when WPF has calculated the actual size of current content.
18231915
CreateOffscreenBrowser(e.NewSize);
18241916

1917+
if (InternalIsBrowserInitialized())
1918+
{
1919+
await CefUiThreadRunAsync(() =>
1920+
{
1921+
SetViewRect(e);
1922+
1923+
var host = browser?.GetHost();
1924+
if (host != null && !host.IsDisposed)
1925+
{
1926+
try
1927+
{
1928+
host.WasResized();
1929+
}
1930+
catch (ObjectDisposedException)
1931+
{
1932+
// See comment in catch in OnWindowStateChanged
1933+
}
1934+
}
1935+
});
1936+
}
1937+
else
1938+
{
1939+
//If the browser hasn't been created yet then directly update the viewRect
1940+
SetViewRect(e);
1941+
}
1942+
}
1943+
1944+
private void SetViewRect(SizeChangedEventArgs e)
1945+
{
18251946
//NOTE: Previous we used Math.Ceiling to round the sizing up, we
18261947
//now set UseLayoutRounding = true; on the control so the sizes are
18271948
//already rounded to a whole number for us.
18281949
viewRect = new Rect(0, 0, (int)e.NewSize.Width, (int)e.NewSize.Height);
1829-
1830-
if (browser != null)
1831-
{
1832-
browser.GetHost().WasResized();
1833-
}
18341950
}
18351951

18361952
/// <summary>
18371953
/// Handles the <see cref="E:IsVisibleChanged" /> event.
18381954
/// </summary>
18391955
/// <param name="sender">The sender.</param>
18401956
/// <param name="args">The <see cref="DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
1841-
private void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs args)
1957+
private async void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs args)
18421958
{
18431959
var isVisible = (bool)args.NewValue;
18441960

18451961
if (browser != null)
18461962
{
1847-
var host = browser.GetHost();
1848-
host.WasHidden(!isVisible);
1963+
await CefUiThreadRunAsync(async () =>
1964+
{
1965+
var host = browser?.GetHost();
1966+
if (host != null && !host.IsDisposed)
1967+
{
1968+
try
1969+
{
1970+
host.WasHidden(!isVisible);
18491971

1850-
if (isVisible)
1972+
if (isVisible)
1973+
{
1974+
await ResizeHackFor2779();
1975+
}
1976+
else if (EnableResizeHackForIssue2779)
1977+
{
1978+
resizeHackForIssue2779Enabled = true;
1979+
}
1980+
}
1981+
catch (ObjectDisposedException)
1982+
{
1983+
// See comment in catch in OnWindowStateChanged
1984+
}
1985+
}
1986+
});
1987+
1988+
if (browser != null)
18511989
{
18521990
//Fix for #1778 - When browser becomes visible we update the zoom level
18531991
//browsers of the same origin will share the same zoomlevel and
18541992
//we need to track the update, so our ZoomLevelProperty works
18551993
//properly
1856-
host.GetZoomLevelAsync().ContinueWith(t =>
1994+
var zoomLevel = await browser.GetHost().GetZoomLevelAsync();
1995+
1996+
UiThreadRunAsync(() =>
18571997
{
18581998
if (!IsDisposed)
18591999
{
1860-
SetCurrentValue(ZoomLevelProperty, t.Result);
2000+
SetCurrentValue(ZoomLevelProperty, zoomLevel);
18612001
}
1862-
},
1863-
CancellationToken.None,
1864-
TaskContinuationOptions.OnlyOnRanToCompletion,
1865-
TaskScheduler.FromCurrentSynchronizationContext());
2002+
});
18662003
}
18672004
}
18682005
}
@@ -2494,5 +2631,30 @@ public IBrowser GetBrowser()
24942631

24952632
return browser;
24962633
}
2634+
2635+
private async Task ResizeHackFor2779()
2636+
{
2637+
if (EnableResizeHackForIssue2779)
2638+
{
2639+
var host = browser?.GetHost();
2640+
if (host != null && !host.IsDisposed)
2641+
{
2642+
resizeHackForIssue2779Size = new Structs.Size(viewRect.Width + 1, viewRect.Height + 1);
2643+
host.WasResized();
2644+
2645+
await Task.Delay(ResizeHackForIssue2279DelayInMs);
2646+
2647+
if (!host.IsDisposed)
2648+
{
2649+
resizeHackForIssue2779Size = null;
2650+
host.WasResized();
2651+
2652+
resizeHackForIssue2779Enabled = false;
2653+
2654+
host.Invalidate(PaintElementType.View);
2655+
}
2656+
}
2657+
}
2658+
}
24972659
}
24982660
}

0 commit comments

Comments
 (0)