WebView: forward keys to host responder chain by default#25
Merged
Conversation
The OS web surface (WKWebView on macOS, WebView2 on Windows) becomes first responder on click and otherwise swallows every keystroke, which prevents plugin hosts from receiving transport, MIDI-typing, and DAW shortcut keys while the WebView has focus. This patch flips the default routing so keys go to the host responder chain unless the JS layer reports that an editable DOM element has focus. Public surface: - WebBrowserComponent::setEditableFocusActive(bool) — runtime toggle - Auto-registered native function __juceSetEditableFocusActive — JS entry point exposed via JUCE's existing getNativeFunction helper, only registered when native integration is already in use (no security regression for components loading untrusted content) macOS: extends the existing WebViewKeyEquivalentResponder<WKWebView> with keyDown:/keyUp: overrides and adjusts performKeyEquivalent: so Cmd-shortcuts also bubble to the host when not editing. State is held in a std::atomic<bool> exposed per-instance through an ivar pointer, mirroring the existing LastFocusChange* ivar pattern. Wired into both WKWebViewImpl and the legacy WebViewImpl for consistency. Windows: switches CreateCoreWebView2Controller to CreateCoreWebView2ControllerWithOptions with AllowHostInputProcessing=TRUE so character keys (spacebar, QWERTY) reach the host's window proc, with a capability-gated fallback to plain Create when the environment or controller options interface isn't available. AcceleratorKeyPressed is subscribed unconditionally to cover Tab/Esc/F-keys/modifier combos on older runtimes — its put_Handled value tracks editableFocusActive too. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
WebBrowserComponent::setEditableFocusActive(bool)+ an auto-registered__juceSetEditableFocusActiveJS native function so a frontend can flip key routing between the host responder chain and the embedded DOMWebViewKeyEquivalentResponder<WKWebView>withkeyDown:/keyUp:overrides and adjustsperformKeyEquivalent:so Cmd-shortcuts bubble to the host outside of editable focusCreateCoreWebView2ControllerWithOptionswithAllowHostInputProcessing=TRUE(capability-gated fallback to plain Create) and subscribesAcceleratorKeyPressedMotivation
The OS web surface (WKWebView on macOS, WebView2 on Windows) becomes first responder on click and otherwise swallows every keystroke. For an audio plugin embedded in a DAW that breaks transport (spacebar), MIDI-typing, save shortcuts, and basically every host keybinding the moment the user touches the WebView. JUCE doesn't currently expose a way to fix this, so plugin authors either eat the regression or write their own NSView/HWND swizzles outside JUCE.
This patch makes JUCE's
WebBrowserComponentforward keys to the host responder chain by default and lets the embedded JS opt back into DOM key routing when an editable element has focus.Public API
The JS side gets a JUCE-injected native function:
__juceSetEditableFocusActiveis only registered when native integration is already in use (withNativeIntegrationEnabledwas set, or any other native function was registered). That keeps the security model intact — components loading untrusted content don't suddenly have native-integration forced on by an internal JUCE function.Platform notes
macOS — uses the existing
WebViewKeyEquivalentResponder<WKWebView>ObjC class subclass machinery; a neweditableFocusActiveMemberNameivar mirrors the existingLastFocusChange*ivar pattern, holding a pointer to astd::atomic<bool>owned byWKWebViewImpl(also wired into the legacyWebViewImplfor consistency). ThekeyDown:/keyUp:overrides forward tonextResponderby default;performKeyEquivalent:returnsNOoutside editable focus so AppKit walks past the WebView and existing host Cmd-shortcuts still work.Windows —
CreateCoreWebView2ControllerWithOptionsis the only way to enableAllowHostInputProcessing, which is a creation-time option not a runtime toggle. The new code path queriesICoreWebView2Environment10+ICoreWebView2ControllerOptions2; if either is unavailable (older WebView2 SDK / runtime / locked-down enterprise environment), the originalCreateCoreWebView2Controlleris used andAcceleratorKeyPressedalone covers the accelerator-key subset (Tab, Esc, F-keys, modifier combos).AcceleratorKeyPressed'sput_Handledvalue tracks the sameeditableFocusActiveatomic so editing-mode behavior is consistent across the two code paths.Test plan
current_VST3(macOS Memory Rites) — compiles after replacing the captured[isEditableFocusActive]lambdas with[]lambdas (JUCE'stoFnPtrrequires captureless lambdas forObjCClass::addMethod)AllowHostInputProcessingand accelerator-key pass-through viaAcceleratorKeyPressedAllowHostInputProcessing(frontend uses a capture-phase guard for these two keys while editable focus is active)🤖 Generated with Claude Code