@@ -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 {
@@ -760,7 +780,18 @@ Rect IRenderWebBrowser.GetViewRect()
760780 /// <returns>View Rectangle</returns>
761781 protected virtual Rect GetViewRect ( )
762782 {
763- return viewRect ;
783+ //Take a local copy as the value is set on a different thread,
784+ //Its possible the struct is set to null after our initial check.
785+ var resizeRect = resizeHackForIssue2779Size ;
786+
787+ if ( resizeRect == null )
788+ {
789+ return viewRect ;
790+ }
791+
792+ var size = resizeRect . Value ;
793+
794+ return new Rect ( 0 , 0 , size . Width , size . Height ) ;
764795 }
765796
766797 bool IRenderWebBrowser . GetScreenPoint ( int viewX , int viewY , out int screenX , out int screenY )
@@ -905,6 +936,11 @@ void IRenderWebBrowser.OnPaint(PaintElementType type, Rect dirtyRect, IntPtr buf
905936 /// <param name="height">height</param>
906937 protected virtual void OnPaint ( bool isPopup , Rect dirtyRect , IntPtr buffer , int width , int height )
907938 {
939+ if ( resizeHackForIssue2779Enabled )
940+ {
941+ return ;
942+ }
943+
908944 var paint = Paint ;
909945 if ( paint != null )
910946 {
@@ -1633,7 +1669,7 @@ private void PresentationSourceChangedHandler(object sender, SourceChangedEventA
16331669 }
16341670 }
16351671
1636- private void OnWindowStateChanged ( object sender , EventArgs e )
1672+ private async void OnWindowStateChanged ( object sender , EventArgs e )
16371673 {
16381674 var window = ( Window ) sender ;
16391675
@@ -1644,16 +1680,54 @@ private void OnWindowStateChanged(object sender, EventArgs e)
16441680 {
16451681 if ( previousWindowState == WindowState . Minimized && browser != null )
16461682 {
1647- browser . GetHost ( ) . WasHidden ( false ) ;
1683+ await CefUiThreadRunAsync ( async ( ) =>
1684+ {
1685+ var host = browser ? . GetHost ( ) ;
1686+ if ( host != null && ! host . IsDisposed )
1687+ {
1688+ try
1689+ {
1690+ host . WasHidden ( false ) ;
1691+
1692+ await ResizeHackFor2779 ( ) ;
1693+ }
1694+ catch ( ObjectDisposedException )
1695+ {
1696+ // Because Dispose runs in another thread there's a race condition between
1697+ // that and this code running on the CEF UI thread, so the host could be disposed
1698+ // between the check and using it. We can either synchronize access using locking
1699+ // (potentially blocking the UI thread in Dispose) or catch the extremely rare
1700+ // exception, which is what we do here
1701+ }
1702+ }
1703+ } ) ;
16481704 }
1705+
16491706 break ;
16501707 }
16511708 case WindowState . Minimized :
16521709 {
1653- if ( browser != null )
1710+ await CefUiThreadRunAsync ( ( ) =>
16541711 {
1655- browser . GetHost ( ) . WasHidden ( true ) ;
1656- }
1712+ var host = browser ? . GetHost ( ) ;
1713+ if ( host != null && ! host . IsDisposed )
1714+ {
1715+ if ( EnableResizeHackForIssue2779 )
1716+ {
1717+ resizeHackForIssue2779Enabled = true ;
1718+ }
1719+
1720+ try
1721+ {
1722+ host . WasHidden ( true ) ;
1723+ }
1724+ catch ( ObjectDisposedException )
1725+ {
1726+ // See comment in catch in OnWindowStateChanged
1727+ }
1728+ }
1729+ } ) ;
1730+
16571731 break ;
16581732 }
16591733 }
@@ -1796,6 +1870,24 @@ internal void UiThreadRunAsync(Action action, DispatcherPriority priority = Disp
17961870 }
17971871 }
17981872
1873+ protected async Task CefUiThreadRunAsync ( Action action )
1874+ {
1875+ if ( ! IsDisposed && InternalIsBrowserInitialized ( ) )
1876+ {
1877+ if ( Cef . CurrentlyOnThread ( CefThreadIds . TID_UI ) )
1878+ {
1879+ action ( ) ;
1880+ }
1881+ else
1882+ {
1883+ await Cef . UIThreadTaskFactory . StartNew ( delegate
1884+ {
1885+ action ( ) ;
1886+ } ) ;
1887+ }
1888+ }
1889+ }
1890+
17991891 /// <summary>
18001892 /// Runs the specific Action on the Dispatcher in an sync fashion
18011893 /// </summary>
@@ -1818,52 +1910,97 @@ private void UiThreadRunSync(Action action, DispatcherPriority priority = Dispat
18181910 /// </summary>
18191911 /// <param name="sender">The sender.</param>
18201912 /// <param name="e">The <see cref="SizeChangedEventArgs"/> instance containing the event data.</param>
1821- private void OnActualSizeChanged ( object sender , SizeChangedEventArgs e )
1913+ private async void OnActualSizeChanged ( object sender , SizeChangedEventArgs e )
18221914 {
18231915 // Initialize RenderClientAdapter when WPF has calculated the actual size of current content.
18241916 CreateOffscreenBrowser ( e . NewSize ) ;
18251917
1918+ if ( InternalIsBrowserInitialized ( ) )
1919+ {
1920+ await CefUiThreadRunAsync ( ( ) =>
1921+ {
1922+ SetViewRect ( e ) ;
1923+
1924+ var host = browser ? . GetHost ( ) ;
1925+ if ( host != null && ! host . IsDisposed )
1926+ {
1927+ try
1928+ {
1929+ host . WasResized ( ) ;
1930+ }
1931+ catch ( ObjectDisposedException )
1932+ {
1933+ // See comment in catch in OnWindowStateChanged
1934+ }
1935+ }
1936+ } ) ;
1937+ }
1938+ else
1939+ {
1940+ //If the browser hasn't been created yet then directly update the viewRect
1941+ SetViewRect ( e ) ;
1942+ }
1943+ }
1944+
1945+ private void SetViewRect ( SizeChangedEventArgs e )
1946+ {
18261947 //NOTE: Previous we used Math.Ceiling to round the sizing up, we
18271948 //now set UseLayoutRounding = true; on the control so the sizes are
18281949 //already rounded to a whole number for us.
18291950 viewRect = new Rect ( 0 , 0 , ( int ) e . NewSize . Width , ( int ) e . NewSize . Height ) ;
1830-
1831- if ( browser != null )
1832- {
1833- browser . GetHost ( ) . WasResized ( ) ;
1834- }
18351951 }
18361952
18371953 /// <summary>
18381954 /// Handles the <see cref="E:IsVisibleChanged" /> event.
18391955 /// </summary>
18401956 /// <param name="sender">The sender.</param>
18411957 /// <param name="args">The <see cref="DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
1842- private void OnIsVisibleChanged ( object sender , DependencyPropertyChangedEventArgs args )
1958+ private async void OnIsVisibleChanged ( object sender , DependencyPropertyChangedEventArgs args )
18431959 {
18441960 var isVisible = ( bool ) args . NewValue ;
18451961
18461962 if ( browser != null )
18471963 {
1848- var host = browser . GetHost ( ) ;
1849- host . WasHidden ( ! isVisible ) ;
1964+ await CefUiThreadRunAsync ( async ( ) =>
1965+ {
1966+ var host = browser ? . GetHost ( ) ;
1967+ if ( host != null && ! host . IsDisposed )
1968+ {
1969+ try
1970+ {
1971+ host . WasHidden ( ! isVisible ) ;
1972+
1973+ if ( isVisible )
1974+ {
1975+ await ResizeHackFor2779 ( ) ;
1976+ }
1977+ else if ( EnableResizeHackForIssue2779 )
1978+ {
1979+ resizeHackForIssue2779Enabled = true ;
1980+ }
1981+ }
1982+ catch ( ObjectDisposedException )
1983+ {
1984+ // See comment in catch in OnWindowStateChanged
1985+ }
1986+ }
1987+ } ) ;
18501988
1851- if ( isVisible )
1989+ if ( browser != null )
18521990 {
18531991 //Fix for #1778 - When browser becomes visible we update the zoom level
18541992 //browsers of the same origin will share the same zoomlevel and
18551993 //we need to track the update, so our ZoomLevelProperty works
18561994 //properly
1857- host . GetZoomLevelAsync ( ) . ContinueWith ( t =>
1995+ var zoomLevel = await browser . GetHost ( ) . GetZoomLevelAsync ( ) ;
1996+
1997+ UiThreadRunAsync ( ( ) =>
18581998 {
18591999 if ( ! IsDisposed )
18602000 {
1861- SetCurrentValue ( ZoomLevelProperty , t . Result ) ;
2001+ SetCurrentValue ( ZoomLevelProperty , zoomLevel ) ;
18622002 }
1863- } ,
1864- CancellationToken . None ,
1865- TaskContinuationOptions . OnlyOnRanToCompletion ,
1866- TaskScheduler . FromCurrentSynchronizationContext ( ) ) ;
2003+ } ) ;
18672004 }
18682005 }
18692006 }
@@ -2498,5 +2635,30 @@ public IBrowser GetBrowser()
24982635
24992636 return browser ;
25002637 }
2638+
2639+ private async Task ResizeHackFor2779 ( )
2640+ {
2641+ if ( EnableResizeHackForIssue2779 )
2642+ {
2643+ var host = browser ? . GetHost ( ) ;
2644+ if ( host != null && ! host . IsDisposed )
2645+ {
2646+ resizeHackForIssue2779Size = new Structs . Size ( viewRect . Width + 1 , viewRect . Height + 1 ) ;
2647+ host . WasResized ( ) ;
2648+
2649+ await Task . Delay ( ResizeHackForIssue2279DelayInMs ) ;
2650+
2651+ if ( ! host . IsDisposed )
2652+ {
2653+ resizeHackForIssue2779Size = null ;
2654+ host . WasResized ( ) ;
2655+
2656+ resizeHackForIssue2779Enabled = false ;
2657+
2658+ host . Invalidate ( PaintElementType . View ) ;
2659+ }
2660+ }
2661+ }
2662+ }
25012663 }
25022664}
0 commit comments