From c06aae839f89ec4cc2b56dcad937ebd4d980225c Mon Sep 17 00:00:00 2001 From: "mr. m" Date: Sat, 1 Aug 2026 22:16:50 +0200 Subject: [PATCH] fix comments --- src/zen/tests/moz.build | 1 + src/zen/tests/window_drag/browser.toml | 10 ++ .../window_drag/browser_window_drag_basic.js | 109 ++++++++++++++++++ src/zen/tests/window_drag/head.js | 57 +++++++++ .../actors/ZenWindowDragChild.sys.mjs | 8 +- .../actors/ZenWindowDragParent.sys.mjs | 6 + 6 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 src/zen/tests/window_drag/browser.toml create mode 100644 src/zen/tests/window_drag/browser_window_drag_basic.js create mode 100644 src/zen/tests/window_drag/head.js diff --git a/src/zen/tests/moz.build b/src/zen/tests/moz.build index fbbf132a8..3e3969fdf 100644 --- a/src/zen/tests/moz.build +++ b/src/zen/tests/moz.build @@ -20,6 +20,7 @@ BROWSER_CHROME_MANIFESTS += [ "ub-actions/browser.toml", "urlbar/browser.toml", "welcome/browser.toml", + "window_drag/browser.toml", "window_sync/browser.toml", ] diff --git a/src/zen/tests/window_drag/browser.toml b/src/zen/tests/window_drag/browser.toml new file mode 100644 index 000000000..6e8dac13c --- /dev/null +++ b/src/zen/tests/window_drag/browser.toml @@ -0,0 +1,10 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +[DEFAULT] +support-files = [ + "head.js", +] + +["browser_window_drag_basic.js"] diff --git a/src/zen/tests/window_drag/browser_window_drag_basic.js b/src/zen/tests/window_drag/browser_window_drag_basic.js new file mode 100644 index 000000000..7887fb68b --- /dev/null +++ b/src/zen/tests/window_drag/browser_window_drag_basic.js @@ -0,0 +1,109 @@ +/* Any copyright is dedicated to the Public Domain. + https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +let gDragStartCount = 0; + +add_setup(async function () { + const observer = () => gDragStartCount++; + Services.obs.addObserver(observer, WINDOW_DRAG_TOPIC); + registerCleanupFunction(() => { + Services.obs.removeObserver(observer, WINDOW_DRAG_TOPIC); + }); +}); + +add_task(async function test_drag_from_empty_top_area() { + await BrowserTestUtils.withNewTab(WINDOW_DRAG_TEST_PAGE, async browser => { + await synthesizeContentDrag(browser, 50, 50); + await TestUtils.waitForCondition( + () => gDragStartCount === 1, + "Dragging an empty area in the top region should start a window drag" + ); + is(gDragStartCount, 1, "Exactly one window drag started"); + }); +}); + +add_task(async function test_no_drag_on_interactive_target() { + await BrowserTestUtils.withNewTab(WINDOW_DRAG_TEST_PAGE, async browser => { + // Start the gesture on top of the link. Messages from the same actor + // pair are ordered, so the control drag afterwards would arrive later. + await BrowserTestUtils.synthesizeMouse( + "#link", + 5, + 5, + { type: "mousedown" }, + browser + ); + await BrowserTestUtils.synthesizeMouse( + "#link", + 35, + 15, + { type: "mousemove", buttons: 1 }, + browser + ); + await BrowserTestUtils.synthesizeMouse( + "#link", + 45, + 20, + { type: "mouseup" }, + browser + ); + + // Control drag from an eligible area. + await synthesizeContentDrag(browser, 50, 50); + await TestUtils.waitForCondition( + () => gDragStartCount >= 2, + "The control drag should start a window drag" + ); + is( + gDragStartCount, + 2, + "Dragging a link must not start a window drag (only the control did)" + ); + }); +}); + +add_task(async function test_no_drag_below_top_region() { + await BrowserTestUtils.withNewTab(WINDOW_DRAG_TEST_PAGE, async browser => { + const innerHeight = await getContentInnerHeight(browser); + await synthesizeContentDrag(browser, 50, Math.floor(innerHeight * 0.6)); + + // Control drag from inside the top region. + await synthesizeContentDrag(browser, 50, 50); + await TestUtils.waitForCondition( + () => gDragStartCount >= 3, + "The control drag should start a window drag" + ); + is( + gDragStartCount, + 3, + "Dragging below the top region must not start a window drag" + ); + }); +}); + +add_task(async function test_pref_disables_window_drag() { + await SpecialPowers.pushPrefEnv({ + set: [["zen.view.drag-window-from-content", false]], + }); + await BrowserTestUtils.withNewTab(WINDOW_DRAG_TEST_PAGE, async browser => { + await synthesizeContentDrag(browser, 50, 50); + }); + await SpecialPowers.popPrefEnv(); + + // Control drag with the pref back on, in a fresh tab so the actor is + // created again. + await BrowserTestUtils.withNewTab(WINDOW_DRAG_TEST_PAGE, async browser => { + await synthesizeContentDrag(browser, 50, 50); + await TestUtils.waitForCondition( + () => gDragStartCount >= 4, + "The control drag should start a window drag" + ); + is( + gDragStartCount, + 4, + "Dragging with the pref disabled must not start a window drag" + ); + }); +}); diff --git a/src/zen/tests/window_drag/head.js b/src/zen/tests/window_drag/head.js new file mode 100644 index 000000000..cd7e1dccb --- /dev/null +++ b/src/zen/tests/window_drag/head.js @@ -0,0 +1,57 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const WINDOW_DRAG_TOPIC = "zen-window-drag-started"; + +const WINDOW_DRAG_TEST_PAGE = `https://example.com/document-builder.sjs?html=${encodeURIComponent(` + + + a link +`)}`; + +/** + * Synthesizes a primary-button drag gesture inside the content area, + * starting at (x, y) and moving well past the drag threshold. + */ +async function synthesizeContentDrag(browser, x, y) { + await BrowserTestUtils.synthesizeMouse( + null, + x, + y, + { type: "mousedown" }, + browser + ); + for (let i = 1; i <= 3; i++) { + await BrowserTestUtils.synthesizeMouse( + null, + x + i * 10, + y + i * 5, + { type: "mousemove", buttons: 1 }, + browser + ); + } + await BrowserTestUtils.synthesizeMouse( + null, + x + 40, + y + 20, + { type: "mouseup" }, + browser + ); +} + +function getContentInnerHeight(browser) { + return SpecialPowers.spawn(browser, [], () => content.innerHeight); +} diff --git a/src/zen/window-drag/actors/ZenWindowDragChild.sys.mjs b/src/zen/window-drag/actors/ZenWindowDragChild.sys.mjs index 95aeecf4b..1ef721739 100644 --- a/src/zen/window-drag/actors/ZenWindowDragChild.sys.mjs +++ b/src/zen/window-drag/actors/ZenWindowDragChild.sys.mjs @@ -103,6 +103,10 @@ export class ZenWindowDragChild extends JSWindowActorChild { #startScreenY = 0; handleEvent(event) { + // Never let pages spoof the gesture with synthetic events. + if (!event.isTrusted) { + return; + } switch (event.type) { case "mousedown": this.#onMouseDown(event); @@ -321,7 +325,9 @@ export class ZenWindowDragChild extends JSWindowActorChild { if (!style) { return false; } - const cursor = style.cursor.split(",").pop().trim(); + // The keyword is always the last component of the computed value. + // Don't split on "," as url() cursor images may contain commas. + const cursor = style.cursor.match(/[a-z-]+$/)?.[0]; return kInteractiveCursors.has(cursor); } } diff --git a/src/zen/window-drag/actors/ZenWindowDragParent.sys.mjs b/src/zen/window-drag/actors/ZenWindowDragParent.sys.mjs index fa6607a0a..3d92ab614 100644 --- a/src/zen/window-drag/actors/ZenWindowDragParent.sys.mjs +++ b/src/zen/window-drag/actors/ZenWindowDragParent.sys.mjs @@ -22,6 +22,12 @@ export class ZenWindowDragParent extends JSWindowActorParent { if (!win || win.closed || win.windowState === win.STATE_FULLSCREEN) { return; } + if (Cu.isInAutomation) { + // Tests can't exercise a real OS drag session; let them observe the + // decision instead. + Services.obs.notifyObservers(win, "zen-window-drag-started"); + return; + } lazy.zenDragAndDropService.beginNativeWindowMove(win); } }