From d812d2f1a71b1c8e0a02d2635a35e8af629b6d34 Mon Sep 17 00:00:00 2001 From: rovertrack <160643895+rovertrack@users.noreply.github.com> Date: Fri, 24 Apr 2026 13:45:53 +0530 Subject: [PATCH] Gamepad: Implement gamepad buttons to be array, removing GamepadList (#44357) Replaced `GamepadButtonList` interface with a frozen array of gamepad buttons to match the gamepad webidl specification. Testing: No idlharness tests possible since the values can't be accessed without a real gamepad. Fixes: #44333 --------- Signed-off-by: Rover track --- components/script/dom/gamepad/gamepad.rs | 65 +++++++++++--- .../script/dom/gamepad/gamepadbuttonlist.rs | 87 ------------------- components/script/dom/gamepad/mod.rs | 1 - .../script_bindings/webidls/Gamepad.webidl | 2 +- .../webidls/GamepadButtonList.webidl | 12 --- tests/wpt/mozilla/meta/MANIFEST.json | 2 +- .../tests/mozilla/interfaces.https.html | 1 - 7 files changed, 56 insertions(+), 114 deletions(-) delete mode 100644 components/script/dom/gamepad/gamepadbuttonlist.rs delete mode 100644 components/script_bindings/webidls/GamepadButtonList.webidl diff --git a/components/script/dom/gamepad/gamepad.rs b/components/script/dom/gamepad/gamepad.rs index 346e143cc77..a72d5f1c7a6 100644 --- a/components/script/dom/gamepad/gamepad.rs +++ b/components/script/dom/gamepad/gamepad.rs @@ -6,19 +6,20 @@ use std::cell::Cell; use dom_struct::dom_struct; use embedder_traits::{GamepadSupportedHapticEffects, GamepadUpdateType}; +use js::rust::MutableHandleValue; use js::typedarray::{Float64, HeapFloat64Array}; use script_bindings::trace::RootedTraceableBox; -use super::gamepadbuttonlist::GamepadButtonList; +use super::gamepadbutton::GamepadButton; use super::gamepadhapticactuator::GamepadHapticActuator; use super::gamepadpose::GamepadPose; use crate::dom::bindings::buffer_source::HeapBufferSource; use crate::dom::bindings::codegen::Bindings::GamepadBinding::{GamepadHand, GamepadMethods}; -use crate::dom::bindings::codegen::Bindings::GamepadButtonListBinding::GamepadButtonListMethods; +use crate::dom::bindings::frozenarray::CachedFrozenArray; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::num::Finite; use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; -use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::bindings::root::{Dom, DomRoot, DomSlice}; use crate::dom::bindings::str::DOMString; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; @@ -45,7 +46,9 @@ pub(crate) struct Gamepad { mapping_type: String, #[ignore_malloc_size_of = "mozjs"] axes: HeapBufferSource, - buttons: Dom, + #[ignore_malloc_size_of = "mozjs"] + frozen_buttons: CachedFrozenArray, + buttons: Vec>, pose: Option>, #[ignore_malloc_size_of = "Defined in rust-webvr"] hand: GamepadHand, @@ -64,7 +67,7 @@ impl Gamepad { connected: bool, timestamp: f64, mapping_type: String, - buttons: &GamepadButtonList, + buttons: &[&GamepadButton], pose: Option<&GamepadPose>, hand: GamepadHand, axis_bounds: (f64, f64), @@ -80,7 +83,11 @@ impl Gamepad { timestamp: Cell::new(timestamp), mapping_type, axes: HeapBufferSource::default(), - buttons: Dom::from_ref(buttons), + frozen_buttons: CachedFrozenArray::new(), + buttons: buttons + .iter() + .map(|button| Dom::from_ref(*button)) + .collect(), pose: pose.map(Dom::from_ref), hand, axis_bounds, @@ -107,7 +114,8 @@ impl Gamepad { xr: bool, can_gc: CanGc, ) -> DomRoot { - let button_list = GamepadButtonList::init_buttons(window, can_gc); + let buttons = Gamepad::init_buttons(window, can_gc); + rooted_vec!(let buttons <- buttons.iter().map(DomRoot::as_traced)); let vibration_actuator = GamepadHapticActuator::new(window, gamepad_id, supported_haptic_effects, can_gc); let index = if xr { -1 } else { 0 }; @@ -119,7 +127,7 @@ impl Gamepad { true, 0., mapping_type, - &button_list, + buttons.r(), None, GamepadHand::_empty, axis_bounds, @@ -168,8 +176,18 @@ impl GamepadMethods for Gamepad { } /// - fn Buttons(&self) -> DomRoot { - DomRoot::from_ref(&*self.buttons) + fn Buttons(&self, cx: JSContext, retval: MutableHandleValue) { + self.frozen_buttons.get_or_init( + || { + self.buttons + .iter() + .map(|b| DomRoot::from_ref(&**b)) + .collect() + }, + cx, + retval, + CanGc::deprecated_note(), + ); } /// @@ -194,6 +212,30 @@ impl Gamepad { self.gamepad_id } + /// Initialize the standard buttons for a gamepad. + /// + fn init_buttons(window: &Window, can_gc: CanGc) -> Vec> { + vec![ + GamepadButton::new(window, false, false, can_gc), // Bottom button in right cluster + GamepadButton::new(window, false, false, can_gc), // Right button in right cluster + GamepadButton::new(window, false, false, can_gc), // Left button in right cluster + GamepadButton::new(window, false, false, can_gc), // Top button in right cluster + GamepadButton::new(window, false, false, can_gc), // Top left front button + GamepadButton::new(window, false, false, can_gc), // Top right front button + GamepadButton::new(window, false, false, can_gc), // Bottom left front button + GamepadButton::new(window, false, false, can_gc), // Bottom right front button + GamepadButton::new(window, false, false, can_gc), // Left button in center cluster + GamepadButton::new(window, false, false, can_gc), // Right button in center cluster + GamepadButton::new(window, false, false, can_gc), // Left stick pressed button + GamepadButton::new(window, false, false, can_gc), // Right stick pressed button + GamepadButton::new(window, false, false, can_gc), // Top button in left cluster + GamepadButton::new(window, false, false, can_gc), // Bottom button in left cluster + GamepadButton::new(window, false, false, can_gc), // Left button in left cluster + GamepadButton::new(window, false, false, can_gc), // Right button in left cluster + GamepadButton::new(window, false, false, can_gc), // Center button in center cluster + ] + } + pub(crate) fn update_connected(&self, connected: bool, has_gesture: bool, can_gc: CanGc) { if self.connected.get() == connected { return; @@ -279,8 +321,9 @@ impl Gamepad { if normalized_value.is_finite() { let pressed = normalized_value >= BUTTON_PRESS_THRESHOLD; // TODO: Determine a way of getting touch capability for button - if let Some(button) = self.buttons.IndexedGetter(button_index as u32) { + if let Some(button) = self.buttons.get(button_index) { button.update(pressed, /*touched*/ pressed, normalized_value); + self.frozen_buttons.clear(); } } else { warn!("Button value is not finite!"); diff --git a/components/script/dom/gamepad/gamepadbuttonlist.rs b/components/script/dom/gamepad/gamepadbuttonlist.rs deleted file mode 100644 index 708b2c99282..00000000000 --- a/components/script/dom/gamepad/gamepadbuttonlist.rs +++ /dev/null @@ -1,87 +0,0 @@ -/* 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 https://mozilla.org/MPL/2.0/. */ - -use dom_struct::dom_struct; - -use crate::dom::bindings::codegen::Bindings::GamepadButtonListBinding::GamepadButtonListMethods; -use crate::dom::bindings::reflector::{Reflector, reflect_dom_object}; -use crate::dom::bindings::root::{Dom, DomRoot, DomSlice}; -use crate::dom::gamepad::gamepadbutton::GamepadButton; -use crate::dom::window::Window; -use crate::script_runtime::CanGc; - -// https://w3c.github.io/gamepad/#gamepadbutton-interface -#[dom_struct] -pub(crate) struct GamepadButtonList { - reflector_: Reflector, - list: Vec>, -} - -impl GamepadButtonList { - fn new_inherited(list: &[&GamepadButton]) -> GamepadButtonList { - GamepadButtonList { - reflector_: Reflector::new(), - list: list.iter().map(|button| Dom::from_ref(*button)).collect(), - } - } - - pub(crate) fn new( - window: &Window, - list: &[&GamepadButton], - can_gc: CanGc, - ) -> DomRoot { - reflect_dom_object( - Box::new(GamepadButtonList::new_inherited(list)), - window, - can_gc, - ) - } -} - -impl GamepadButtonListMethods for GamepadButtonList { - /// - fn Length(&self) -> u32 { - self.list.len() as u32 - } - - /// - fn Item(&self, index: u32) -> Option> { - self.list - .get(index as usize) - .map(|button| DomRoot::from_ref(&**button)) - } - - /// - fn IndexedGetter(&self, index: u32) -> Option> { - self.Item(index) - } -} - -impl GamepadButtonList { - /// Initialize the number of buttons in the "standard" gamepad mapping. - /// - pub(crate) fn init_buttons(window: &Window, can_gc: CanGc) -> DomRoot { - let standard_buttons = &[ - GamepadButton::new(window, false, false, can_gc), // Bottom button in right cluster - GamepadButton::new(window, false, false, can_gc), // Right button in right cluster - GamepadButton::new(window, false, false, can_gc), // Left button in right cluster - GamepadButton::new(window, false, false, can_gc), // Top button in right cluster - GamepadButton::new(window, false, false, can_gc), // Top left front button - GamepadButton::new(window, false, false, can_gc), // Top right front button - GamepadButton::new(window, false, false, can_gc), // Bottom left front button - GamepadButton::new(window, false, false, can_gc), // Bottom right front button - GamepadButton::new(window, false, false, can_gc), // Left button in center cluster - GamepadButton::new(window, false, false, can_gc), // Right button in center cluster - GamepadButton::new(window, false, false, can_gc), // Left stick pressed button - GamepadButton::new(window, false, false, can_gc), // Right stick pressed button - GamepadButton::new(window, false, false, can_gc), // Top button in left cluster - GamepadButton::new(window, false, false, can_gc), // Bottom button in left cluster - GamepadButton::new(window, false, false, can_gc), // Left button in left cluster - GamepadButton::new(window, false, false, can_gc), // Right button in left cluster - GamepadButton::new(window, false, false, can_gc), // Center button in center cluster - ]; - rooted_vec!(let buttons <- standard_buttons.iter().map(DomRoot::as_traced)); - Self::new(window, buttons.r(), can_gc) - } -} diff --git a/components/script/dom/gamepad/mod.rs b/components/script/dom/gamepad/mod.rs index 17b026be1d3..39c904532f3 100644 --- a/components/script/dom/gamepad/mod.rs +++ b/components/script/dom/gamepad/mod.rs @@ -6,7 +6,6 @@ pub(crate) mod gamepad; pub(crate) use gamepad::Gamepad; pub(crate) mod gamepadbutton; -pub(crate) mod gamepadbuttonlist; pub(crate) mod gamepadevent; pub(crate) mod gamepadhapticactuator; pub(crate) mod gamepadpose; diff --git a/components/script_bindings/webidls/Gamepad.webidl b/components/script_bindings/webidls/Gamepad.webidl index e040c0a2ab7..6bdd2e5ac38 100644 --- a/components/script_bindings/webidls/Gamepad.webidl +++ b/components/script_bindings/webidls/Gamepad.webidl @@ -13,7 +13,7 @@ interface Gamepad { readonly attribute DOMHighResTimeStamp timestamp; readonly attribute DOMString mapping; readonly attribute Float64Array axes; - [SameObject] readonly attribute GamepadButtonList buttons; + readonly attribute any buttons; [SameObject] readonly attribute GamepadHapticActuator vibrationActuator; }; diff --git a/components/script_bindings/webidls/GamepadButtonList.webidl b/components/script_bindings/webidls/GamepadButtonList.webidl deleted file mode 100644 index 040a9acc133..00000000000 --- a/components/script_bindings/webidls/GamepadButtonList.webidl +++ /dev/null @@ -1,12 +0,0 @@ -/* 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 https://mozilla.org/MPL/2.0/. */ - -// skip-unless CARGO_FEATURE_GAMEPAD - -// https://w3c.github.io/gamepad/#dom-gamepad-buttons -[Exposed=Window, Pref="dom_gamepad_enabled"] -interface GamepadButtonList { - getter GamepadButton? item(unsigned long index); - readonly attribute unsigned long length; -}; diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 30452976e67..4ba829d1e42 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -14340,7 +14340,7 @@ ] ], "interfaces.https.html": [ - "566deaac42e0f430fadcbd28bf4dba751c7ba39a", + "decaa2df2047787dbfc4b83d09ce49b117a7a53c", [ null, {} diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html index 566deaac42e..decaa2df204 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html @@ -112,7 +112,6 @@ test_interfaces([ "GainNode", "Gamepad", "GamepadButton", - "GamepadButtonList", "GamepadEvent", "GamepadHapticActuator", "GamepadPose",