diff --git a/build.zig b/build.zig index 1b588363..3d3574fe 100644 --- a/build.zig +++ b/build.zig @@ -490,6 +490,12 @@ pub fn build(b: *std.Build) void { .{ .path = "src/platform/windows/webview2_host.cpp", .pattern = "bridgeOriginForWebViewUrl(source_webview->second, source_url)" }, .{ .path = "src/platform/windows/webview2_host.cpp", .pattern = "webview.spa_fallback = spa_fallback != 0;" }, }); + addFileContainsCheckStep(b, file_contains_checker, test_step, "test-devtools-inspector", "Verify DevTools inspector is enabled on Windows and Linux", &.{ + .{ .path = "src/platform/windows/webview2_host.cpp", .pattern = "put_AreDevToolsEnabled(TRUE)" }, + .{ .path = "src/platform/windows/webview2_host.cpp", .pattern = "OpenDevToolsWindow()" }, + .{ .path = "src/platform/linux/gtk_host.c", .pattern = "webkit_settings_set_enable_developer_extras" }, + .{ .path = "src/platform/linux/gtk_host.c", .pattern = "webkit_web_view_get_inspector" }, + }); addFileContainsCheckStep(b, file_contains_checker, test_step, "test-macos-cef-packaged-assets-webviews", "Verify macOS CEF child WebViews resolve packaged asset URLs before loading", &.{ .{ .path = "src/platform/macos/cef_host.mm", .pattern = "self.assetRoots = [[NSMutableDictionary alloc] init];" }, .{ .path = "src/platform/macos/cef_host.mm", .pattern = "resolvedWebViewURLString:(NSString *)url windowId:(uint64_t)windowId" }, diff --git a/changelog.d/devtools-inspector.md b/changelog.d/devtools-inspector.md new file mode 100644 index 00000000..ce89f097 --- /dev/null +++ b/changelog.d/devtools-inspector.md @@ -0,0 +1 @@ +feature: **DevTools inspector on Windows & Linux**: press F12 or Ctrl+Shift+I to open the browser DevTools inspector. Right-click "Inspect Element" is also available. Matches the existing macOS Cmd+Option+I behavior. diff --git a/src/platform/linux/gtk_host.c b/src/platform/linux/gtk_host.c index c0895892..768afdf7 100644 --- a/src/platform/linux/gtk_host.c +++ b/src/platform/linux/gtk_host.c @@ -251,6 +251,7 @@ typedef struct native_sdk_gtk_window { * UNDER the WebView (or the WebView over a canvas) set it through * the same layer channel the child views use. */ int main_webview_layer; + int inspector_open; /* Last pointer press on a gpu_surface view, in window coordinates — * what an interactive window move begins from. The device/time pair * comes from the originating event; a zero time means no press has @@ -2277,11 +2278,37 @@ static int native_sdk_shortcut_modifiers_match(uint32_t shortcut_modifiers, GdkM has_command == needs_command; } +static gboolean on_inspector_open_window(WebKitWebInspector *inspector, gpointer data) { + (void)inspector; + native_sdk_gtk_window_t *win = data; + win->inspector_open = 1; + return FALSE; +} + +static void on_inspector_closed(WebKitWebInspector *inspector, gpointer data) { + (void)inspector; + native_sdk_gtk_window_t *win = data; + win->inspector_open = 0; +} + static gboolean on_shortcut_key_pressed(GtkEventControllerKey *controller, guint keyval, guint keycode, GdkModifierType state, gpointer data) { (void)controller; (void)keycode; native_sdk_gtk_window_t *win = data; native_sdk_gtk_host_t *host = win ? win->host : NULL; + if (win && win->web_view && + (keyval == GDK_KEY_F12 || + (keyval == GDK_KEY_I && (state & GDK_CONTROL_MASK) && (state & GDK_SHIFT_MASK)))) { + WebKitWebInspector *inspector = webkit_web_view_get_inspector(win->web_view); + if (win->inspector_open) { + webkit_web_inspector_close(inspector); + win->inspector_open = 0; + } else { + webkit_web_inspector_show(inspector); + win->inspector_open = 1; + } + return TRUE; + } if (!host || host->shortcut_count == 0) return FALSE; char key_buffer[32]; int uses_implicit_shift = 0; @@ -2597,6 +2624,10 @@ static native_sdk_gtk_window_t *native_sdk_create_window_internal(native_sdk_gtk "user-content-manager", win->content_manager, NULL)); win->web_view = wv; + WebKitSettings *wk_settings = webkit_web_view_get_settings(wv); + webkit_settings_set_enable_developer_extras(wk_settings, TRUE); + g_signal_connect(webkit_web_view_get_inspector(wv), "open-window", G_CALLBACK(on_inspector_open_window), win); + g_signal_connect(webkit_web_view_get_inspector(wv), "closed", G_CALLBACK(on_inspector_closed), win); if (!host->scheme_registered) { webkit_web_context_register_uri_scheme(webkit_web_view_get_context(wv), "zero", native_sdk_asset_scheme_request, host, NULL); host->scheme_registered = 1; @@ -3608,6 +3639,9 @@ int native_sdk_gtk_create_webview(native_sdk_gtk_host_t *host, uint64_t window_i return 0; } + WebKitSettings *child_wk_settings = webkit_web_view_get_settings(web_view); + webkit_settings_set_enable_developer_extras(child_wk_settings, TRUE); + native_sdk_gtk_webview_t *webview = &win->webviews[win->webview_count++]; memset(webview, 0, sizeof(*webview)); webview->label = label_copy; diff --git a/src/platform/windows/webview2_host.cpp b/src/platform/windows/webview2_host.cpp index 7ff27d2a..b6a48e64 100644 --- a/src/platform/windows/webview2_host.cpp +++ b/src/platform/windows/webview2_host.cpp @@ -4101,6 +4101,11 @@ static bool createChildWebView(Host *host, const std::string &key) { controller->put_ZoomFactor(found->second.zoom); controller->put_IsVisible(TRUE); if (found->second.webview) { + ICoreWebView2Settings *settings = nullptr; + if (SUCCEEDED(found->second.webview->get_Settings(&settings)) && settings) { + settings->put_AreDevToolsEnabled(TRUE); + settings->Release(); + } if (found->second.bridge_enabled) { found->second.webview->AddScriptToExecuteOnDocumentCreated(nativeSdkBridgeScript(), nullptr); EventRegistrationToken bridge_token = {}; @@ -4146,7 +4151,7 @@ static bool createChildWebView(Host *host, const std::string &key) { EventRegistrationToken accelerator_token = {}; uint64_t accelerator_window_id = found->second.window_id; found->second.controller->add_AcceleratorKeyPressed(Callback( - [host, accelerator_window_id, lifetime](ICoreWebView2Controller *, ICoreWebView2AcceleratorKeyPressedEventArgs *args) -> HRESULT { + [host, key, accelerator_window_id, lifetime](ICoreWebView2Controller *, ICoreWebView2AcceleratorKeyPressedEventArgs *args) -> HRESULT { auto token = lifetime.lock(); if (!token) return S_OK; std::lock_guard guard(token->mutex); @@ -4156,6 +4161,14 @@ static bool createChildWebView(Host *host, const std::string &key) { if (kind != COREWEBVIEW2_KEY_EVENT_KIND_KEY_DOWN && kind != COREWEBVIEW2_KEY_EVENT_KIND_SYSTEM_KEY_DOWN) return S_OK; UINT virtual_key = 0; if (FAILED(args->get_VirtualKey(&virtual_key))) return S_OK; + if (virtual_key == VK_F12 || (virtual_key == 'I' && keyDown(VK_CONTROL) && keyDown(VK_SHIFT))) { + auto it = host->webviews.find(key); + if (it != host->webviews.end() && it->second.webview) { + it->second.webview->OpenDevToolsWindow(); + } + args->put_Handled(TRUE); + return S_OK; + } if (emitShortcutForWindowId(host, accelerator_window_id, virtual_key)) { args->put_Handled(TRUE); }