Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WPF - Multiple browsers blank after minimize/restore #3870

Closed
dkrainyk opened this issue Nov 4, 2021 · 11 comments
Closed

WPF - Multiple browsers blank after minimize/restore #3870

dkrainyk opened this issue Nov 4, 2021 · 11 comments
Labels
upstream These issues require fixing in the Chromium Embedded Framework(CEF) or Chromium.

Comments

@dkrainyk
Copy link

dkrainyk commented Nov 4, 2021

  • What version of the product are you using?
    94.4.110 Nuget

  • What architecture x86 or x64?
    both

  • What version of .Net?
    4.5.2

  • On what operating system?
    Win 10 & Win Server 2016

  • Are you using WinForms, WPF or OffScreen?
    WPF

  • What steps will reproduce the problem?
    https://github.com/dkrainyk/CefSharp.MinimalExample/tree/issue/94.4.110

    • Create multiple browser components on a single form (e.g. 3x2 grid of 6 tabs)
    • Launch the app & wait till all browser tabs are fully initialized
    • Minimize the app
    • Wait 5 seconds
    • Restore the app
    • At least one of the browser tabs will be blank, resizing the window fixes the issue
  • What is the expected output? What do you see instead?
    Browser tabs should not be empty after restoring the window from minimized state.
    cefbug

  • Please provide any additional information below.
    Behavior is similar to WPF - Browser goes blank if used in combination with TabControl #2779 but has different steps.
    Could be an upstream issue too.

  • Does the cef log provide any relevant information? (By default there should be a debug.log file in your bin directory)
    no

@amaitland
Copy link
Member

Did you test with the CEF Sample application? You seem to have skipped thatsection of the bug report.

Behavior is similar to WPF - Browser goes blank if used in combination with TabControl #2779 but has different steps.
Could be an upstream issue too.

Sounds exactly the same. How many different machines have you tested with?

@dkrainyk
Copy link
Author

dkrainyk commented Nov 4, 2021

It doesn't seem to happen with single browser component, so I do not see a reason to test with sample CEF app because it also features single browser.

Checked on several VDIs in AWS and Azure (win server 2016 and win 10).

According to our checks #2779 case is not reproducible anymore (tabbed interface displaying one browser instance at a time)

@amaitland amaitland changed the title WPF - Browser blank after minimize/restore WPF - Multiple browsers blank after minimize/restore Nov 4, 2021
@amaitland amaitland added the upstream These issues require fixing in the Chromium Embedded Framework(CEF) or Chromium. label Nov 4, 2021
@amaitland
Copy link
Member

so I do not see a reason to test with sample CEF app because it also features single browser.

You can download a standard distribution from https://cef-builds.spotifycdn.com/index.html#windows64 and modify cefclient to display multiple CefBrowser instances.

I'd suggest asking on https://bitbucket.org/chromiumembedded/cef/issues/2483/osr-invalidate-does-not-generate-frame if a new issue should be created.

Depending on your usage requirements switching to https://github.com/cefsharp/CefSharp.Wpf.HwndHost might be an option.

@amaitland
Copy link
Member

Starting in version 95.7.141 you can override the newly added OnBrowserWasHidden method to intercept the IBrowserHost.WasHidden calls. Obviously this has performance implications as the browsers are still active when hidden.

public class ChromiumWebBrowserEx : ChromiumWebBrowser
{
	protected override void OnBrowserWasHidden(bool hidden)
	{
		//NO-OP Don't notify CEF that the browser was hidden/shown
		//https://github.com/cefsharp/CefSharp/issues/3870
	}
}

You can also try implement a resize hack similar to #2779, the following has only had minimal testing.

public class ChromiumWebBrowserEx : ChromiumWebBrowser
{
	private bool resizeHackEnabled;
	private Size? resizeHackSize;

	protected override Rect GetViewRect()
	{
		//Take a local copy as the value is set on a different thread,
		//Its possible the struct is set to null after our initial check.
		var resizeRect = resizeHackSize;

		if (resizeRect == null)
		{
			return base.GetViewRect();
		}

		var size = resizeRect.Value;

		return new Rect(0, 0, size.Width, size.Height);
	}

	protected override void OnPaint(bool isPopup, Rect dirtyRect, System.IntPtr buffer, int width, int height)
	{
		if (resizeHackEnabled)
		{
			return;
		}

		base.OnPaint(isPopup, dirtyRect, buffer, width, height);
	}

	protected override void OnBrowserWasHidden(bool hidden)
	{
		if(hidden)
		{
			CefUiThreadRunAsync(() =>
			{
				try
				{
					var host = this.GetBrowserHost();
					if (host != null && !host.IsDisposed)
					{
						resizeHackEnabled = true;

						host.WasHidden(true);
					}                        
				}
				catch (ObjectDisposedException)
				{
					// Because Dispose runs in another thread there's a race condition between
					// that and this code running on the CEF UI thread, so the host could be disposed
					// between the check and using it. We can either synchronize access using locking
					// (potentially blocking the UI thread in Dispose) or catch the extremely rare
					// exception, which is what we do here
				}
			});
		}
		else
		{
			CefUiThreadRunAsync(async () =>
			{
				try
				{
					var host = this.GetBrowserHost();
					if (host != null && !host.IsDisposed)
					{
						host.WasHidden(false);

						var viewRect = GetViewRect();
						resizeHackSize = new Size(viewRect.Width + 1, viewRect.Height + 1);
						host.WasResized();

						await Task.Delay(50);

						if (!host.IsDisposed)
						{
							resizeHackSize = null;
							host.WasResized();

							resizeHackEnabled = false;

							host.Invalidate(PaintElementType.View);
						}
					}                        
				}
				catch (ObjectDisposedException)
				{
					// Because Dispose runs in another thread there's a race condition between
					// that and this code running on the CEF UI thread, so the host could be disposed
					// between the check and using it. We can either synchronize access using locking
					// (potentially blocking the UI thread in Dispose) or catch the extremely rare
					// exception, which is what we do here
				}
			});
		}
	}

	private void CefUiThreadRunAsync(Action action)
	{
		var webBrowser = (IWebBrowser)this;

		if (!IsDisposed && webBrowser.IsBrowserInitialized)
		{
			if (Cef.CurrentlyOnThread(CefThreadIds.TID_UI))
			{
				action();
			}
			else
			{
				_ = Cef.UIThreadTaskFactory.StartNew(delegate
				{
					action();
				});
			}
		}
	}
}

I'm going to close this as upstream as the issue will need to be fixed in CEF.

@amaitland
Copy link
Member

If someone would like to sponsor my time I'll add a resize hack back into the ChromiumWebBrowser source.

@dkrainyk
Copy link
Author

dkrainyk commented Nov 7, 2021

OnBrowserWasHidden

Thank you, I will try to use this, I think this is desired behavior anyway, we do not want browser to switch into "sleeping" state once control is out of focus.
Unfortunately resize hack has a showstopper drawback in our scenario - 1 pixel jump is quite annoying for users.

@amaitland
Copy link
Member

Unfortunately resize hack has a showstopper drawback in our scenario - 1 pixel jump is quite annoying for users.

What jump exactly? The OnPaint call is ignored until the original size is restored.

@dkrainyk
Copy link
Author

dkrainyk commented Nov 7, 2021

What jump exactly?

The old resize fix for #2779 it was noticeable when users switched between tab.
It was sending that resize event down to webpage forcing it to resize and re-render.

@amaitland
Copy link
Member

The old resize fix for #2779 it was noticeable when users switched between tab.
It was sending that resize event down to webpage forcing it to resize and re-render.

Personally I don't recall seeing this and have had no other reports. You can easily add additional checks to avoid rendering the larger frames.

@dkrainyk
Copy link
Author

dkrainyk commented Nov 8, 2021

Starting in version 95.7.141 you can override the newly added OnBrowserWasHidden method to intercept the IBrowserHost.WasHidden calls. Obviously this has performance implications as the browsers are still active when hidden.

I've tried that and it works for us. Please keep this virtual method in future versions.

@amaitland
Copy link
Member

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
upstream These issues require fixing in the Chromium Embedded Framework(CEF) or Chromium.
Projects
None yet
Development

No branches or pull requests

2 participants