fix: CJK browser_search panic and multi-tab tab_close crash (#257, #258)

* Fix UTF-8 char boundary panic in browser_search

The search snippet window sliced body[..start] / body[end..] at byte
offsets derived from char counts and needle.len(), which can land inside
a multi-byte (CJK) character and panic, killing the MCP stdio transport.
Snap start/end to char boundaries before slicing and advance past
whitespace by the actual char width. Closes #257.

* Add setLineDash and friends to the canvas 2D mock

Charting libraries (Highcharts, ECharts) call setLineDash/getLineDash/
ellipse every animation frame. The missing setLineDash threw 'is not a
function' from a timer each tick, spamming the console. Adds the
line-dash methods plus ellipse/roundRect/createConicGradient/
getContextAttributes stubs. Addresses the error spam in #258; the
multi-tab isolate-teardown crash is a separate deeper issue.

* Fix multi-tab tab_close V8 isolate abort

rusty_v8 enters each isolate on creation and requires them dropped in
reverse order of creation. The MCP gave each tab its own isolate and
dropped them in arbitrary (user-chosen) order on tab_close, so closing a
non-newest tab aborted the process (Check failed: heap->isolate() ==
Isolate::TryGetCurrent()). Pages with active timers (Highcharts/ECharts)
made it easy to hit.

Enforce the CDP server's single-live-isolate discipline in the MCP: at
most one tab's isolate is ever live. page_mut calls a new activate() that
suspends every other tab (drops its isolate, keeps its DOM) and resumes
the active one. close_tab drops the closed tab's isolate via suspend_js
while it is still the only entered isolate, and tool_close suspends all
tabs before clearing. Confined to obscura-mcp; the engine, CDP server and
obstacle course are untouched, and peak memory drops to one isolate
instead of N. Closes #258.
This commit is contained in:
SGavrl authored and GitHub committed 2026-06-07 11:44:49 +02:00
1 parent c1e0b0fb5a
commit fb2841b7ab
2 files changed
+59 -9

No files matched your search

+9
View File
@@ -5029,6 +5029,15 @@ class _Canvas2D {
createPattern() { return {}; }
isPointInPath() { return false; }
isPointInStroke() { return false; }
// Line-dash plus a few path/style methods that charting libraries (Highcharts,
// ECharts) call on every animation frame. A missing setLineDash threw
// "is not a function" from a timer each tick, spamming errors (#258).
setLineDash() {}
getLineDash() { return []; }
ellipse() {}
roundRect() {}
createConicGradient() { return { addColorStop(){} }; }
getContextAttributes() { return { alpha: true, desynchronized: false, colorSpace: "srgb", willReadFrequently: false }; }
}
Element.prototype.getContext = function getContext(type) {