mahdi-chihaoui-speedykom opened a new issue, #8286:
URL: https://github.com/apache/hop/issues/8286
### Apache Hop version?
SNAPSHOT-20260908 — verified against main @ e6d9ef48f6; also in 2.19.0.
### Java version?
OpenJDK 21
### Operating system
Docker
### What happened?
<!--
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-->
Hop Web renders pipeline/workflow graphs as a server-rendered SVG snapshot
placed in an
HTML overlay above the RAP canvas widget. The browser side must decide
*which* DOM element
that overlay attaches to. The resolver guesses when its intended target is
not yet in the
DOM, and the guess is unsound:
https://github.com/apache/hop/blob/e6d9ef48f6ebc834d57b7e6d59b9dfeb08a748b3/rap/src/main/resources/org/apache/hop/ui/hopgui/canvas-svg.js#L47-L73
```js
function findCanvasForWidget(canvasId) {
if (canvasId) {
var widgetElement = document.getElementById(canvasId);
if (widgetElement) {
if (widgetElement.tagName === "CANVAS") return widgetElement;
var nestedCanvas = widgetElement.querySelector("canvas");
if (nestedCanvas) return nestedCanvas;
}
}
return findVisibleGraphCanvas(); // <-- guesses
}
function findVisibleGraphCanvas() {
// returns the FIRST canvas in the document larger than 500x500
}
```
The server always sends the correct widget id
(`CanvasSvgRendererHandler.updateRemoteObject`
sets `canvasId` before calling `attachListener`), so the guess is never
*needed* — but it is
reached in two situations:
1. the widget element is not in the DOM yet (initial RAP layout), or
2. it is, but RAP has not yet lazily created its nested `<canvas>` (that
element only appears
on the first GC draw).
In both cases control reaches `findVisibleGraphCanvas()`, whose only test is
`rect.width > 500 && rect.height > 500`. That single line produces three
distinct bugs:
| Document state | Guess returns | Symptom |
|---|---|---|
| graph canvas absent, viewport large | the graph canvas | works *by
accident* |
| graph canvas absent, a dialog is open | **the dialog's canvas** | graph
SVG renders inside the dialog |
| graph canvas absent, viewport small | **`null`** | nothing renders at all |
**Row 3 is the most visible.** When no canvas in the document clears 500x500,
`_findAndAttachCanvas` retries 20 x 100 ms and then **gives up silently**:
https://github.com/apache/hop/blob/e6d9ef48f6ebc834d57b7e6d59b9dfeb08a748b3/rap/src/main/resources/org/apache/hop/ui/hopgui/canvas-svg.js#L384-L404
`_attachToCanvas` is never called, so there is no overlay, no `canvasRender`
request, and no
console error — just a blank canvas. Enlarging the window or going
fullscreen triggers an SWT
paint, which calls `attachListener` again; the canvas now clears 500x500 and
the graph appears.
Note the threshold applies to the **canvas rect, not the window**: the
perspective sidebar,
toolbar, tab bar and the `SashForm` the canvas shares with the
execution/results panel all
subtract from it. So there is no clean window breakpoint, and dragging that
sash reproduces
the blank canvas at a fixed window size.
The same resolver and the same 500x500 threshold are duplicated in
`canvas-zoom.js`
(its comment reads "same approach as canvas-svg.js"), so mouse-wheel zoom is
dead under the
same conditions:
https://github.com/apache/hop/blob/e6d9ef48f6ebc834d57b7e6d59b9dfeb08a748b3/rap/src/main/resources/org/apache/hop/ui/hopgui/canvas-zoom.js#L95-L118
This is not a server-side or authorization problem:
`HopGuiPipelineGraph.paintControl` skips
only when a dimension is exactly `0`, and `PipelineCanvasSvgRenderer` has no
size gate. The
snapshot is rendered and stored correctly; the browser simply never attaches
it.
The overlay and its fallback were introduced in #7431 (issue #7430), which
added
`canvas-svg.js`. The fallback was a reasonable safety net at the time; it
has since become
the failure mode.
### Steps to reproduce
Blank canvas (row 3):
1. Open Hop Web in a browser window roughly 800x600 or smaller (or keep the
window large and
drag the execution/results sash up until the canvas is under ~500 px
tall).
2. Open any pipeline.
3. The canvas is blank. DevTools shows no `canvasRender` request and no
error.
4. Maximise the window -> the graph appears. Shrink it again -> it *stays*
rendered, because
the polling loop only re-syncs layout and never re-resolves the target.
That last asymmetry is the giveaway: a genuine small-canvas rendering bug
would also break on
the shrink.
Graph inside a dialog (row 2):
1. Load a pipeline in a fresh session (embedding Hop Web in an iframe widens
the window).
2. Open a transform dialog or the context/add-transform dialog while the
graph is still
resolving.
3. The graph SVG is appended under the dialog's DOM subtree.
### Proposed fix
Resolve strictly from the server-provided widget id and delete the guess.
```js
/**
* Resolve the element the SVG overlay anchors to, strictly from the widget
id the server
* sent. Returns null when the widget is not in the DOM yet; the caller
retries. Never
* resolves to an unrelated canvas.
*/
function findCanvasForWidget(canvasId) {
if (!canvasId) return null;
var widgetElement = document.getElementById(canvasId);
if (!widgetElement) return null;
if (widgetElement.tagName === "CANVAS") return widgetElement;
// RAP creates the nested <canvas> lazily on the first GC draw. Prefer
it so the overlay
// keeps its existing parent and geometry, but never treat its absence
as a reason to
// look elsewhere.
return widgetElement.querySelector("canvas");
}
```
`findVisibleGraphCanvas()` can then be removed entirely — it becomes
unreachable, since
`canvasId` is always set before `attachListener`.
Second, make exhaustion degrade instead of failing silently. In
`_findAndAttachCanvas`, after
the retries, anchor to the widget element itself and warn:
```js
if (!canvas) {
if (attempts < maxAttempts) { setTimeout(tryFind, 100); return; }
var widget = self._canvasId ? document.getElementById(self._canvasId) :
null;
if (widget) { self._attachToCanvas(widget); return; }
console.warn("hop.CanvasSvgRenderer: widget " + self._canvasId +
" never appeared; graph overlay not attached");
return;
}
```
Anchoring to the widget element is coordinate-safe: every mouse handler
derives graph
coordinates as `event.clientX - this._canvas.getBoundingClientRect().left`
against whichever
element `_canvas` points at, and never uses the target-relative
`event.offsetX`.
`_syncOverlayLayout` reads its geometry from the same element, so the two
stay consistent.
Apply both changes to `canvas-zoom.js` as well.
Incidentally, `canvas-zoom.js` line 86 (`setCanvas`) reads
`properties.canvasId` while the
server sets the property as `"canvas"`. It is currently harmless because the
registered
`propertyHandler.canvas` intercepts and `setCanvas` never runs, but it would
null out
`_canvasId` and force the fallback on every update if it were ever wired up.
Worth deleting
along with the equivalent dead `setCanvasId` in `canvas-svg.js`.
I have this fix working against 2.19.0 and am happy to open a PR.
### Possibly the same root cause
#8281 ("Hop Web gets confused when floating the results panel") may well be
this bug: floating
the results panel resizes the graph canvas, and the reported behaviour of
the graph
re-surfacing when the context dialog appears, and reappearing while a
selection lasso is
dragged, both match repaint-triggered re-resolution. Worth cross-checking
before treating them
as separate.
### Issue Priority
Priority: 2
### Issue Component
Component: Hop Web
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]