@@ -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