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