From 7da02dc49598929e5b763bec392a07dfe2ed29ee Mon Sep 17 00:00:00 2001 From: kattapug Date: Sat, 25 Jul 2026 01:54:39 +0700 Subject: [PATCH] fix: probe WebView2 backend with the real user data folder On Windows, juce::WebBrowserComponent::areOptionsSupported() is not a compile-time capability check: it calls createWebViewHandle(), which in turn calls CreateCoreWebView2EnvironmentWithOptions() with the user data folder taken from the supplied options. The startup probes passed Options().withBackend(webview2) with no WinWebView2 options, so WebView2 fell back to its default user data folder of "\OpenStudio.exe.WebView2". For an installed build that is C:\Program Files\OpenStudio, which is not writable by a normal user, so environment creation failed with E_ACCESSDENIED and the probe reported the backend as unavailable. The app then showed the "Embedded browser backend unavailable" screen even though the WebView2 runtime was installed and working, and even though the real WebBrowserComponent already points at a writable user data folder under AppData. Add makeBrowserBackendProbeOptions() and use it for all three probes (constructor diagnostics, buildStartupSelfTestReport, buildStartupDiagnostics) so they exercise the same user data folder the real component uses. Also reuse getWebView2UserDataFolder() in the component options instead of repeating the path literal. Verified on Windows 11 with WebView2 runtime 150.0.4078.83: the installed build logged "Embedded browser backend supported: No" for a normal user and "Yes" when run elevated (which made the Program Files folder writable), matching the E_ACCESSDENIED path fixed here. Co-Authored-By: Claude Opus 5 (1M context) --- Source/MainComponent.cpp | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/Source/MainComponent.cpp b/Source/MainComponent.cpp index 927b342..69ccd71 100644 --- a/Source/MainComponent.cpp +++ b/Source/MainComponent.cpp @@ -456,6 +456,25 @@ juce::File getWebView2UserDataFolder() .getChildFile("WebView2UserData"); } +// juce::WebBrowserComponent::areOptionsSupported() does not merely report a compile-time +// capability on Windows: it actually creates a WebView2 environment with the supplied options. +// Without an explicit user data folder, WebView2 defaults to "\OpenStudio.exe.WebView2", +// which is not writable for a normal user under "C:\Program Files\OpenStudio", so environment +// creation fails with E_ACCESSDENIED and the backend is misreported as unavailable. +// Probe with the same options the real WebBrowserComponent uses. +juce::WebBrowserComponent::Options makeBrowserBackendProbeOptions() +{ + auto options = juce::WebBrowserComponent::Options().withBackend(getPreferredBrowserBackend()); + +#if JUCE_WINDOWS + options = options.withWinWebView2Options( + juce::WebBrowserComponent::Options::WinWebView2() + .withUserDataFolder(getWebView2UserDataFolder())); +#endif + + return options; +} + juce::File getStartupLogFile() { auto logDir = juce::File::getSpecialLocation(juce::File::userApplicationDataDirectory) @@ -1115,9 +1134,7 @@ juce::Array MainComponent::activeInstances; juce::var MainComponent::buildStartupSelfTestReport() { - const auto preferredBackend = getPreferredBrowserBackend(); - const auto supported = juce::WebBrowserComponent::areOptionsSupported( - juce::WebBrowserComponent::Options().withBackend(preferredBackend)); + const auto supported = juce::WebBrowserComponent::areOptionsSupported(makeBrowserBackendProbeOptions()); const auto dependencyStatus = evaluateStartupDependencies(supported); auto* report = new juce::DynamicObject(); @@ -1178,9 +1195,7 @@ MainComponent::MainComponent(AudioEngine& audioEngineIn, #if JUCE_WINDOWS .withWinWebView2Options ( juce::WebBrowserComponent::Options::WinWebView2() - .withUserDataFolder (juce::File::getSpecialLocation (juce::File::userApplicationDataDirectory) - .getChildFile ("OpenStudio") - .getChildFile ("WebView2UserData")) + .withUserDataFolder (getWebView2UserDataFolder()) .withStatusBarDisabled()) #endif .withNativeIntegrationEnabled() @@ -6281,8 +6296,7 @@ MainComponent::MainComponent(AudioEngine& audioEngineIn, const auto packagedFrontend = getPackagedFrontendEntryPoint(); const auto webViewUserDataDir = getWebView2UserDataFolder(); - auto checkOptions = juce::WebBrowserComponent::Options() - .withBackend(preferredBackend); + const auto checkOptions = makeBrowserBackendProbeOptions(); const bool supported = juce::WebBrowserComponent::areOptionsSupported(checkOptions); const auto dependencyStatus = evaluateStartupDependencies(supported); @@ -7134,8 +7148,7 @@ void MainComponent::repairWindowsPrerequisites() juce::var MainComponent::buildStartupDiagnostics() const { const auto dependencyStatus = evaluateStartupDependencies( - juce::WebBrowserComponent::areOptionsSupported( - juce::WebBrowserComponent::Options().withBackend(getPreferredBrowserBackend()))); + juce::WebBrowserComponent::areOptionsSupported(makeBrowserBackendProbeOptions())); auto* diagnostics = new juce::DynamicObject(); const auto selfTestReport = buildStartupSelfTestReport(); diagnostics->setProperty("windowRole", getWindowRoleQueryValue(windowRole));