Skip to content

Commit 3243948

Browse files
lvcabralclaude
andcommitted
fix(scenegraph): keep m.global's own descendants out of its findNode search
Device-confirmed: a node appended to m.global is found by neither m.global.findNode() nor m.top.findNode(). findNode's search space is the subject node's nearest component ancestor; for the global node that is the Scene, and the global node itself sits outside the Scene's children, so its own subtree is not in that space. This engine searched the subject's own subtree first and found it. Skip that self-search for the global node. Every other node's subtree is part of its component ancestor's subtree anyway, so searching it first is only a shortcut and their behavior is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent e4e24b6 commit 3243948

2 files changed

Lines changed: 26 additions & 2 deletions

File tree

src/extensions/scenegraph/components/RoSGNode.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1769,8 +1769,14 @@ export abstract class RoSGNode extends BrsComponent implements BrsValue, ISGNode
17691769
}
17701770
const id = name.getValue();
17711771
if (id.trim() === "") return BrsInvalid.Instance;
1772-
// perform search to child nodes
1773-
let node = this.findNodeById(this, id);
1772+
// The search space is the subject node's nearest component ancestor. The global
1773+
// node's is the Scene, and the global node itself sits *outside* the Scene's
1774+
// children, so its own descendants are not in that space: a device does not find a
1775+
// node appended to m.global via m.global.findNode(). Skipping the self-search keeps
1776+
// that boundary — every other node's subtree is part of its ancestor's anyway, so
1777+
// searching it first is only a shortcut.
1778+
const searchSelf: boolean = sgRoot.mGlobal !== this;
1779+
let node: RoSGNode | BrsInvalid = searchSelf ? this.findNodeById(this, id) : BrsInvalid.Instance;
17741780
if (node instanceof BrsInvalid) {
17751781
// if not found, search from root
17761782
node = this.findNodeById(this.findRootNode(), id);

test/extensions/scenegraph/GlobalFindNode.test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,24 @@ describe("m.global is parented to the Scene", () => {
3232
expect(scene.getNodeChildren()).not.toContain(sgRoot.mGlobal);
3333
});
3434

35+
test("does not find its own descendants — the search space is the Scene", () => {
36+
const scene = SGNodeFactory.createNode("Scene");
37+
sgRoot.setScene(scene);
38+
const ownChild = new Node([], "Node");
39+
ownChild.setValue("id", new BrsString("GlobalOwnChild"), false);
40+
sgRoot.mGlobal.appendChildToParent(ownChild);
41+
42+
const interpreter = new Interpreter();
43+
// Device-confirmed: a node appended to m.global is reachable from neither search, because
44+
// the global node sits outside the Scene's children.
45+
const globalFind = sgRoot.mGlobal.getMethod("findnode");
46+
const args = [new BrsString("GlobalOwnChild")];
47+
expect(isInvalid(interpreter.call(globalFind, args, sgRoot.mGlobal.m, interpreter.location))).toBe(true);
48+
49+
const sceneFind = scene.getMethod("findnode");
50+
expect(isInvalid(interpreter.call(sceneFind, args, scene.m, interpreter.location))).toBe(true);
51+
});
52+
3553
test("finds a scene node by id from the global node", () => {
3654
const scene = SGNodeFactory.createNode("Scene");
3755
const screen = new Node([], "Group");

0 commit comments

Comments
 (0)