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();
}