Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ebbb018
Playground: build via local ParparVM JavaScript target
shai-almog Jun 16, 2026
2d58a7e
js-port: thread string-hoist alias counter across chunks
shai-almog Jun 16, 2026
a45fef8
js-port: localforage shim must override fontmetrics' bundled localForage
shai-almog Jun 16, 2026
a0d49fb
Playground JS editor: fix CORS, html serving, Monaco worker/layout, b…
shai-almog Jun 16, 2026
e09addf
js-port: deliver BrowserComponent iframe->app messages (fixes editor)
shai-almog Jun 17, 2026
99ceef2
fix(js-port): anchor editor iframe URL to origin in setBrowserPageInH…
shai-almog Jun 17, 2026
e6816e9
fix(js-port): make LocalForage storage synchronous, not a Thread.slee…
shai-almog Jun 17, 2026
6a16e6f
fix(playground): import java.lang in the BeanShell eval namespace so …
shai-almog Jun 17, 2026
5273e8e
perf(playground): trim unused Monaco language bundles (~6MB raw / ~1.…
shai-almog Jun 17, 2026
343001e
fix(playground-js): editor/panel input, deep-link URLs, unified loade…
shai-almog Jun 17, 2026
5cf8a33
fix(playground-js): stop editor iframe reloading on every interaction…
shai-almog Jun 18, 2026
02e4fed
fix(playground-js): stop editor caret bouncing to 0,0 / reverting on …
shai-almog Jun 18, 2026
17459ae
perf(playground-js): cut edit->preview debounce ~1130ms -> 420ms
shai-almog Jun 18, 2026
4f420a1
perf(parparvm-js): cap the inline-goto fold; JS codegen 21min -> 2.7m…
shai-almog Jun 18, 2026
b241ae0
fix(playground-js): make GPU + gaming samples work on the ParparVM JS…
shai-almog Jun 19, 2026
7f4711d
perf(playground-js): cache per-class registry handler to kill the dis…
shai-almog Jun 19, 2026
bb864fd
fix(playground-js): repair Storage round-trip in worker so saved stat…
shai-almog Jun 19, 2026
458b207
chore(playground-js): remove dead isBytes helper from localforage shim
shai-almog Jun 19, 2026
05c3b8b
fix(js-port): render RenderView/WebGL as an in-order component so 3D …
shai-almog Jun 20, 2026
941cb86
fix(playground/js): stop RenderView rAF loop when its preview is repl…
shai-almog Jun 20, 2026
8e0bc4e
fix(playground/editor): raise edit->preview debounce 120ms -> 350ms
shai-almog Jun 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CodenameOne/src/com/codename1/gpu/RenderView.java
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,16 @@ protected void initComponent() {
} else {
revalidate();
}
} else if (placeholder.getComponentCount() == 0) {
// The platform reports GPU support but the peer could not be
// created at runtime -- e.g. a browser where WebGL is disabled,
// the GPU/driver is on the browser's WebGL blocklist, or
// privacy hardening (resistFingerprinting) blocks it. Without
// this the placeholder stays empty and the view is a silent
// blank with no hint as to why; show a message instead.
placeholder.setLayout(new BorderLayout());
placeholder.add(BorderLayout.CENTER, new Label("3D unavailable (WebGL blocked or disabled)"));
revalidate();
}
}
}
Expand Down
Binary file added Ports/JavaSE/src/AndroidMaterialTheme.res
Binary file not shown.
Binary file added Ports/JavaSE/src/androidTheme.res
Binary file not shown.
Binary file added Ports/JavaSE/src/android_holo_light.res
Binary file not shown.
Binary file added Ports/JavaSE/src/iOSModernTheme.res
Binary file not shown.
Binary file added Ports/JavaSE/src/iPhoneTheme.res
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,18 @@ public void drawImage(Object img, int x, int y, int w, int h) {
imageTransformRenderAdapter.drawImage((NativeImage)img, x, y, w, h);
}

/// Buffers a blit of a raw canvas (an offscreen WebGL render target) into the
/// display op stream. Used by the GPU compositing path so a RenderView's 3D
/// frame is drawn onto the display surface in flushGraphics(), layering with
/// the rest of the UI -- unlike {@link #drawImage} this takes a live canvas,
/// not a NativeImage.
public void drawCanvas(com.codename1.html5.js.dom.HTMLCanvasElement canvas, int x, int y, int w, int h) {
if (canvas == null || w <= 0 || h <= 0) {
return;
}
upcoming.add(new com.codename1.impl.html5.graphics.DrawCanvas(canvas, x, y, w, h, 255));
}

@Override
public void tileImage(Object img, int x, int y, int w, int h) {
imageTransformRenderAdapter.tileImage((NativeImage)img, x, y, w, h);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,20 +67,22 @@ public class HTML5BrowserComponent extends HTML5Peer {

@Override
public void handleEvent(final MessageEvent e) {
Window win = iframe == null ? Window.current() : iframe.getContentWindow();
//HTML5Implementation._log("Received event "+e.getDataAsString());
if (getEventSource(e) == win) {
//HTML5Implementation._log("From our iframe");
// Original code only forwarded the message when
// ``getEventSource(e) == iframe.getContentWindow()``. On the worker-
// based ParparVM port those are two different worker-side wrappers
// for the same window (the comparison is always false), so onMessage
// never fired at all. The worker cannot reliably compare window
// identity, so instead forward any message that carries a source
// (i.e. came from a posting window such as our iframe). Apps that
// host multiple frames disambiguate in their own onMessage handler.
if (getEventSource(e) != null) {
final String data = e.getDataAsString();
HTML5Implementation.callSerially(new Runnable() {
public void run() {
parent.fireWebEvent(BrowserComponent.onMessage, new ActionEvent(e.getDataAsString()));

parent.fireWebEvent(BrowserComponent.onMessage, new ActionEvent(data));
}
});
} else {
//HTML5Implementation._log("Not from our iframe");
}

}

};
Expand Down Expand Up @@ -154,7 +156,30 @@ public void handleEvent(Event event) {
if (iframe == null) {
return;
}
// These listeners live on the iframe's own content window, so they
// only fire when a pointer/mouse/touch event actually reaches the
// iframe -- i.e. when #codenameone-canvas has been flipped to
// pointer-events:none over a transparent ("punched") hole because the
// peer is showing through (see browser_bridge.js installPeerPointerToggle).
// In that state the event genuinely belongs to the peer (e.g. typing /
// clicking in the Playground's Monaco editor), so re-dispatching a
// synthetic copy back into CN1 is wrong: CN1 treats it as a press on the
// peer component and relayout/refocus churn reloads the iframe, wiping
// the editor back to its bootstrap source. When the canvas is opaque the
// event never reaches here (the canvas consumes it), so there is nothing
// to forward. Hence: if the canvas is "none", let the peer keep the event.
HTMLElement oc = HTML5Implementation.getInstance().outputCanvas;
if (oc != null && "none".equals(oc.getStyle().getPropertyValue("pointer-events"))) {
return;
}
TextRectangle clRect = iframe.getBoundingClientRect();
if (clRect == null) {
// getBoundingClientRect can come back null when invoked through the
// worker JSO bridge (the iframe arg is a host-ref proxy); without a
// rect we cannot offset coordinates, and dereferencing it throws an
// NPE on every event. Nothing to forward -- leave the event to the peer.
return;
}
Event evt;
if ("MozMousePixelScroll".equals(eventType) || eventType.equals(HTML5Implementation.getWheelEventType())) {
evt = copyWheelEvent(event, iframe, clRect.getLeft(), clRect.getTop());
Expand Down Expand Up @@ -252,8 +277,29 @@ private void uncancelScroll() {
private static native boolean supportsSrcdocAttribute();
private boolean supportsSrcdocAttribute;

@JSBody(params={"iframe"}, script="try{if(iframe.contentWindow.document){return false} else {return true}}catch(e){return true}")
private native static boolean isCORSRestricted(HTMLIFrameElement iframe);
// NOTE: this must NOT be an @JSBody. On the ParparVM worker model an
// @JSBody script runs in the worker, where the ``iframe`` argument is a
// host-ref proxy with no live DOM -- ``iframe.contentWindow`` is undefined,
// so the old inline probe always threw and reported EVERY BrowserComponent
// (even a same-origin one like the Playground editor) as CORS-restricted,
// which made execute()/executeAndReturnString() throw and the editor never
// bootstrapped. Probe through the JSO bridge instead so contentWindow /
// document access runs on the MAIN thread where it is meaningful: a
// same-origin iframe yields a non-null document; a genuinely cross-origin
// one throws on access (caught here) and is correctly reported restricted.
// (TeaVM ran everything on the main thread, so its @JSBody worked -- this
// only bit the worker-based ParparVM port.)
private static boolean isCORSRestricted(HTMLIFrameElement iframe) {
try {
Window cw = iframe.getContentWindow();
if (cw == null) {
return true;
}
return cw.getDocument() == null;
} catch (Throwable t) {
return true;
}
}

private boolean listenersInstalled;
private List<EventListener> frameListeners;
Expand Down Expand Up @@ -534,18 +580,26 @@ public void reload(){
setURL(getURL());
}

@JSBody(
params={"el"},
script="var doc = el ? el.ownerDocument : null;"
+ "if (!doc) { return false; }"
+ "if (doc.documentElement && typeof doc.documentElement.contains === 'function') {"
+ " return doc.documentElement.contains(el);"
+ "}"
+ "if (doc.body && typeof doc.body.contains === 'function') {"
+ " return doc.body.contains(el);"
+ "}"
+ "return !!el.isConnected;")
private native static boolean documentContains(HTMLElement el);
// NOTE: this must NOT be an @JSBody. On the ParparVM worker model an @JSBody
// script runs in the worker, where ``el`` is a host-ref proxy with no live DOM,
// so ``doc.documentElement.contains(el)`` compares the worker's doc proxy
// against the el proxy and ALWAYS returns false. initComponent() then thinks the
// iframe was never added and re-appends it on every call -- and re-inserting an
// iframe RELOADS it, which re-runs editor.js / re-bootstraps Monaco and wipes the
// user's edits back to the bootstrap source ("typed character erased immediately"
// / "no interaction"). Because clicking a peer now actually reaches it (the
// pointer-events toggle), initComponent gets driven repeatedly and the editor
// reloaded on every interaction. Probe through the JSO bridge instead:
// getParentNode() runs on the MAIN thread and reliably reports null (detached)
// vs a real parent, and the peer's only parent is the in-document peers
// container. Same class of worker-proxy bug as isCORSRestricted() below.
private static boolean documentContains(HTMLElement el) {
try {
return el != null && el.getParentNode() != null;
} catch (Throwable t) {
return false;
}
}


@Override
Expand Down Expand Up @@ -611,7 +665,7 @@ public void execute(String javascript){
throw new RuntimeException("Cannot execute javascript in this browser component because it is CORS-restricted. Javascript was "+javascript);
}
WindowExt win = iframe == null ? ((WindowExt)Window.current()) : (WindowExt)iframe.getContentWindow();

win.eval(javascript);
//Window win = iframe.getContentWindow();
//win.getLocation().assign("javascript:"+javascript);
Expand Down
Loading
Loading