@@ -140,12 +140,29 @@ public class ChromiumWebBrowser : Control, IRenderWebBrowser, IWpfWebBrowser
140140 /// </summary>
141141 private static bool DesignMode ;
142142
143+ private bool resizeHackForIssue2779Enabled ;
144+ private CefSharp . Structs . Size ? resizeHackForIssue2779Size ;
145+
143146 /// <summary>
144147 /// The value for disposal, if it's 1 (one) then this instance is either disposed
145148 /// or in the process of getting disposed
146149 /// </summary>
147150 private int disposeSignaled ;
148151
152+ /// <summary>
153+ /// Hack to work around issue https://github.com/cefsharp/CefSharp/issues/2779
154+ /// Enabled by default
155+ /// </summary>
156+ public bool EnableResizeHackForIssue2779 { get ; set ; }
157+
158+ /// <summary>
159+ /// Number of miliseconds to wait after resizing the browser when it first
160+ /// becomes visible. After the delay the browser will revert to it's
161+ /// original size.
162+ /// Hack to work around issue https://github.com/cefsharp/CefSharp/issues/2779
163+ /// </summary>
164+ public int ResizeHackForIssue2279DelayInMs { get ; set ; }
165+
149166 /// <summary>
150167 /// Used as workaround for issue https://github.com/cefsharp/CefSharp/issues/3021
151168 /// </summary>
@@ -643,6 +660,9 @@ public ChromiumWebBrowser(string initialAddress)
643660 [ MethodImpl ( MethodImplOptions . NoInlining ) ]
644661 private void NoInliningConstructor ( )
645662 {
663+ EnableResizeHackForIssue2779 = true ;
664+ ResizeHackForIssue2279DelayInMs = 50 ;
665+
646666 //Initialize CEF if it hasn't already been initialized
647667 if ( ! Cef . IsInitialized )
648668 {
@@ -909,7 +929,18 @@ Rect IRenderWebBrowser.GetViewRect()
909929 /// <returns>View Rectangle</returns>
910930 protected virtual Rect GetViewRect ( )
911931 {
912- return viewRect ;
932+ //Take a local copy as the value is set on a different thread,
933+ //Its possible the struct is set to null after our initial check.
934+ var resizeRect = resizeHackForIssue2779Size ;
935+
936+ if ( resizeRect == null )
937+ {
938+ return viewRect ;
939+ }
940+
941+ var size = resizeRect . Value ;
942+
943+ return new Rect ( 0 , 0 , size . Width , size . Height ) ;
913944 }
914945
915946 bool IRenderWebBrowser . GetScreenPoint ( int viewX , int viewY , out int screenX , out int screenY )
@@ -1054,6 +1085,11 @@ void IRenderWebBrowser.OnPaint(PaintElementType type, Rect dirtyRect, IntPtr buf
10541085 /// <param name="height">height</param>
10551086 protected virtual void OnPaint ( bool isPopup , Rect dirtyRect , IntPtr buffer , int width , int height )
10561087 {
1088+ if ( resizeHackForIssue2779Enabled )
1089+ {
1090+ return ;
1091+ }
1092+
10571093 var paint = Paint ;
10581094 if ( paint != null )
10591095 {
@@ -1868,7 +1904,7 @@ private void PresentationSourceChangedHandler(object sender, SourceChangedEventA
18681904 }
18691905 }
18701906
1871- private void OnWindowStateChanged ( object sender , EventArgs e )
1907+ private async void OnWindowStateChanged ( object sender , EventArgs e )
18721908 {
18731909 var window = ( Window ) sender ;
18741910
@@ -1879,16 +1915,54 @@ private void OnWindowStateChanged(object sender, EventArgs e)
18791915 {
18801916 if ( previousWindowState == WindowState . Minimized && browser != null )
18811917 {
1882- browser . GetHost ( ) . WasHidden ( false ) ;
1918+ await CefUiThreadRunAsync ( async ( ) =>
1919+ {
1920+ var host = browser ? . GetHost ( ) ;
1921+ if ( host != null && ! host . IsDisposed )
1922+ {
1923+ try
1924+ {
1925+ host . WasHidden ( false ) ;
1926+
1927+ await ResizeHackFor2779 ( ) ;
1928+ }
1929+ catch ( ObjectDisposedException )
1930+ {
1931+ // Because Dispose runs in another thread there's a race condition between
1932+ // that and this code running on the CEF UI thread, so the host could be disposed
1933+ // between the check and using it. We can either synchronize access using locking
1934+ // (potentially blocking the UI thread in Dispose) or catch the extremely rare
1935+ // exception, which is what we do here
1936+ }
1937+ }
1938+ } ) ;
18831939 }
1940+
18841941 break ;
18851942 }
18861943 case WindowState . Minimized :
18871944 {
1888- if ( browser != null )
1945+ await CefUiThreadRunAsync ( ( ) =>
18891946 {
1890- browser . GetHost ( ) . WasHidden ( true ) ;
1891- }
1947+ var host = browser ? . GetHost ( ) ;
1948+ if ( host != null && ! host . IsDisposed )
1949+ {
1950+ if ( EnableResizeHackForIssue2779 )
1951+ {
1952+ resizeHackForIssue2779Enabled = true ;
1953+ }
1954+
1955+ try
1956+ {
1957+ host . WasHidden ( true ) ;
1958+ }
1959+ catch ( ObjectDisposedException )
1960+ {
1961+ // See comment in catch in OnWindowStateChanged
1962+ }
1963+ }
1964+ } ) ;
1965+
18921966 break ;
18931967 }
18941968 }
@@ -2031,6 +2105,24 @@ internal void UiThreadRunAsync(Action action, DispatcherPriority priority = Disp
20312105 }
20322106 }
20332107
2108+ protected async Task CefUiThreadRunAsync ( Action action )
2109+ {
2110+ if ( ! IsDisposed && InternalIsBrowserInitialized ( ) )
2111+ {
2112+ if ( Cef . CurrentlyOnThread ( CefThreadIds . TID_UI ) )
2113+ {
2114+ action ( ) ;
2115+ }
2116+ else
2117+ {
2118+ await Cef . UIThreadTaskFactory . StartNew ( delegate
2119+ {
2120+ action ( ) ;
2121+ } ) ;
2122+ }
2123+ }
2124+ }
2125+
20342126 /// <summary>
20352127 /// Runs the specific Action on the Dispatcher in an sync fashion
20362128 /// </summary>
@@ -2053,52 +2145,97 @@ private void UiThreadRunSync(Action action, DispatcherPriority priority = Dispat
20532145 /// </summary>
20542146 /// <param name="sender">The sender.</param>
20552147 /// <param name="e">The <see cref="SizeChangedEventArgs"/> instance containing the event data.</param>
2056- private void OnActualSizeChanged ( object sender , SizeChangedEventArgs e )
2148+ private async void OnActualSizeChanged ( object sender , SizeChangedEventArgs e )
20572149 {
20582150 // Initialize RenderClientAdapter when WPF has calculated the actual size of current content.
20592151 CreateOffscreenBrowser ( e . NewSize ) ;
20602152
2153+ if ( InternalIsBrowserInitialized ( ) )
2154+ {
2155+ await CefUiThreadRunAsync ( ( ) =>
2156+ {
2157+ SetViewRect ( e ) ;
2158+
2159+ var host = browser ? . GetHost ( ) ;
2160+ if ( host != null && ! host . IsDisposed )
2161+ {
2162+ try
2163+ {
2164+ host . WasResized ( ) ;
2165+ }
2166+ catch ( ObjectDisposedException )
2167+ {
2168+ // See comment in catch in OnWindowStateChanged
2169+ }
2170+ }
2171+ } ) ;
2172+ }
2173+ else
2174+ {
2175+ //If the browser hasn't been created yet then directly update the viewRect
2176+ SetViewRect ( e ) ;
2177+ }
2178+ }
2179+
2180+ private void SetViewRect ( SizeChangedEventArgs e )
2181+ {
20612182 //NOTE: Previous we used Math.Ceiling to round the sizing up, we
20622183 //now set UseLayoutRounding = true; on the control so the sizes are
20632184 //already rounded to a whole number for us.
20642185 viewRect = new Rect ( 0 , 0 , ( int ) e . NewSize . Width , ( int ) e . NewSize . Height ) ;
2065-
2066- if ( browser != null )
2067- {
2068- browser . GetHost ( ) . WasResized ( ) ;
2069- }
20702186 }
20712187
20722188 /// <summary>
20732189 /// Handles the <see cref="E:IsVisibleChanged" /> event.
20742190 /// </summary>
20752191 /// <param name="sender">The sender.</param>
20762192 /// <param name="args">The <see cref="DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
2077- private void OnIsVisibleChanged ( object sender , DependencyPropertyChangedEventArgs args )
2193+ private async void OnIsVisibleChanged ( object sender , DependencyPropertyChangedEventArgs args )
20782194 {
20792195 var isVisible = ( bool ) args . NewValue ;
20802196
20812197 if ( browser != null )
20822198 {
2083- var host = browser . GetHost ( ) ;
2084- host . WasHidden ( ! isVisible ) ;
2199+ await CefUiThreadRunAsync ( async ( ) =>
2200+ {
2201+ var host = browser ? . GetHost ( ) ;
2202+ if ( host != null && ! host . IsDisposed )
2203+ {
2204+ try
2205+ {
2206+ host . WasHidden ( ! isVisible ) ;
2207+
2208+ if ( isVisible )
2209+ {
2210+ await ResizeHackFor2779 ( ) ;
2211+ }
2212+ else if ( EnableResizeHackForIssue2779 )
2213+ {
2214+ resizeHackForIssue2779Enabled = true ;
2215+ }
2216+ }
2217+ catch ( ObjectDisposedException )
2218+ {
2219+ // See comment in catch in OnWindowStateChanged
2220+ }
2221+ }
2222+ } ) ;
20852223
2086- if ( isVisible )
2224+ if ( browser != null )
20872225 {
20882226 //Fix for #1778 - When browser becomes visible we update the zoom level
20892227 //browsers of the same origin will share the same zoomlevel and
20902228 //we need to track the update, so our ZoomLevelProperty works
20912229 //properly
2092- host . GetZoomLevelAsync ( ) . ContinueWith ( t =>
2230+ var zoomLevel = await browser . GetHost ( ) . GetZoomLevelAsync ( ) ;
2231+
2232+ UiThreadRunAsync ( ( ) =>
20932233 {
20942234 if ( ! IsDisposed )
20952235 {
2096- SetCurrentValue ( ZoomLevelProperty , t . Result ) ;
2236+ SetCurrentValue ( ZoomLevelProperty , zoomLevel ) ;
20972237 }
2098- } ,
2099- CancellationToken . None ,
2100- TaskContinuationOptions . OnlyOnRanToCompletion ,
2101- TaskScheduler . FromCurrentSynchronizationContext ( ) ) ;
2238+ } ) ;
21022239 }
21032240 }
21042241 }
@@ -2738,5 +2875,30 @@ private bool InternalIsBrowserInitialized()
27382875 // Volatile.Read would likely use a memory barrier which I believe is unnecessary in this scenario
27392876 return Interlocked . CompareExchange ( ref browserInitialized , 0 , 0 ) == 1 ;
27402877 }
2878+
2879+ private async Task ResizeHackFor2779 ( )
2880+ {
2881+ if ( EnableResizeHackForIssue2779 )
2882+ {
2883+ var host = browser ? . GetHost ( ) ;
2884+ if ( host != null && ! host . IsDisposed )
2885+ {
2886+ resizeHackForIssue2779Size = new Structs . Size ( viewRect . Width + 1 , viewRect . Height + 1 ) ;
2887+ host . WasResized ( ) ;
2888+
2889+ await Task . Delay ( ResizeHackForIssue2279DelayInMs ) ;
2890+
2891+ if ( ! host . IsDisposed )
2892+ {
2893+ resizeHackForIssue2779Size = null ;
2894+ host . WasResized ( ) ;
2895+
2896+ resizeHackForIssue2779Enabled = false ;
2897+
2898+ host . Invalidate ( PaintElementType . View ) ;
2899+ }
2900+ }
2901+ }
2902+ }
27412903 }
27422904}
0 commit comments