Commit 69b90aa1 authored by Raphael Kubo da Costa's avatar Raphael Kubo da Costa Committed by Commit Bot

gamepad: Use FrozenArrays for Gamepad#axes and Gamepad#buttons.

Sync our IDL file with https://github.com/w3c/gamepad/pull/62 ("Switch from
WebIDL arrays to FrozenArray<>s in the IDLs").

WebIDL has not had array types since 2015, so finally make our IDL files
compliant with modern WebIDL following the spec fix.

It is important to note that this change modifies the existing behavior
slightly.
- |axes| and |buttons| are now frozen objects with all the related
  consequences for its properties and prototype.
- Those two attributes now return the same _object_ until their values
  change instead of always returning a new object on access.

Doing so aligns our code with both the spec as well as Gecko, which has done
the above ever since it implemented the Gamepad spec.

Bug: 740875
Change-Id: Ifb618c9d4f8860eb55efc882e701dae7390808a5
Reviewed-on: https://chromium-review.googlesource.com/595979Reviewed-by: default avatarMatt Reynolds <mattreynolds@chromium.org>
Commit-Queue: Raphael Kubo da Costa (rakuco) <raphael.kubo.da.costa@intel.com>
Cr-Commit-Position: refs/heads/master@{#491440}
parent cc5fd144
......@@ -22,6 +22,8 @@ PASS gamepad.buttons.__proto__ is Array.prototype
PASS gamepad.buttons[0].pressed.__proto__ is Boolean.prototype
PASS gamepad.buttons[0].value.__proto__ is Number.prototype
PASS gamepad.mapping.__proto__ is String.prototype
PASS Object.isFrozen(gamepad.axes) is true
PASS Object.isFrozen(gamepad.buttons) is true
PASS successfullyParsed is true
TEST COMPLETE
......
......@@ -34,6 +34,8 @@
shouldBe("gamepad.buttons[0].pressed.__proto__", "Boolean.prototype");
shouldBe("gamepad.buttons[0].value.__proto__", "Number.prototype");
shouldBe("gamepad.mapping.__proto__", "String.prototype");
shouldBeTrue("Object.isFrozen(gamepad.axes)");
shouldBeTrue("Object.isFrozen(gamepad.buttons)");
}
else
{
......
......@@ -19,6 +19,8 @@ PASS event.gamepad.buttons[2].pressed is true
PASS event.gamepad.axes[0] is 0.5
PASS event.gamepad.axes[1] is -1.0
PASS event.gamepad.axes[2] is 0.333333
PASS axes[0] is 0.5
PASS buttons.hasOwnProperty('foo') is false
Gamepad disconnected
PASS event.__proto__ is GamepadEvent.prototype
PASS event.__proto__.__proto__ is Event.prototype
......
......@@ -25,6 +25,14 @@
shouldBe("event.gamepad.axes[0]", "0.5");
shouldBe("event.gamepad.axes[1]", "-1.0");
shouldBe("event.gamepad.axes[2]", "0.333333");
// |axes| and |buttons| are FrozenArrays, changing them has no
// effect.
axes = event.gamepad.axes;
axes[0] = 42;
shouldBe("axes[0]", "0.5");
buttons = event.gamepad.buttons;
buttons.foo = "bar";
shouldBeFalse("buttons.hasOwnProperty('foo')");
gamepadController.disconnect(0);
}
......
......@@ -20,6 +20,18 @@ PASS navigator.getGamepads()[0].buttons[1].pressed is false
PASS navigator.getGamepads()[0].axes.length is 2
PASS navigator.getGamepads()[0].axes[0] is 0.5
PASS navigator.getGamepads()[0].axes[1] is -1.0
PASS navigator.getGamepads()[0].axes === navigator.getGamepads()[0].axes is true
PASS navigator.getGamepads()[0].buttons === navigator.getGamepads()[0].buttons is true
PASS navigator.getGamepads()[0].axes === oldAxes is true
PASS navigator.getGamepads()[0].buttons === oldButtons is true
PASS navigator.getGamepads()[0].axes === oldAxes is true
PASS navigator.getGamepads()[0].buttons === oldButtons is true
PASS navigator.getGamepads()[0].axes === oldAxes is false
PASS navigator.getGamepads()[0].buttons === oldButtons is false
PASS navigator.getGamepads()[0].axes === oldAxes is true
PASS navigator.getGamepads()[0].buttons === oldButtons is true
PASS navigator.getGamepads()[0].axes === oldAxes is false
PASS navigator.getGamepads()[0].buttons === oldButtons is false
PASS successfullyParsed is true
TEST COMPLETE
......
......@@ -45,6 +45,35 @@
shouldBe("navigator.getGamepads()[0].axes.length", "2");
shouldBe("navigator.getGamepads()[0].axes[0]", "0.5");
shouldBe("navigator.getGamepads()[0].axes[1]", "-1.0");
// check that accessing the |axes| and |buttons| attributes fetches the
// same objects until their values change.
shouldBeTrue("navigator.getGamepads()[0].axes === navigator.getGamepads()[0].axes");
shouldBeTrue("navigator.getGamepads()[0].buttons === navigator.getGamepads()[0].buttons");
oldAxes = navigator.getGamepads()[0].axes;
oldButtons = navigator.getGamepads()[0].buttons;
// updates with the same values are skipped.
gamepadController.setAxisCount(0, 2);
gamepadController.setButtonCount(0, 2);
shouldBeTrue("navigator.getGamepads()[0].axes === oldAxes");
shouldBeTrue("navigator.getGamepads()[0].buttons === oldButtons");
gamepadController.setAxisData(0, 0, .5);
gamepadController.setButtonData(0, 1, 0);
shouldBeTrue("navigator.getGamepads()[0].axes === oldAxes");
shouldBeTrue("navigator.getGamepads()[0].buttons === oldButtons");
// updates with different values are not skipped.
gamepadController.setAxisCount(0, 1);
gamepadController.setButtonCount(0, 1);
shouldBeFalse("navigator.getGamepads()[0].axes === oldAxes");
shouldBeFalse("navigator.getGamepads()[0].buttons === oldButtons");
oldAxes = navigator.getGamepads()[0].axes;
oldButtons = navigator.getGamepads()[0].buttons;
shouldBeTrue("navigator.getGamepads()[0].axes === oldAxes");
shouldBeTrue("navigator.getGamepads()[0].buttons === oldButtons");
gamepadController.setAxisData(0, 0, .9);
gamepadController.setButtonData(0, 0, .3);
shouldBeFalse("navigator.getGamepads()[0].axes === oldAxes");
shouldBeFalse("navigator.getGamepads()[0].buttons === oldButtons");
}
else
{
......
......@@ -25,30 +25,60 @@
#include "modules/gamepad/Gamepad.h"
#include <algorithm>
namespace blink {
Gamepad::Gamepad() : index_(0), timestamp_(0), display_id_(0) {}
Gamepad::Gamepad()
: index_(0),
timestamp_(0),
display_id_(0),
is_axis_data_dirty_(true),
is_button_data_dirty_(true) {}
Gamepad::~Gamepad() {}
const Gamepad::DoubleVector& Gamepad::axes() {
is_axis_data_dirty_ = false;
return axes_;
}
void Gamepad::SetAxes(unsigned count, const double* data) {
bool skip_update =
axes_.size() == count && std::equal(data, data + count, axes_.begin());
if (skip_update)
return;
axes_.resize(count);
if (count)
std::copy(data, data + count, axes_.begin());
is_axis_data_dirty_ = true;
}
const GamepadButtonVector& Gamepad::buttons() {
is_button_data_dirty_ = false;
return buttons_;
}
void Gamepad::SetButtons(unsigned count, const device::GamepadButton* data) {
bool skip_update =
buttons_.size() == count &&
std::equal(data, data + count, buttons_.begin(),
[](const device::GamepadButton& device_gamepad_button,
const Member<GamepadButton>& gamepad_button) {
return gamepad_button->IsEqual(device_gamepad_button);
});
if (skip_update)
return;
if (buttons_.size() != count) {
buttons_.resize(count);
for (unsigned i = 0; i < count; ++i)
buttons_[i] = GamepadButton::Create();
}
for (unsigned i = 0; i < count; ++i) {
buttons_[i]->SetValue(data[i].value);
buttons_[i]->SetPressed(data[i].pressed);
buttons_[i]->SetTouched(data[i].touched || data[i].pressed ||
(data[i].value > 0.0f));
}
for (unsigned i = 0; i < count; ++i)
buttons_[i]->UpdateValuesFrom(data[i]);
is_button_data_dirty_ = true;
}
void Gamepad::SetPose(const device::GamepadPose& pose) {
......
......@@ -61,11 +61,13 @@ class Gamepad final : public GarbageCollectedFinalized<Gamepad>,
const String& mapping() const { return mapping_; }
void SetMapping(const String& val) { mapping_ = val; }
const DoubleVector& axes() const { return axes_; }
const DoubleVector& axes();
void SetAxes(unsigned count, const double* data);
bool isAxisDataDirty() const { return is_axis_data_dirty_; }
const GamepadButtonVector& buttons() const { return buttons_; }
const GamepadButtonVector& buttons();
void SetButtons(unsigned count, const device::GamepadButton* data);
bool isButtonDataDirty() const { return is_button_data_dirty_; }
GamepadPose* pose() const { return pose_; }
void SetPose(const device::GamepadPose&);
......@@ -91,6 +93,8 @@ class Gamepad final : public GarbageCollectedFinalized<Gamepad>,
Member<GamepadPose> pose_;
String hand_;
unsigned display_id_;
bool is_axis_data_dirty_;
bool is_button_data_dirty_;
};
} // namespace blink
......
......@@ -36,9 +36,8 @@ interface Gamepad {
readonly attribute boolean connected;
readonly attribute unsigned long long timestamp;
readonly attribute DOMString mapping;
// https://github.com/w3c/gamepad/issues/28
[MeasureAs=GamepadAxes] readonly attribute double[] axes;
[MeasureAs=GamepadButtons] readonly attribute GamepadButton[] buttons;
[CachedAttribute=isAxisDataDirty, MeasureAs=GamepadAxes] readonly attribute FrozenArray<double> axes;
[CachedAttribute=isButtonDataDirty, MeasureAs=GamepadButtons] readonly attribute FrozenArray<GamepadButton> buttons;
[OriginTrialEnabled=GamepadExtensions, MeasureAs=GamepadPose] readonly attribute GamepadPose? pose;
[OriginTrialEnabled=GamepadExtensions, MeasureAs=GamepadHand] readonly attribute GamepadHand hand;
......
......@@ -12,4 +12,18 @@ GamepadButton* GamepadButton::Create() {
GamepadButton::GamepadButton() : value_(0.), pressed_(false), touched_(false) {}
bool GamepadButton::IsEqual(const device::GamepadButton& device_button) const {
return value_ == device_button.value && pressed_ == device_button.pressed &&
touched_ == (device_button.touched || device_button.pressed ||
(device_button.value > 0.0f));
}
void GamepadButton::UpdateValuesFrom(
const device::GamepadButton& device_button) {
value_ = device_button.value;
pressed_ = device_button.pressed;
touched_ = (device_button.touched || device_button.pressed ||
(device_button.value > 0.0f));
}
} // namespace blink
......@@ -5,6 +5,7 @@
#ifndef GamepadButton_h
#define GamepadButton_h
#include "device/gamepad/public/cpp/gamepad.h"
#include "platform/bindings/ScriptWrappable.h"
#include "platform/heap/Handle.h"
#include "platform/wtf/Vector.h"
......@@ -27,6 +28,9 @@ class GamepadButton final : public GarbageCollected<GamepadButton>,
bool touched() const { return touched_; }
void SetTouched(bool val) { touched_ = val; }
bool IsEqual(const device::GamepadButton&) const;
void UpdateValuesFrom(const device::GamepadButton&);
DEFINE_INLINE_TRACE() {}
private:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment