Search code examples
c#windowswinformscefsharpringcentral

CEFSharp app displays blank screen and then responds on it's own after a minute


I've been working on a app which uses CEFSharp (version 83.4.20) to load my companies VOIP platform (engage.ringcentral.com). The source is up on Github https://github.com/dylanlangston/EngageRC

The app has been working for about a month now and after putting it into production I've received reports of an odd issue. People are seeing a blank screen and unable to interact with the webpage ( Example of issue ). After a few minutes the issue seems to resolve itself and their able to interact with the webpage again. I haven't been able to reproduce the issue myself.

I thought this might be a rendering issue. So far I've tried to adjust the following in my app:

Remove the lines below.

settings.CefCommandLineArgs.Add("disable-gpu");
settings.CefCommandLineArgs.Add("disable-gpu-shader-disk-cache", "1");

Replace them with.

settings.CefCommandLineArgs.Add("disable-gpu-compositing");

Unfortunately this hasn't resolved the issues and I'm unsure what I'm missing.

The relevant code for CEF Initialization is located in https://github.com/dylanlangston/EngageRC/blob/master/Windows/MainWindow.Designer.cs under the InitializeChromium method. See Below

    // 
    // Chrome
    // 
    private CefSharp.WinForms.ChromiumWebBrowser chromeBrowser;
    public ChromiumWebBrowser InitializeChromium(string URL, string Name)
    {
        // Check if already Initialized
        if (Cef.IsInitialized == false)
        {
            CefSettings settings = new CefSettings();
            // Enable Logging if debugging or console window
            if (!this.debug || !this.console)
            {
                settings.LogSeverity = LogSeverity.Disable;
            }
            else
            {
                settings.LogSeverity = LogSeverity.Verbose;
            }
            // Enable Microphone settings
            settings.CefCommandLineArgs.Add("enable-media-stream", "1");
            // Set Custom Browser Paths
            settings.CefCommandLineArgs.Add("disable-gpu-shader-disk-cache", "1");
            // Disable GPU to fix rendering issues on some machines.
            settings.CefCommandLineArgs.Add("disable-gpu");
            // Disable CORS protection (cross site scripting) which the engage.ringcentral.com site doesn't seem to like/respect, disabled as the website doesn't seem to improve with this turned on.
            //settings.CefCommandLineArgs.Add("disable-web-security");
            // Enable session Cookie persistence, disabled as it's unneeded. 
            //settings.PersistSessionCookies = true;
            // Custom Browser paths
            settings.BrowserSubprocessPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources\CEFSharp\CefSharp.BrowserSubprocess.exe");
            settings.LocalesDirPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources\CEFSharp\locales\");
            settings.ResourcesDirPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources\CEFSharp\");
            // Check if Resources folder is writable. If it isn't then write to application data.
            DirectoryInfo di = new DirectoryInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources\CEFSharp\"));
            if ((di.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
            {
                settings.RootCachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"EngageRC\CEFSharp\cache");
                settings.CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"EngageRC\CEFSharp\cache");
                settings.LogFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"EngageRC\CEFSharp\debug.log");
            }
            else
            {
                settings.RootCachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources\CEFSharp\cache");
                settings.CachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources\CEFSharp\cache");
                settings.LogFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources\CEFSharp\debug.log");
            }
            // Initialize cef with the provided settings or add new tab
            Cef.Initialize(settings, performDependencyCheck: true, browserProcessHandler: null);
        }
        // Create a browser component
        chromeBrowser = new ChromiumWebBrowser(URL);
        // Set Name
        chromeBrowser.Name = Name;
        // Adjust size and scaling
        this.chromeBrowser.Dock = DockStyle.Fill;
        this.chromeBrowser.Width = this.Width;
        this.chromeBrowser.Height = this.Height;
        // Add control
        this.Controls.Add(chromeBrowser);
        // Bring to front
        this.chromeBrowser.BringToFront();
        // Set label to Goodbye.
        this.label1.Text = "Goodbye";

        // Return control
        return chromeBrowser;
    }

I wasn't able to find any previous questions with my Google foo so it seems this isn't something other's are seeing their applications. I'm still learning the ropes when it comes to C# so it's 100% possible this is my own fault and something I'm doing wrong.

I was able to get verbose logging from a machine that experienced the issue. I'm mostly seeing the following error. Full logs at https://gist.github.com/dylanlangston/194e389ead437e6c6fe08b2d4746bf43

[0729/083616.491:ERROR:paint_controller.cc(646)] PaintController::FinishCycle() completed

Any ideas or recommendations to help troubleshoot this is appreciated!


Updated: Based on @amaitland's helpful comments it seems this behavior may be caused by using Thread.Sleep on a thread on I'm not supposed to.

After reviewing my code it looks like I have this in two locations.

First I call thread.sleep to wait until the browsers zoom level matches the value I've saved to the windows isolatedstorage. This shouldn't be running except when the application first starts. I don't think this is the problem but I am posting it here anyways to be safe. (https://github.com/dylanlangston/EngageRC/blob/master/CEFSharpHandlers.cs)

public void OnLoadingStateChanged(object sender, LoadingStateChangedEventArgs args)
        {
            ChromiumWebBrowser chrome = (ChromiumWebBrowser)sender;
            if (!args.IsLoading) // Loading finished.
            {
                // If first load set zoom level to previous level
                if (firstLoad < 3)
                {
                    try
                    {
                        double zoom = double.Parse(ConfigReader.ReadIsolatedStorage("z"), System.Globalization.CultureInfo.InvariantCulture);
                        while (args.Browser.GetZoomLevelAsync().Result != zoom) { chrome.SetZoomLevel(zoom); Thread.Sleep(50); }
                    }
                    catch { }
                    firstLoad++;
                }
                // These JS and CSS modifications built into EngageRC
                string hotfixJS = "";
                string hotfixCSS = "/* Fix for dropdowns */ ul.dropdown-menu[style=\\\"opacity: 1;\\\"] {display: block !important; } /* End fix for dropdowns */";
                // Inject custom javascript and css code on page load
                chrome.ExecuteScriptAsync(hotfixJS);
                chrome.ExecuteScriptAsync("var engageRCSS = document.getElementById('EngageRCSS'); if (!engageRCSS) { var node = document.createElement('style'); node.setAttribute('id', 'EngageRCSS'); node.innerHTML = \"" + hotfixCSS +"\"; document.body.appendChild(node); }");
                if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources\Custom.js"))) { chrome.ExecuteScriptAsync(System.IO.File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources\Custom.js"))); }
                if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources\Custom.css"))) { chrome.ExecuteScriptAsync("var customCSS = document.getElementById('customcss'); if (!customCSS) { var node = document.createElement('style'); node.setAttribute('id', 'customcss'); node.innerHTML = \"" + System.IO.File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources\Custom.css")).Replace("\n", String.Empty).Replace("\r", String.Empty).Replace("\t", String.Empty).Replace("\"", "\\\"") + "\"; document.body.appendChild(node); }"); }
            }
         }

I also have it when displaying a notification. This is most likely my problem as this is running in the main application thread... (https://github.com/dylanlangston/EngageRC/blob/master/NotificationSupport.cs)

public void displayNotification(string Title = "EngageRC", string Message = "")
            {
                // Display notification for 5 seconds
                Notify notification = MainWindow.notification;
                try
                {
                    if (!IsActive(hWnd)) // Check if application is already active.
                    {
                        string config = ConfigReader.GetConfigValue("NotificationsDisabled").ToLower();
                        if (!(config == "true" || config == "1"))
                        {
                            notification.NewNotification(Title, Message, 5000);
                        }
                        config = ConfigReader.GetConfigValue("GetFocusOnCallDisabled").ToLower();
                        if (!(config == "true" || config == "1") && (Message == "You have incoming call"))
                        {
                            // Minimize window
                            ShowWindow(hWnd, 0x02);
                            // Restore window to previous state.
                            ShowWindow(hWnd, 0x09);
                        }
                        config = ConfigReader.GetConfigValue("GetFocusOnPendingDispositionDisabled").ToLower();
                        if (!(config == "true" || config == "1") && (Message == "You have a disposition pending"))
                        {
                            // Minimize window
                            ShowWindow(hWnd, 0x02);
                            // Restore window to previous state.
                            ShowWindow(hWnd, 0x09);
                        }
                    }
                }
                catch { notification.SetIconVisible(false); }
                finally
                {
                    Thread.Sleep(4999);
                    notification.SetIconVisible(false);
                }
            }

Based on what I've heard I should make the Notification Form run on a different thread from the main application then? Or am I way off base?


Update: I was able to move the notification into it's own thread. I'm closing this issue and will have the user's test to see if the blank screen persists.

Thanks for the help again!!


Solution

  • @amaitland's comments were helpful in narrowing this issue down.