diff --git a/modules/juce_gui_extra/misc/juce_WebBrowserComponent.h b/modules/juce_gui_extra/misc/juce_WebBrowserComponent.h index 068a1f9aa8cd..f80e59cb1f38 100644 --- a/modules/juce_gui_extra/misc/juce_WebBrowserComponent.h +++ b/modules/juce_gui_extra/misc/juce_WebBrowserComponent.h @@ -259,12 +259,35 @@ class JUCE_API WebBrowserComponent : public Component return withMember (*this, &AppleWkWebView::acceptsFirstMouse, false); } + /** Sets the background colour that the WKWebView paints underneath all web content. + + By default WKWebView's NSView paints an opaque white background until the page + actually renders, which causes a one-frame flash whenever the surface is shown + or resized into real bounds. Setting a colour here turns that off so the JUCE + layer beneath composites through (transparent / clear colour), or the chosen + colour shows in the overscroll area (macOS 12+ / iOS 15+). + + This mirrors the equivalent option on WinWebView2 so cross-platform host code + can configure both backends from one place. + + The colour must be either fully opaque or fully transparent. + */ + [[nodiscard]] AppleWkWebView withBackgroundColour (const Colour& colour) const + { + // the background colour must be either fully opaque or transparent! + jassert (colour.isOpaque() || colour.isTransparent()); + + return withMember (*this, &AppleWkWebView::backgroundColour, colour); + } + auto getAllowAccessToEnclosingDirectory() const { return allowAccessToEnclosingDirectory; } - auto getAcceptsFirstMouse() const { return acceptsFirstMouse; } + auto getAcceptsFirstMouse() const { return acceptsFirstMouse; } + auto getBackgroundColour() const { return backgroundColour; } private: bool allowAccessToEnclosingDirectory = false; bool acceptsFirstMouse = true; + std::optional backgroundColour; }; /** Specifies options that apply to the Windows implementation when the WebView2 feature is diff --git a/modules/juce_gui_extra/native/juce_WebBrowserComponent_mac.mm b/modules/juce_gui_extra/native/juce_WebBrowserComponent_mac.mm index d881e76ef279..d2ab19eb652d 100644 --- a/modules/juce_gui_extra/native/juce_WebBrowserComponent_mac.mm +++ b/modules/juce_gui_extra/native/juce_WebBrowserComponent_mac.mm @@ -878,9 +878,11 @@ void evaluateJavascript (const String&, WebBrowserComponent::EvaluationCallback) [config.get() setURLSchemeHandler:webViewDelegate.get() forURLScheme:@"juce"]; } - #if JUCE_DEBUG + // Enable the Safari Web Inspector in all build configurations. + // Plugins running inside a DAW are inherently a release-build target, + // so gating this on JUCE_DEBUG made the inspector unreachable in the + // only environment where developers actually run them. [preferences setValue: @(true) forKey: @"developerExtrasEnabled"]; - #endif #if JUCE_MAC auto& webviewClass = [&]() -> auto& @@ -912,6 +914,68 @@ void evaluateJavascript (const String&, WebBrowserComponent::EvaluationCallback) [webView.get() setNavigationDelegate: webViewDelegate.get()]; [webView.get() setUIDelegate: webViewDelegate.get()]; + // The `developerExtrasEnabled` preference above is the legacy path + // and remains sufficient on older macOS. From 13.3 / iOS 16.4 onward + // Apple requires the public `inspectable` property to be set as well + // before Safari's Develop menu will list this WebView. + if (@available (macOS 13.3, iOS 16.4, *)) + [webView.get() setInspectable: YES]; + + // Mirrors WinWebView2::withBackgroundColour. Stops WKWebView from + // flashing its default opaque background colour before the page has + // painted: drawsBackground = NO makes the OS surface transparent so + // the JUCE layer beneath composites through, and (on macOS 12+ / + // iOS 15+) the public setUnderPageBackgroundColor handles the + // overscroll bounce area. + // + // The KVC trick must target the WKWebView itself, not the + // WKWebViewConfiguration — WKWebView copies config values at init + // time, so setValue on config after webview creation is a no-op. + // `drawsBackground` is undocumented but stable since macOS 10.14 + // and is what Safari, Xcode and Tauri/wry use. + if (const auto bg = browserOptions.getAppleWkWebViewOptions().getBackgroundColour()) + { + @try + { + [webView.get() setValue: @(NO) forKey: @"drawsBackground"]; + } + @catch (NSException* exception) + { + // KVC compliance is private API; if a future OS rejects it, + // fall back silently rather than crashing the host. + (void) exception; + } + + #if JUCE_MAC + // Belt-and-braces: also tell the underlying NSView's CALayer + // not to paint its own opaque background. Some WebKit builds + // still flash on the very first frame because the host NSView + // composites independently of the WKWebView's drawsBackground. + // Setting wantsLayer + a transparent layer background is a + // public-API safety net. + [webView.get() setWantsLayer: YES]; + if (auto* layer = [webView.get() layer]) + layer.backgroundColor = CGColorGetConstantColor (kCGColorClear); + #endif + + if (@available (macOS 12.0, iOS 15.0, *)) + { + const auto c = *bg; + #if JUCE_MAC + auto* colour = [NSColor colorWithSRGBRed: c.getFloatRed() + green: c.getFloatGreen() + blue: c.getFloatBlue() + alpha: c.getFloatAlpha()]; + #else + auto* colour = [UIColor colorWithRed: c.getFloatRed() + green: c.getFloatGreen() + blue: c.getFloatBlue() + alpha: c.getFloatAlpha()]; + #endif + [webView.get() setUnderPageBackgroundColor: colour]; + } + } + setView (webView.get()); owner.owner.addAndMakeVisible (this); } diff --git a/modules/juce_gui_extra/native/juce_WebBrowserComponent_windows.cpp b/modules/juce_gui_extra/native/juce_WebBrowserComponent_windows.cpp index 4cedbaf7a9a0..1f2afa472121 100644 --- a/modules/juce_gui_extra/native/juce_WebBrowserComponent_windows.cpp +++ b/modules/juce_gui_extra/native/juce_WebBrowserComponent_windows.cpp @@ -1019,9 +1019,11 @@ class WebBrowserComponent::Impl::Platform::WebView2 final : public WebBrowserCom if (settings != nullptr) { - #if ! JUCE_DEBUG - settings->put_AreDevToolsEnabled (false); - #endif + // Leave WebView2 DevTools enabled in all build configurations. + // Plugins running inside a DAW are inherently a release-build + // target, so disabling DevTools in release made the inspector + // unreachable in the only environment where developers + // actually run them. settings->put_IsStatusBarEnabled (! preferences.getWinWebView2BackendOptions().getIsStatusBarDisabled()); settings->put_IsBuiltInErrorPageEnabled (! preferences.getWinWebView2BackendOptions().getIsBuiltInErrorPageDisabled());