Skip to content

Commit 9140c08

Browse files
committed
WPF - Re-add Resize Hack for when browser switches from hidden to visible (#2779)
Problem still reproduces, so readding hack to release branch
1 parent 61138ec commit 9140c08

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
@@ -134,12 +134,29 @@ public partial class ChromiumWebBrowser : Control, IRenderWebBrowser, IWpfWebBro
134134
/// </summary>
135135
private static bool DesignMode;
136136

137+
private bool resizeHackForIssue2779Enabled;
138+
private CefSharp.Structs.Size? resizeHackForIssue2779Size;
139+
137140
/// <summary>
138141
/// This flag is set when the browser gets focus before the underlying CEF browser
139142
/// has been initialized.
140143
/// </summary>
141144
private bool initialFocus;
142145

146+
/// <summary>
147+
/// Hack to work around issue https://github.com/cefsharp/CefSharp/issues/2779
148+
/// Enabled by default
149+
/// </summary>
150+
public bool EnableResizeHackForIssue2779 { get; set; }
151+
152+
/// <summary>
153+
/// Number of miliseconds to wait after resizing the browser when it first
154+
/// becomes visible. After the delay the browser will revert to it's
155+
/// original size.
156+
/// Hack to work around issue https://github.com/cefsharp/CefSharp/issues/2779
157+
/// </summary>
158+
public int ResizeHackForIssue2279DelayInMs { get; set; }
159+
143160
/// <summary>
144161
/// Gets a value indicating whether this instance is disposed.
145162
/// </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
{
@@ -763,7 +783,18 @@ Rect IRenderWebBrowser.GetViewRect()
763783
/// <returns>View Rectangle</returns>
764784
protected virtual Rect GetViewRect()
765785
{
766-
return viewRect;
786+
//Take a local copy as the value is set on a different thread,
787+
//Its possible the struct is set to null after our initial check.
788+
var resizeRect = resizeHackForIssue2779Size;
789+
790+
if (resizeRect == null)
791+
{
792+
return viewRect;
793+
}
794+
795+
var size = resizeRect.Value;
796+
797+
return new Rect(0, 0, size.Width, size.Height);
767798
}
768799

769800
/// <inheritdoc />
@@ -916,6 +947,11 @@ void IRenderWebBrowser.OnPaint(PaintElementType type, Rect dirtyRect, IntPtr buf
916947
/// <param name="height">height</param>
917948
protected virtual void OnPaint(bool isPopup, Rect dirtyRect, IntPtr buffer, int width, int height)
918949
{
950+
if (resizeHackForIssue2779Enabled)
951+
{
952+
return;
953+
}
954+
919955
var paint = Paint;
920956
if (paint != null)
921957
{
@@ -1679,7 +1715,7 @@ protected override void OnDpiChanged(DpiScale oldDpi, DpiScale newDpi)
16791715
}
16801716
#endif
16811717

1682-
private void OnWindowStateChanged(object sender, EventArgs e)
1718+
private async void OnWindowStateChanged(object sender, EventArgs e)
16831719
{
16841720
var window = (Window)sender;
16851721

@@ -1690,16 +1726,54 @@ private void OnWindowStateChanged(object sender, EventArgs e)
16901726
{
16911727
if (previousWindowState == WindowState.Minimized && browser != null)
16921728
{
1693-
browser.GetHost().WasHidden(false);
1729+
await CefUiThreadRunAsync(async () =>
1730+
{
1731+
var host = browser?.GetHost();
1732+
if (host != null && !host.IsDisposed)
1733+
{
1734+
try
1735+
{
1736+
host.WasHidden(false);
1737+
1738+
await ResizeHackFor2779();
1739+
}
1740+
catch (ObjectDisposedException)
1741+
{
1742+
// Because Dispose runs in another thread there's a race condition between
1743+
// that and this code running on the CEF UI thread, so the host could be disposed
1744+
// between the check and using it. We can either synchronize access using locking
1745+
// (potentially blocking the UI thread in Dispose) or catch the extremely rare
1746+
// exception, which is what we do here
1747+
}
1748+
}
1749+
});
16941750
}
1751+
16951752
break;
16961753
}
16971754
case WindowState.Minimized:
16981755
{
1699-
if (browser != null)
1756+
await CefUiThreadRunAsync(() =>
17001757
{
1701-
browser.GetHost().WasHidden(true);
1702-
}
1758+
var host = browser?.GetHost();
1759+
if (host != null && !host.IsDisposed)
1760+
{
1761+
if (EnableResizeHackForIssue2779)
1762+
{
1763+
resizeHackForIssue2779Enabled = true;
1764+
}
1765+
1766+
try
1767+
{
1768+
host.WasHidden(true);
1769+
}
1770+
catch (ObjectDisposedException)
1771+
{
1772+
// See comment in catch in OnWindowStateChanged
1773+
}
1774+
}
1775+
});
1776+
17031777
break;
17041778
}
17051779
}
@@ -1842,6 +1916,24 @@ internal void UiThreadRunAsync(Action action, DispatcherPriority priority = Disp
18421916
}
18431917
}
18441918

1919+
protected async Task CefUiThreadRunAsync(Action action)
1920+
{
1921+
if (!IsDisposed && InternalIsBrowserInitialized())
1922+
{
1923+
if (Cef.CurrentlyOnThread(CefThreadIds.TID_UI))
1924+
{
1925+
action();
1926+
}
1927+
else
1928+
{
1929+
await Cef.UIThreadTaskFactory.StartNew(delegate
1930+
{
1931+
action();
1932+
});
1933+
}
1934+
}
1935+
}
1936+
18451937
/// <summary>
18461938
/// Runs the specific Action on the Dispatcher in an sync fashion
18471939
/// </summary>
@@ -1864,52 +1956,97 @@ private void UiThreadRunSync(Action action, DispatcherPriority priority = Dispat
18641956
/// </summary>
18651957
/// <param name="sender">The sender.</param>
18661958
/// <param name="e">The <see cref="SizeChangedEventArgs"/> instance containing the event data.</param>
1867-
private void OnActualSizeChanged(object sender, SizeChangedEventArgs e)
1959+
private async void OnActualSizeChanged(object sender, SizeChangedEventArgs e)
18681960
{
18691961
// Initialize RenderClientAdapter when WPF has calculated the actual size of current content.
18701962
CreateOffscreenBrowser(e.NewSize);
18711963

1964+
if (InternalIsBrowserInitialized())
1965+
{
1966+
await CefUiThreadRunAsync(() =>
1967+
{
1968+
SetViewRect(e);
1969+
1970+
var host = browser?.GetHost();
1971+
if (host != null && !host.IsDisposed)
1972+
{
1973+
try
1974+
{
1975+
host.WasResized();
1976+
}
1977+
catch (ObjectDisposedException)
1978+
{
1979+
// See comment in catch in OnWindowStateChanged
1980+
}
1981+
}
1982+
});
1983+
}
1984+
else
1985+
{
1986+
//If the browser hasn't been created yet then directly update the viewRect
1987+
SetViewRect(e);
1988+
}
1989+
}
1990+
1991+
private void SetViewRect(SizeChangedEventArgs e)
1992+
{
18721993
//NOTE: Previous we used Math.Ceiling to round the sizing up, we
18731994
//now set UseLayoutRounding = true; on the control so the sizes are
18741995
//already rounded to a whole number for us.
18751996
viewRect = new Rect(0, 0, (int)e.NewSize.Width, (int)e.NewSize.Height);
1876-
1877-
if (browser != null)
1878-
{
1879-
browser.GetHost().WasResized();
1880-
}
18811997
}
18821998

18831999
/// <summary>
18842000
/// Handles the <see cref="E:IsVisibleChanged" /> event.
18852001
/// </summary>
18862002
/// <param name="sender">The sender.</param>
18872003
/// <param name="args">The <see cref="DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
1888-
private void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs args)
2004+
private async void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs args)
18892005
{
18902006
var isVisible = (bool)args.NewValue;
18912007

18922008
if (browser != null)
18932009
{
1894-
var host = browser.GetHost();
1895-
host.WasHidden(!isVisible);
2010+
await CefUiThreadRunAsync(async () =>
2011+
{
2012+
var host = browser?.GetHost();
2013+
if (host != null && !host.IsDisposed)
2014+
{
2015+
try
2016+
{
2017+
host.WasHidden(!isVisible);
18962018

1897-
if (isVisible)
2019+
if (isVisible)
2020+
{
2021+
await ResizeHackFor2779();
2022+
}
2023+
else if (EnableResizeHackForIssue2779)
2024+
{
2025+
resizeHackForIssue2779Enabled = true;
2026+
}
2027+
}
2028+
catch (ObjectDisposedException)
2029+
{
2030+
// See comment in catch in OnWindowStateChanged
2031+
}
2032+
}
2033+
});
2034+
2035+
if (browser != null)
18982036
{
18992037
//Fix for #1778 - When browser becomes visible we update the zoom level
19002038
//browsers of the same origin will share the same zoomlevel and
19012039
//we need to track the update, so our ZoomLevelProperty works
19022040
//properly
1903-
host.GetZoomLevelAsync().ContinueWith(t =>
2041+
var zoomLevel = await browser.GetHost().GetZoomLevelAsync();
2042+
2043+
UiThreadRunAsync(() =>
19042044
{
19052045
if (!IsDisposed)
19062046
{
1907-
SetCurrentValue(ZoomLevelProperty, t.Result);
2047+
SetCurrentValue(ZoomLevelProperty, zoomLevel);
19082048
}
1909-
},
1910-
CancellationToken.None,
1911-
TaskContinuationOptions.OnlyOnRanToCompletion,
1912-
TaskScheduler.FromCurrentSynchronizationContext());
2049+
});
19132050
}
19142051
}
19152052
}
@@ -2551,5 +2688,30 @@ public IBrowser GetBrowser()
25512688

25522689
return browser;
25532690
}
2691+
2692+
private async Task ResizeHackFor2779()
2693+
{
2694+
if (EnableResizeHackForIssue2779)
2695+
{
2696+
var host = browser?.GetHost();
2697+
if (host != null && !host.IsDisposed)
2698+
{
2699+
resizeHackForIssue2779Size = new Structs.Size(viewRect.Width + 1, viewRect.Height + 1);
2700+
host.WasResized();
2701+
2702+
await Task.Delay(ResizeHackForIssue2279DelayInMs);
2703+
2704+
if (!host.IsDisposed)
2705+
{
2706+
resizeHackForIssue2779Size = null;
2707+
host.WasResized();
2708+
2709+
resizeHackForIssue2779Enabled = false;
2710+
2711+
host.Invalidate(PaintElementType.View);
2712+
}
2713+
}
2714+
}
2715+
}
25542716
}
25552717
}

0 commit comments

Comments
 (0)