Skip to content

choc_Webview.h Construction has Race Condition Caused by Caching Mechanism (Windows) #89

@jackkilgore

Description

@jackkilgore

The Problem

Hello, I have been using choc_WebView.h to embed a webview in a C++ audio plugin for a while. It's been working great, but we recently updated our choc submodule and now the constructor non deterministically fails to load the webview.

I've tracked down the issue to this commit: 442a8ed
Specifically, a newly introduced caching mechanism around line 1371:
if (auto userDataFolder = getUserDataFolder(); ! userDataFolder.empty()) { }

Behavior

When I load a fresh build of our plugin on windows, the webview always loads. After that, construction is a dice roll. Sometimes it works perfectly, and other times it never even hits our index.html. Even when using theopts.webviewIsReady it still does not help with the race condition. I confirmed that the race condition occurs in opts.webviewIsReady. Whenever I add a debugger break point in the callback and I continue from the break point it, the webview always successfully loads.

So through some hunting I found the root of the above mentioned race condition comes from this newly introduced caching mechanism in 442a8ed. The cache is stored in the roaming folder of the host platform. For example, if I am loading our plugin in REAPER, the webview stores a cache at this path: C:\Users\yourusername\AppData\Roaming\reaper.exe\EBWebView\.

If I delete the cache every time before reopening the UI, the webview successfully loads. THIS WAS THE AHA moment.

The Fix

  1. The main problem seems to be the new userDataFolder option. Would it be possible to implement something in CHOC that exposes a flag to disallow the caching mechanism, aka make sure userDataFolder is always NULL?

  2. Or is there some cleaner mechanism to avoid a race condition when a cache exists? Is our own code logic wrong? Do we need a more defensive method for binding and navigating the webview?

Context

For context, here is how we construct the webview:

    choc::ui::WebView::Options opts;

#if JUCE_DEBUG
    opts.enableDebugMode = true;
#endif

#if !JUCE_DEBUG
    opts.fetchResource = [=](const std::string& path) -> std::optional<choc::ui::WebView::Options::Resource>
    {
  ...
    };
#endif
    opts.acceptsFirstMouseClick = true;
    opts.enableDefaultClipboardKeyShortcutsInSafari = false;
    opts.transparentBackground = true;

    // THIS IS WHERE THE RACE CONDITION OCCURS
    opts.webviewIsReady = [this](choc::ui::WebView& webView)
    {
        DBG("[WebViewEditor]: WebView ready, finish construction...");
#if JUCE_MAC
        viewContainer.setView(webView.getViewHandle());
#elif JUCE_WINDOWS
        viewContainer.setHWND(webView.getViewHandle());
#else
#error "Only support Mac and Windows here yet."
#endif
bind(); // code that calls choc::ui::WebView::bind
#if JUCE_DEBUG
        webView.navigate("http://localhost:5173/");
#else
        webView.navigate(PluginUtils::getIndexFilePath());
#endif
    };
    // Create the webview
    webView = std::make_unique<choc::ui::WebView>(opts);

Conclusion

Thank you for your time! Let me know if you need information from me or if you can reproduce it.

-- Jack Kilgore (Lunacy Audio Dev)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions