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]

Reply via email to