Skip to content

Canvas 36: add application items to the engine's own context menu - #73

Merged
shannah merged 2 commits into
masterfrom
claude/wizardly-dijkstra-1mfgqe
Oct 23, 2026
Merged

shannah merged 2 commits into
masterfrom
claude/wizardly-dijkstra-1mfgqe

Conversation

@shannah

@shannah shannah commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator

What

Applications can now add their own items to the browser engine's own right-click menu. The engine keeps every native item (Copy, Look Up, spelling suggestions, Back/Reload, Inspect Element when debugging, and so on), and the application's items are appended after them, below one separator. This is an augment model that sits next to Canvas 9's replace model (WebViewMouseListener + WebViewContextMenu). Consumer: webliteca/agentic-app-framework's "Ask" item on a text selection.

SPDD: requirements/[User-story-12]…, spdd/analysis/…-[Analysis]-add-application-items-to-the-native-context-menu.md, and Canvas 36 (spdd/prompt/36-20261019-1500-[Feat]-Add-Application-Items-To-The-Native-Context-Menu.md). The code was produced from Canvas 36 with /spdd-generate. One finding from verification (D9b, below) went back into the Canvas before the code changed.

API (additive, binary-compatible)

// ca.weblite.webview.swing.WebViewComponent
public final WebViewComponent addContextMenuContributor(WebViewContextMenuContributor c);
public final WebViewComponent removeContextMenuContributor(WebViewContextMenuContributor c);
public static boolean isContextMenuContributionSupported();

// ca.weblite.webview
@FunctionalInterface
public interface WebViewContextMenuContributor {
    List<WebViewContextMenuItem> contribute(WebViewContextMenuContext context); // engine UI thread, off the EDT
}
public final class WebViewContextMenuItem {
    static WebViewContextMenuItem of(String id, String label, Consumer<WebViewContextMenuContext> action); // action runs on the EDT
    WebViewContextMenuItem withEnabled(boolean enabled);
    String id(); String label(); boolean isEnabled(); Consumer<WebViewContextMenuContext> action();
}
public final class WebViewContextMenuContext {
    WebViewComponent source(); String selectionText(); boolean hasSelection();
    String linkUrl(); String imageUrl(); String mediaUrl(); boolean isEditable();
    Point point(); Rectangle selectionBounds(); DomTarget target(); Point toScreenPoint();
}
  • Precedence with Canvas 9: when a mouse listener suppresses the engine menu, no engine menu opens, so contributors are not asked. With setDefaultContextMenuEnabled(true) the listener is told and the engine menu opens with the contributed items. Verified on Linux.
  • Security: an item can be activated only from the native menu item, never through a JavaScript channel. Activation is tied to a token for the newest menu shown, and each menu fires at most once. Labels are shown as plain text: _ is doubled on GTK, & is doubled on WebView2, and macOS titles are literal. Labels travel as UTF-16, so emoji survive.
  • Wire format: no JSON. Native code gets a String[] answer back. The Canvas 9 base64/pipe record now has 5 more fields at the end: the selection rectangle and the selection inside a text field. The existing parser already accepts extra trailing fields.

Per-platform status

Platform Hook Implemented Compiled Run
Linux, lightweight (the only Linux mode) WebKitGTK context-menu + GAction items, using optional runtime-loaded symbols yes yes (local, and CI) yes, under Xvfb
Linux, heavyweight same hook, so both GTK engines stay alike yes yes not run (lightweight only, per CLAUDE.md)
macOS class-wide -[WKWebView willOpenMenu:withEvent:] hook that appends NSMenuItems yes only a local syntax check of the new code against stub ObjC headers; CI macOS jobs pending not run
Windows ICoreWebView2_11::add_ContextMenuRequested + ICoreWebView2Environment9::CreateContextMenuItem yes only a local syntax check of the new code with mingw against the real WebView2 1.0.2592.51 header; CI MSVC job pending not run

Linux lightweight: what was verified (NCMDEMO_AUTO=1 ./run-linux-native-context-menu-demo.sh under Xvfb → PASS, plus xdotool runs):

  • the native menu opens at the pointer with Copy and Inspect Element, then a separator and Ask;
  • choosing Ask runs its action on the EDT with the selected text, the selection rectangle and the click point;
  • link, image, editable field and textarea selection are all reported correctly;
  • disabled items are greyed out, and the label Literal: snake_case & <b>no markup</b> 😀 shows exactly as written;
  • with no contributor the menu is unchanged.
  • mvn test: 297 tests, 0 failures (26 new, in ContextMenuDispatcherTest).

Linux lightweight fixes that came with this (D9 / D9b)

  1. Placement. The engine's menu used to open at the screen's top-left corner, because WebKit anchors it to the engine window, which is parked off-screen. The component now reports its screen origin, and a GtkMenu::popped-up emission hook moves the menu under the pointer and keeps it inside the monitor's work area.
  2. The menu opening at all. On the unchanged library, a right-press held for more than a few milliseconds (which is any human click) opened no menu at all. While the button is down, the Swing window holds the X server's implicit pointer grab, so GTK cannot grab the pointer for its menu and skips the popup. The lightweight component now holds a BUTTON3 press and forwards it together with the release. One side effect: a page's right-button mousedown and contextmenu now fire at release, as they do on Windows.

Limitations

  • On macOS and Windows nothing was run, only compiled. Two things to check on a device:
    • whether WKWebView owns willOpenMenu:withEvent: (the hook handles both cases);
    • that WebView2 treats & in labels as an access key.
  • The WebView2 runtime must be 1.0.1185 or newer. On an older runtime the menu is unchanged.
  • On Linux and macOS the selected text comes from the page's right-click event (the Canvas 9 shim, top frame only). Windows reads it natively.
  • Linux menu placement assumes GTK and Java use the same display scale.
  • Items are flat: no submenus, icons or check marks.

Demo

demos/WebViewNativeContextMenuDemo and run-{linux,mac}-native-context-menu-demo.sh / run-windows-native-context-menu-demo.bat.


Generated by Claude Code

STORY-012-001 asks for the augment model beside Canvas 9's replace model:
the engine's own right-click menu keeps every native item and carries the
application's items (the Agentic App Framework's "Ask" on a selection).
The analysis records the WebKitGTK spike, including that the lightweight
component's engine menu opens at the top-left corner of the screen today.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XGTCDAC4LCHTgaKR6k2xMh
New augment-model API beside Canvas 9's replace model:
WebViewComponent.addContextMenuContributor / removeContextMenuContributor /
isContextMenuContributionSupported, WebViewContextMenuContributor,
WebViewContextMenuItem and WebViewContextMenuContext.  Every native item
stays; contributed items are appended after a separator, and the chosen
item's action runs once on the EDT with what the click hit (selected text,
link, image, media, editable, click point, selection rectangle).

- ContextMenuDispatcher builds the context from native hit-test data plus
  the newest Canvas 9 shim record (extended with the selection rectangle and
  text-control selection), asks contributors, and binds activation to a
  token of the newest shown menu.  No JavaScript path reaches activation.
- Linux: WebKitGTK "context-menu" signal with GAction items (optional
  runtime symbols).  Lightweight: the engine menu now opens at the pointer
  (moved from the parked window via a GtkMenu::popped-up hook), and a
  right-button press is forwarded at release so GTK can grab the pointer;
  before this a normal right-click opened no menu at all.
- macOS: class-wide -[WKWebView willOpenMenu:withEvent:] hook appending
  NSMenuItems.
- Windows: ICoreWebView2_11 ContextMenuRequested with CreateContextMenuItem.
- ContextMenuDispatcherTest, WebViewNativeContextMenuDemo with an automatic
  Linux check, run-*-native-context-menu-demo scripts, README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XGTCDAC4LCHTgaKR6k2xMh
@shannah
shannah marked this pull request as ready for review October 23, 2026 14:19
@shannah
shannah merged commit b0be850 into master Oct 23, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants