Skip to content

Commit b1d1279

Browse files
committed
WPF - Resize Hack for when browser switches from hidden to visible (#2779)
1 parent 4cb77e4 commit b1d1279

File tree

1 file changed

+177
-22
lines changed

1 file changed

+177
-22
lines changed

CefSharp.Wpf/ChromiumWebBrowser.cs

Lines changed: 177 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,21 @@ public class ChromiumWebBrowser : Control, IRenderWebBrowser, IWpfWebBrowser
134134
/// </summary>
135135
private static bool DesignMode;
136136

137+
private bool resizeHackForIssue2779Enabled;
138+
private CefSharp.Structs.Size? resizeHackForIssue2779Size;
139+
137140
/// <summary>
138141
/// The value for disposal, if it's 1 (one) then this instance is either disposed
139142
/// or in the process of getting disposed
140143
/// </summary>
141144
private int disposeSignaled;
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+
143152
/// <summary>
144153
/// Gets a value indicating whether this instance is disposed.
145154
/// </summary>
@@ -557,6 +566,8 @@ public ChromiumWebBrowser(string initialAddress)
557566
[MethodImpl(MethodImplOptions.NoInlining)]
558567
private void NoInliningConstructor()
559568
{
569+
EnableResizeHackForIssue2779 = true;
570+
560571
//Initialize CEF if it hasn't already been initialized
561572
if (!Cef.IsInitialized)
562573
{
@@ -822,7 +833,18 @@ Rect IRenderWebBrowser.GetViewRect()
822833
/// <returns>View Rectangle</returns>
823834
protected virtual Rect GetViewRect()
824835
{
825-
return viewRect;
836+
//Take a local copy as the value is set on a different thread,
837+
//Its possible the struct is set to null after our initial check.
838+
var resizeRect = resizeHackForIssue2779Size;
839+
840+
if (resizeRect == null)
841+
{
842+
return viewRect;
843+
}
844+
845+
var size = resizeRect.Value;
846+
847+
return new Rect(0, 0, size.Width, size.Height);
826848
}
827849

828850
bool IRenderWebBrowser.GetScreenPoint(int viewX, int viewY, out int screenX, out int screenY)
@@ -967,6 +989,11 @@ void IRenderWebBrowser.OnPaint(PaintElementType type, Rect dirtyRect, IntPtr buf
967989
/// <param name="height">height</param>
968990
protected virtual void OnPaint(bool isPopup, Rect dirtyRect, IntPtr buffer, int width, int height)
969991
{
992+
if (resizeHackForIssue2779Enabled)
993+
{
994+
return;
995+
}
996+
970997
var paint = Paint;
971998
if (paint != null)
972999
{
@@ -1759,7 +1786,7 @@ private void PresentationSourceChangedHandler(object sender, SourceChangedEventA
17591786
}
17601787
}
17611788

1762-
private void OnWindowStateChanged(object sender, EventArgs e)
1789+
private async void OnWindowStateChanged(object sender, EventArgs e)
17631790
{
17641791
var window = (Window)sender;
17651792

@@ -1770,16 +1797,54 @@ private void OnWindowStateChanged(object sender, EventArgs e)
17701797
{
17711798
if (previousWindowState == WindowState.Minimized && browser != null)
17721799
{
1773-
browser.GetHost().WasHidden(false);
1800+
await CefUiThreadRunAsync(async () =>
1801+
{
1802+
var host = browser?.GetHost();
1803+
if (host != null && !host.IsDisposed)
1804+
{
1805+
try
1806+
{
1807+
host.WasHidden(false);
1808+
1809+
await ResizeHackFor2779();
1810+
}
1811+
catch (ObjectDisposedException)
1812+
{
1813+
// Because Dispose runs in another thread there's a race condition between
1814+
// that and this code running on the CEF UI thread, so the host could be disposed
1815+
// between the check and using it. We can either synchronize access using locking
1816+
// (potentially blocking the UI thread in Dispose) or catch the extremely rare
1817+
// exception, which is what we do here
1818+
}
1819+
}
1820+
});
17741821
}
1822+
17751823
break;
17761824
}
17771825
case WindowState.Minimized:
17781826
{
1779-
if (browser != null)
1827+
await CefUiThreadRunAsync(() =>
17801828
{
1781-
browser.GetHost().WasHidden(true);
1782-
}
1829+
var host = browser?.GetHost();
1830+
if (host != null && !host.IsDisposed)
1831+
{
1832+
if (EnableResizeHackForIssue2779)
1833+
{
1834+
resizeHackForIssue2779Enabled = true;
1835+
}
1836+
1837+
try
1838+
{
1839+
host.WasHidden(true);
1840+
}
1841+
catch (ObjectDisposedException)
1842+
{
1843+
// See comment in catch in OnWindowStateChanged
1844+
}
1845+
}
1846+
});
1847+
17831848
break;
17841849
}
17851850
}
@@ -1916,6 +1981,24 @@ internal void UiThreadRunAsync(Action action, DispatcherPriority priority = Disp
19161981
}
19171982
}
19181983

1984+
protected async Task CefUiThreadRunAsync(Action action)
1985+
{
1986+
if (!IsDisposed && InternalIsBrowserInitialized())
1987+
{
1988+
if (Cef.CurrentlyOnThread(CefThreadIds.TID_UI))
1989+
{
1990+
action();
1991+
}
1992+
else
1993+
{
1994+
await Cef.UIThreadTaskFactory.StartNew(delegate
1995+
{
1996+
action();
1997+
});
1998+
}
1999+
}
2000+
}
2001+
19192002
/// <summary>
19202003
/// Runs the specific Action on the Dispatcher in an sync fashion
19212004
/// </summary>
@@ -1938,52 +2021,97 @@ private void UiThreadRunSync(Action action, DispatcherPriority priority = Dispat
19382021
/// </summary>
19392022
/// <param name="sender">The sender.</param>
19402023
/// <param name="e">The <see cref="SizeChangedEventArgs"/> instance containing the event data.</param>
1941-
private void OnActualSizeChanged(object sender, SizeChangedEventArgs e)
2024+
private async void OnActualSizeChanged(object sender, SizeChangedEventArgs e)
19422025
{
19432026
// Initialize RenderClientAdapter when WPF has calculated the actual size of current content.
19442027
CreateOffscreenBrowser(e.NewSize);
19452028

2029+
if (InternalIsBrowserInitialized())
2030+
{
2031+
await CefUiThreadRunAsync(() =>
2032+
{
2033+
SetViewRect(e);
2034+
2035+
var host = browser?.GetHost();
2036+
if (host != null && !host.IsDisposed)
2037+
{
2038+
try
2039+
{
2040+
host.WasResized();
2041+
}
2042+
catch (ObjectDisposedException)
2043+
{
2044+
// See comment in catch in OnWindowStateChanged
2045+
}
2046+
}
2047+
});
2048+
}
2049+
else
2050+
{
2051+
//If the browser hasn't been created yet then directly update the viewRect
2052+
SetViewRect(e);
2053+
}
2054+
}
2055+
2056+
private void SetViewRect(SizeChangedEventArgs e)
2057+
{
19462058
//NOTE: Previous we used Math.Ceiling to round the sizing up, we
19472059
//now set UseLayoutRounding = true; on the control so the sizes are
19482060
//already rounded to a whole number for us.
19492061
viewRect = new Rect(0, 0, (int)e.NewSize.Width, (int)e.NewSize.Height);
1950-
1951-
if (browser != null)
1952-
{
1953-
browser.GetHost().WasResized();
1954-
}
19552062
}
19562063

19572064
/// <summary>
19582065
/// Handles the <see cref="E:IsVisibleChanged" /> event.
19592066
/// </summary>
19602067
/// <param name="sender">The sender.</param>
19612068
/// <param name="args">The <see cref="DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
1962-
private void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs args)
2069+
private async void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs args)
19632070
{
19642071
var isVisible = (bool)args.NewValue;
19652072

19662073
if (browser != null)
19672074
{
1968-
var host = browser.GetHost();
1969-
host.WasHidden(!isVisible);
2075+
await CefUiThreadRunAsync(async () =>
2076+
{
2077+
var host = browser?.GetHost();
2078+
if (host != null && !host.IsDisposed)
2079+
{
2080+
try
2081+
{
2082+
host.WasHidden(!isVisible);
19702083

1971-
if (isVisible)
2084+
if (isVisible)
2085+
{
2086+
await ResizeHackFor2779();
2087+
}
2088+
else if (EnableResizeHackForIssue2779)
2089+
{
2090+
resizeHackForIssue2779Enabled = true;
2091+
}
2092+
}
2093+
catch (ObjectDisposedException)
2094+
{
2095+
// See comment in catch in OnWindowStateChanged
2096+
}
2097+
}
2098+
});
2099+
2100+
if (browser != null)
19722101
{
19732102
//Fix for #1778 - When browser becomes visible we update the zoom level
19742103
//browsers of the same origin will share the same zoomlevel and
19752104
//we need to track the update, so our ZoomLevelProperty works
19762105
//properly
1977-
host.GetZoomLevelAsync().ContinueWith(t =>
2106+
var zoomLevel = await browser.GetHost().GetZoomLevelAsync();
2107+
2108+
UiThreadRunAsync(() =>
19782109
{
19792110
if (!IsDisposed)
19802111
{
1981-
SetCurrentValue(ZoomLevelProperty, t.Result);
2112+
SetCurrentValue(ZoomLevelProperty, zoomLevel);
19822113
}
1983-
},
1984-
CancellationToken.None,
1985-
TaskContinuationOptions.OnlyOnRanToCompletion,
1986-
TaskScheduler.FromCurrentSynchronizationContext());
2114+
});
19872115
}
19882116
}
19892117
}
@@ -2625,5 +2753,32 @@ private bool InternalIsBrowserInitialized()
26252753
// Volatile.Read would likely use a memory barrier which I believe is unnecessary in this scenario
26262754
return Interlocked.CompareExchange(ref browserInitialized, 0, 0) == 1;
26272755
}
2756+
2757+
private async Task ResizeHackFor2779()
2758+
{
2759+
if (EnableResizeHackForIssue2779)
2760+
{
2761+
const int delayInMs = 50;
2762+
2763+
var host = browser?.GetHost();
2764+
if (host != null && !host.IsDisposed)
2765+
{
2766+
resizeHackForIssue2779Size = new Structs.Size(viewRect.Width - 1, viewRect.Height - 1);
2767+
host.WasResized();
2768+
2769+
await Task.Delay(delayInMs);
2770+
2771+
if (!host.IsDisposed)
2772+
{
2773+
resizeHackForIssue2779Size = null;
2774+
host.WasResized();
2775+
2776+
resizeHackForIssue2779Enabled = false;
2777+
2778+
host.Invalidate(PaintElementType.View);
2779+
}
2780+
}
2781+
}
2782+
}
26282783
}
26292784
}

0 commit comments

Comments
 (0)