diff --git a/core/src/player.rs b/core/src/player.rs index 78af833a868d..8666c733466e 100644 --- a/core/src/player.rs +++ b/core/src/player.rs @@ -1374,8 +1374,12 @@ impl Player { }; if let Some(target) = target { let event = ClipEvent::MouseWheel { delta }; - target.event_dispatch_to_avm2(context, event); - target.handle_clip_event(context, event); + if target.event_dispatch_to_avm2(context, event) == ClipEventResult::Handled { + player_event_handled = true; + } + if target.handle_clip_event(context, event) == ClipEventResult::Handled { + player_event_handled = true; + } } }); } diff --git a/web/packages/selfhosted/test/integration_tests/mouse_wheel_avm2/Test.as b/web/packages/selfhosted/test/integration_tests/mouse_wheel_avm2/Test.as new file mode 100644 index 000000000000..d83fe9b62b4d --- /dev/null +++ b/web/packages/selfhosted/test/integration_tests/mouse_wheel_avm2/Test.as @@ -0,0 +1,65 @@ +package { + import flash.display.*; + import flash.text.*; + import flash.events.*; + + [SWF(width="800", height="400")] + public class Test extends MovieClip { + private var text: TextField; + + public function Test() { + var a = newSprite() + a.x = 0; + a.y = 0; + a.width = 200; + a.height = 400; + a.addEventListener(MouseEvent.MOUSE_WHEEL, consumeWheel1); + addChild(a); + + var b = newSprite() + b.x = 400; + b.y = 0; + b.width = 200; + b.height = 400; + b.addEventListener(MouseEvent.MOUSE_WHEEL, consumeWheel2); + addChild(b); + + var c = new TextField(); + c.mouseWheelEnabled = true; + c.border = true; + c.x = 200; + c.y = 0; + c.width = 200; + c.height = 400; + c.multiline = true; + for (var i = 0; i < 100; ++ i) { + c.text += "line\n"; + } + addChild(c); + text = c; + + trace("Loaded!"); + } + + function consumeWheel1(event: MouseEvent) { + trace("Wheel consumed 1, vscroll: " + text.scrollV); + } + + function consumeWheel2(event: MouseEvent) { + trace("Wheel consumed 2, vscroll: " + text.scrollV); + event.preventDefault(); + } + + function handleScroll(event: Event) { + trace("Scrolled"); + } + + private function newSprite(): Sprite { + var s:Sprite = new Sprite(); + s.graphics.beginFill(0xFF00FF); + s.graphics.drawRect(0, 0, 200, 400); + s.graphics.endFill(); + return s; + } + } +} diff --git a/web/packages/selfhosted/test/integration_tests/mouse_wheel_avm2/index.html b/web/packages/selfhosted/test/integration_tests/mouse_wheel_avm2/index.html new file mode 100644 index 000000000000..03eff6c95034 --- /dev/null +++ b/web/packages/selfhosted/test/integration_tests/mouse_wheel_avm2/index.html @@ -0,0 +1,15 @@ + + + + + mouse_wheel_avm2 + + + + +
+ +
+ + + diff --git a/web/packages/selfhosted/test/integration_tests/mouse_wheel_avm2/test.swf b/web/packages/selfhosted/test/integration_tests/mouse_wheel_avm2/test.swf new file mode 100644 index 000000000000..8b1e37ef6c4e Binary files /dev/null and b/web/packages/selfhosted/test/integration_tests/mouse_wheel_avm2/test.swf differ diff --git a/web/packages/selfhosted/test/integration_tests/mouse_wheel_avm2/test.ts b/web/packages/selfhosted/test/integration_tests/mouse_wheel_avm2/test.ts new file mode 100644 index 000000000000..8231893fdbb2 --- /dev/null +++ b/web/packages/selfhosted/test/integration_tests/mouse_wheel_avm2/test.ts @@ -0,0 +1,86 @@ +import { + getTraceOutput, + hideHardwareAccelerationModal, + injectRuffleAndWait, + openTest, + playAndMonitor, +} from "../../utils.js"; +import { expect, use } from "chai"; +import chaiHtml from "chai-html"; + +use(chaiHtml); + +async function scroll( + browser: WebdriverIO.Browser, + player: ChainablePromiseElement, + x: number, + y: number, + lines: number, +) { + const canvas = await player.shadow$("canvas"); + + return await browser.execute( + (element, x, y, lines) => { + const el = element as unknown as HTMLElement; + el.dispatchEvent( + new PointerEvent("pointermove", { + clientX: x, + clientY: y, + }), + ); + return el.dispatchEvent( + new WheelEvent("wheel", { + deltaY: lines, + deltaMode: WheelEvent.DOM_DELTA_LINE, + cancelable: true, + }), + ); + }, + canvas, + x, + y, + lines, + ); +} + +describe("Mouse Wheel AVM2", () => { + it("load the test", async () => { + await openTest(browser, "integration_tests/mouse_wheel_avm2"); + await injectRuffleAndWait(browser); + const player = await browser.$(""); + await playAndMonitor(browser, player, "Loaded!\n"); + await hideHardwareAccelerationModal(browser, player); + }); + + it("scroll the first clip", async () => { + const player = await browser.$("#objectElement"); + + expect(await scroll(browser, player, 100, 100, 1)).to.equal(false); + + expect(await getTraceOutput(browser, player)).to.equal( + "Wheel consumed 1, vscroll: 1\n", + ); + }); + + it("scroll the text field", async () => { + const player = await browser.$("#objectElement"); + + expect(await scroll(browser, player, 300, 100, 1)).to.equal(false); + }); + + it("scroll the second clip", async () => { + const player = await browser.$("#objectElement"); + + expect(await scroll(browser, player, 500, 100, 1)).to.equal(false); + + expect(await getTraceOutput(browser, player)).to.equal( + "Wheel consumed 2, vscroll: 2\n", + ); + }); + + it("scroll non-interactive content", async () => { + const player = await browser.$("#objectElement"); + + expect(await scroll(browser, player, 700, 100, 1)).to.equal(true); + }); +}); diff --git a/web/packages/selfhosted/test/utils.ts b/web/packages/selfhosted/test/utils.ts index fbc57c0e38e2..4c8462487a0f 100644 --- a/web/packages/selfhosted/test/utils.ts +++ b/web/packages/selfhosted/test/utils.ts @@ -208,3 +208,28 @@ export function loadJsAPI(swf?: string) { } }); } + +export async function closeAllModals( + browser: WebdriverIO.Browser, + player: ChainablePromiseElement, +) { + await browser.execute( + (modals) => { + for (const modal of modals) { + const m = modal as unknown as HTMLElement; + const cl = m.querySelector(".close-modal")! as HTMLElement; + cl.click(); + } + }, + await player.$$(".modal:not(.hidden)"), + ); +} + +export async function hideHardwareAccelerationModal( + browser: WebdriverIO.Browser, + player: ChainablePromiseElement, +) { + // Trigger it if not triggered yet + await player.moveTo(); + await closeAllModals(browser, player); +} diff --git a/web/src/lib.rs b/web/src/lib.rs index fef263296178..7f20164e2797 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -680,7 +680,9 @@ impl RuffleHandle { _ => return, }; let _ = instance.with_core_mut(|core| { - core.handle_event(PlayerEvent::MouseWheel { delta }); + if core.handle_event(PlayerEvent::MouseWheel { delta }) { + js_event.prevent_default(); + } if core.should_prevent_scrolling() { js_event.prevent_default(); }