Skip to content

Commit 752c3f5

Browse files
Copilotshai-almog
andauthored
Skip translated screenshot method to avoid canvasToBlob hang; add Uint8ClampedArray to inferFn
Agent-Logs-Url: https://github.com/codenameone/CodenameOne/sessions/ede5220b-d007-4fb6-860a-248673dd964b Co-authored-by: shai-almog <67850168+shai-almog@users.noreply.github.com>
1 parent 4c94c89 commit 752c3f5

2 files changed

Lines changed: 26 additions & 22 deletions

File tree

Ports/JavaScriptPort/STATUS.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,22 @@ What Was Fixed In This Pass
236236
- All CN1SS chunk data reaches Playwright, enabling screenshot extraction.
237237
- Playwright exits promptly after suite completion, saving CI time.
238238

239+
18. Fixed screenshot hang caused by canvasToBlob async callback across worker boundary.
240+
- File: `Ports/JavaScriptPort/src/main/webapp/port.js`
241+
- Root cause:
242+
- The translated screenshot method calls `ImageIO.save()` which calls
243+
`BlobUtil.canvasToBlob()`. That method uses the async
244+
`HTMLCanvasElement.toBlob(BlobCallback)` browser API. In the worker
245+
architecture the BlobCallback is a Java object that cannot be invoked
246+
from the host thread, so `canvasToBlob()` hangs forever in
247+
`while (!complete) { lock.wait(200); }`.
248+
- Fix:
249+
- `emitCurrentFormScreenshotDom` now always uses the DOM-based host
250+
bridge capture path (`__cn1_capture_canvas_png__`) instead of the
251+
translated screenshot method. This avoids async callbacks entirely.
252+
- Also added `Uint8ClampedArray` to the JSO `inferFn` for proper type
253+
recognition when wrapping typed arrays received from the host.
254+
239255
Known Failing Symptoms (Latest CI Logs/Artifacts)
240256
-------------------------------------------------
241257

Ports/JavaScriptPort/src/main/webapp/port.js

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
if (typeof global.ArrayBuffer !== "undefined" && value instanceof global.ArrayBuffer) {
2727
return "com_codename1_html5_js_typedarrays_ArrayBuffer";
2828
}
29+
if (typeof global.Uint8ClampedArray !== "undefined" && value instanceof global.Uint8ClampedArray) {
30+
return "com_codename1_html5_js_typedarrays_Uint8ClampedArray";
31+
}
2932
if (typeof global.Uint8Array !== "undefined" && value instanceof global.Uint8Array) {
3033
return "com_codename1_html5_js_typedarrays_Uint8Array";
3134
}
@@ -3227,31 +3230,16 @@ bindCiFallback("Cn1ssDeviceRunnerHelper.emitCurrentFormScreenshotDom", [
32273230
cn1ssEmitCurrentFormScreenshotMethodId + "__impl",
32283231
cn1ssEmitCurrentFormScreenshotMethodId
32293232
], cn1ssHelperClassName, fallbackSymbol);
3233+
// In worker mode the translated screenshot path eventually calls
3234+
// BlobUtil.canvasToBlob() which uses HTMLCanvasElement.toBlob(callback).
3235+
// That callback is a Java object and cannot be invoked from the host
3236+
// thread, so the worker hangs forever in a wait-loop. Always use the
3237+
// DOM-based capture via host bridge calls instead – this avoids async
3238+
// callbacks entirely and works reliably across the worker boundary.
32303239
if (originalResolved && typeof originalResolved.fn === "function") {
3231-
if (cn1ssEmitCurrentFormScreenshotInvokeDepth > 0) {
3232-
emitDiagLine("PARPAR:DIAG:FALLBACK:cn1ssEmitCurrentFormScreenshotDom:originalReentryBypass=1");
3233-
shouldUseDomFallback = true;
3234-
} else {
3235-
try {
3236-
cn1ssEmitCurrentFormScreenshotInvokeDepth++;
3237-
emitDiagLine("PARPAR:DIAG:FALLBACK:cn1ssEmitCurrentFormScreenshotDom:originalResolved=" + originalResolved.source);
3238-
yield* originalResolved.fn(testName, completion);
3239-
return null;
3240-
} catch (originalErr) {
3241-
emitDiagLine("PARPAR:DIAG:FALLBACK:cn1ssEmitCurrentFormScreenshotDom:originalInvokeErr="
3242-
+ String(originalErr && originalErr.message ? originalErr.message : originalErr));
3243-
if (originalErr && originalErr.stack) {
3244-
emitDiagLine("PARPAR:DIAG:FALLBACK:cn1ssEmitCurrentFormScreenshotDom:originalInvokeStack="
3245-
+ String(originalErr.stack).split("\n").slice(0, 2).join(" | "));
3246-
}
3247-
shouldUseDomFallback = true;
3248-
} finally {
3249-
cn1ssEmitCurrentFormScreenshotInvokeDepth = Math.max(0, cn1ssEmitCurrentFormScreenshotInvokeDepth - 1);
3250-
}
3251-
}
3240+
emitDiagLine("PARPAR:DIAG:FALLBACK:cn1ssEmitCurrentFormScreenshotDom:skipTranslated=canvasToBlob_hang");
32523241
} else {
32533242
emitDiagLine("PARPAR:DIAG:FALLBACK:cn1ssEmitCurrentFormScreenshotDom:originalMissing=1");
3254-
shouldUseDomFallback = true;
32553243
}
32563244
const canvas = global.document && typeof global.document.querySelector === "function"
32573245
? global.document.querySelector("canvas")

0 commit comments

Comments
 (0)