Bug 1299937 - Part 1: Implement GamepadHapticActuator in Gamepad API; r=qdot
authorDaosheng Mu <daoshengmu@gmail.com>
Thu, 02 Feb 2017 14:30:58 +0800
changeset 349315 56e2fa280d2a9f00e37238c3c2532caa01c3b2a5
parent 349314 6b9bbd396faca96f2e4549dab53b82557fe22403
child 349316 691a5be9ff0d942abf71dfe73714c51fba04f55d
push id39455
push userdmu@mozilla.com
push dateFri, 24 Mar 2017 03:25:06 +0000
treeherderautoland@5d8e162ef5f7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersqdot
bugs1299937
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1299937 - Part 1: Implement GamepadHapticActuator in Gamepad API; r=qdot MozReview-Commit-ID: GJZvgxSBVlB
dom/gamepad/Gamepad.cpp
dom/gamepad/Gamepad.h
dom/gamepad/GamepadHapticActuator.cpp
dom/gamepad/GamepadHapticActuator.h
dom/gamepad/moz.build
dom/tests/mochitest/general/test_interfaces.js
dom/webidl/Gamepad.webidl
dom/webidl/GamepadHapticActuator.webidl
dom/webidl/GamepadPose.webidl
dom/webidl/moz.build
--- a/dom/gamepad/Gamepad.cpp
+++ b/dom/gamepad/Gamepad.cpp
@@ -16,50 +16,57 @@ namespace dom {
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Gamepad)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Gamepad)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Gamepad)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Gamepad, mParent, mButtons, mPose)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Gamepad, mParent, mButtons, mPose,
+                                      mHapticActuators)
 
 void
 Gamepad::UpdateTimestamp()
 {
   nsCOMPtr<nsPIDOMWindowInner> newWindow(do_QueryInterface(mParent));
   if(newWindow) {
     Performance* perf = newWindow->GetPerformance();
     if (perf) {
-      mTimestamp =  perf->Now();
+      mTimestamp = perf->Now();
     }
   }
 }
 
 Gamepad::Gamepad(nsISupports* aParent,
                  const nsAString& aID, uint32_t aIndex,
+                 uint32_t aHashKey,
                  GamepadMappingType aMapping,
                  GamepadHand aHand,
-                 uint32_t aNumButtons, uint32_t aNumAxes)
+                 uint32_t aNumButtons, uint32_t aNumAxes,
+                 uint32_t aNumHaptics)
   : mParent(aParent),
     mID(aID),
     mIndex(aIndex),
+    mHashKey(aHashKey),
     mMapping(aMapping),
     mHand(aHand),
     mConnected(true),
     mButtons(aNumButtons),
     mAxes(aNumAxes),
     mTimestamp(0)
 {
   for (unsigned i = 0; i < aNumButtons; i++) {
     mButtons.InsertElementAt(i, new GamepadButton(mParent));
   }
   mAxes.InsertElementsAt(0, aNumAxes, 0.0f);
   mPose = new GamepadPose(aParent);
+  for (uint32_t i = 0; i < aNumHaptics; ++i) {
+    mHapticActuators.AppendElement(new GamepadHapticActuator(mParent, mHashKey, i));
+  }
   UpdateTimestamp();
 }
 
 void
 Gamepad::SetIndex(uint32_t aIndex)
 {
   mIndex = aIndex;
 }
@@ -120,27 +127,31 @@ Gamepad::SyncState(Gamepad* aOther)
   if (changed) {
     GamepadBinding::ClearCachedAxesValue(this);
   }
 
   if (Preferences::GetBool(kGamepadExtEnabledPref)) {
     MOZ_ASSERT(aOther->GetPose());
     mPose->SetPoseState(aOther->GetPose()->GetPoseState());
     mHand = aOther->Hand();
+    for (uint32_t i = 0; i < mHapticActuators.Length(); ++i) {
+      mHapticActuators[i]->Set(aOther->mHapticActuators[i]);
+    }
   }
 
   UpdateTimestamp();
 }
 
 already_AddRefed<Gamepad>
 Gamepad::Clone(nsISupports* aParent)
 {
   RefPtr<Gamepad> out =
-    new Gamepad(aParent, mID, mIndex, mMapping,
-                mHand, mButtons.Length(), mAxes.Length());
+    new Gamepad(aParent, mID, mIndex, mHashKey, mMapping,
+                mHand, mButtons.Length(), mAxes.Length(),
+                mHapticActuators.Length());
   out->SyncState(this);
   return out.forget();
 }
 
 /* virtual */ JSObject*
 Gamepad::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return GamepadBinding::Wrap(aCx, this, aGivenProto);
--- a/dom/gamepad/Gamepad.h
+++ b/dom/gamepad/Gamepad.h
@@ -6,16 +6,17 @@
 
 #ifndef mozilla_dom_gamepad_Gamepad_h
 #define mozilla_dom_gamepad_Gamepad_h
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/GamepadBinding.h"
 #include "mozilla/dom/GamepadButton.h"
 #include "mozilla/dom/GamepadPose.h"
+#include "mozilla/dom/GamepadHapticActuator.h"
 #include "mozilla/dom/Performance.h"
 #include <stdint.h>
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
@@ -36,18 +37,21 @@ const int kRightStickYAxis = 3;
 
 
 class Gamepad final : public nsISupports,
                       public nsWrapperCache
 {
 public:
   Gamepad(nsISupports* aParent,
           const nsAString& aID, uint32_t aIndex,
+          uint32_t aHashKey,
           GamepadMappingType aMapping, GamepadHand aHand,
-          uint32_t aNumButtons, uint32_t aNumAxes);
+          uint32_t aNumButtons, uint32_t aNumAxes,
+          uint32_t aNumHaptics);
+
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Gamepad)
 
   void SetConnected(bool aConnected);
   void SetButton(uint32_t aButton, bool aPressed, double aValue);
   void SetAxis(uint32_t aAxis, double aValue);
   void SetIndex(uint32_t aIndex);
   void SetPose(const GamepadPoseState& aPose);
@@ -91,50 +95,63 @@ public:
     return mConnected;
   }
 
   uint32_t Index() const
   {
     return mIndex;
   }
 
+  uint32_t HashKey() const
+  {
+    return mHashKey;
+  }
+
   void GetButtons(nsTArray<RefPtr<GamepadButton>>& aButtons) const
   {
     aButtons = mButtons;
   }
 
   void GetAxes(nsTArray<double>& aAxes) const
   {
     aAxes = mAxes;
   }
 
   GamepadPose* GetPose() const
   {
     return mPose;
   }
 
+  void GetHapticActuators(nsTArray<RefPtr<GamepadHapticActuator>>& aHapticActuators) const
+  {
+    aHapticActuators = mHapticActuators;
+  }
+
 private:
   virtual ~Gamepad() {}
   void UpdateTimestamp();
 
 protected:
   nsCOMPtr<nsISupports> mParent;
   nsString mID;
   uint32_t mIndex;
+  // the gamepad hash key in GamepadManager
+  uint32_t mHashKey;
 
   // The mapping in use.
   GamepadMappingType mMapping;
   GamepadHand mHand;
 
   // true if this gamepad is currently connected.
   bool mConnected;
 
   // Current state of buttons, axes.
   nsTArray<RefPtr<GamepadButton>> mButtons;
   nsTArray<double> mAxes;
   DOMHighResTimeStamp mTimestamp;
   RefPtr<GamepadPose> mPose;
+  nsTArray<RefPtr<GamepadHapticActuator>> mHapticActuators;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_gamepad_Gamepad_h
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/GamepadHapticActuator.cpp
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "mozilla/dom/GamepadHapticActuator.h"
+#include "mozilla/dom/GamepadManager.h"
+#include "mozilla/dom/Promise.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(GamepadHapticActuator)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(GamepadHapticActuator)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GamepadHapticActuator)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GamepadHapticActuator, mParent)
+
+GamepadHapticActuator::GamepadHapticActuator(nsISupports* aParent, uint32_t aGamepadId,
+                                             uint32_t aIndex)
+  : mParent(aParent), mGamepadId(aGamepadId),
+    mType(GamepadHapticActuatorType::Vibration), mIndex(aIndex)
+{
+
+}
+
+/* virtual */ JSObject*
+GamepadHapticActuator::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return GamepadHapticActuatorBinding::Wrap(aCx, this, aGivenProto);
+}
+
+nsISupports*
+GamepadHapticActuator::GetParentObject() const
+{
+  return mParent;
+}
+
+#define CLAMP(f, min, max) \
+          (((f) < min)? min : (((f) > max) ? max : (f)))
+
+already_AddRefed<Promise>
+GamepadHapticActuator::Pulse(double aValue, double aDuration, ErrorResult& aRv)
+{
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
+  MOZ_ASSERT(global);
+
+  RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
+  MOZ_ASSERT(gamepadManager);
+
+  // Clamp intensity aValue to be 0~1.
+  double value = CLAMP(aValue, 0, 1);
+  // aDuration should be always positive.
+  double duration = CLAMP(aDuration, 0, aDuration);
+
+  switch (mType) {
+    case GamepadHapticActuatorType::Vibration:
+    {
+      RefPtr<Promise> promise =
+        gamepadManager->VibrateHaptic(
+          mGamepadId, mIndex, value, duration, global, aRv);
+      if (!promise) {
+        return nullptr;
+      }
+      return promise.forget();
+    }
+    default:
+    {
+      // We need to implement other types of haptic
+      MOZ_ASSERT(false);
+      return nullptr;
+    }
+  }
+}
+
+GamepadHapticActuatorType
+GamepadHapticActuator::Type() const
+{
+  return mType;
+}
+
+void
+GamepadHapticActuator::Set(const GamepadHapticActuator* aOther) {
+  mGamepadId = aOther->mGamepadId;
+  mType = aOther->mType;
+  mIndex = aOther->mIndex;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/GamepadHapticActuator.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_gamepad_GamepadHapticActuator_h
+#define mozilla_dom_gamepad_GamepadHapticActuator_h
+
+#include "nsCOMPtr.h"
+#include "nsWrapperCache.h"
+#include "mozilla/dom/GamepadHapticActuatorBinding.h"
+#include "mozilla/dom/Gamepad.h"
+
+namespace mozilla {
+namespace dom {
+class Promise;
+
+class GamepadHapticActuator : public nsISupports,
+                              public nsWrapperCache
+{
+public:
+  GamepadHapticActuator(nsISupports* aParent, uint32_t aGamepadId,
+                        uint32_t aIndex);
+  explicit GamepadHapticActuator(nsISupports* aParent)
+    : mParent(aParent), mType(GamepadHapticActuatorType::Vibration)
+  {
+  }
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(GamepadHapticActuator)
+
+  nsISupports* GetParentObject() const;
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  already_AddRefed<Promise> Pulse(double aValue, double aDuration, ErrorResult& aRv);
+
+  GamepadHapticActuatorType Type() const;
+
+  void Set(const GamepadHapticActuator* aOther);
+
+private:
+  virtual ~GamepadHapticActuator() {}
+
+protected:
+  nsCOMPtr<nsISupports> mParent;
+  uint32_t mGamepadId;
+  GamepadHapticActuatorType mType;
+  uint32_t mIndex;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_gamepad_GamepadHapticActuator_h
--- a/dom/gamepad/moz.build
+++ b/dom/gamepad/moz.build
@@ -11,16 +11,17 @@ IPDL_SOURCES += [
     'ipc/GamepadEventTypes.ipdlh',
     'ipc/PGamepadEventChannel.ipdl',
     'ipc/PGamepadTestChannel.ipdl'
 ]
 
 EXPORTS.mozilla.dom += [
     'Gamepad.h',
     'GamepadButton.h',
+    'GamepadHapticActuator.h',
     'GamepadManager.h',
     'GamepadMonitoring.h',
     'GamepadPlatformService.h',
     'GamepadPose.h',
     'GamepadPoseState.h',
     'GamepadServiceTest.h',
     'ipc/GamepadEventChannelChild.h',
     'ipc/GamepadEventChannelParent.h',
@@ -28,16 +29,17 @@ EXPORTS.mozilla.dom += [
     'ipc/GamepadServiceType.h',
     'ipc/GamepadTestChannelChild.h',
     'ipc/GamepadTestChannelParent.h'
 ]
 
 UNIFIED_SOURCES = [
     'Gamepad.cpp',
     'GamepadButton.cpp',
+    'GamepadHapticActuator.cpp',
     'GamepadManager.cpp',
     'GamepadMonitoring.cpp',
     'GamepadPlatformService.cpp',
     'GamepadPose.cpp',
     'GamepadServiceTest.cpp',
     'ipc/GamepadEventChannelChild.cpp',
     'ipc/GamepadEventChannelParent.cpp',
     'ipc/GamepadTestChannelChild.cpp',
--- a/dom/tests/mochitest/general/test_interfaces.js
+++ b/dom/tests/mochitest/general/test_interfaces.js
@@ -377,16 +377,18 @@ var interfaceNamesInGlobalScope =
     "GamepadAxisMoveEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "GamepadButtonEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "GamepadButton",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "GamepadEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "GamepadHapticActuator", release: false},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "GamepadPose", release: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HashChangeEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Headers",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "History",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/webidl/Gamepad.webidl
+++ b/dom/webidl/Gamepad.webidl
@@ -75,9 +75,16 @@ interface Gamepad {
    */
   readonly attribute DOMHighResTimeStamp timestamp;
 
   /**
    * The current pose of the device, a GamepadPose.
    */
   [Pref="dom.gamepad.extensions.enabled"]
   readonly attribute GamepadPose? pose;
+
+  /**
+   * The current haptic actuator of the device, an array of
+   * GamepadHapticActuator.
+   */
+  [Constant, Cached, Frozen, Pref="dom.gamepad.extensions.enabled"]
+  readonly attribute sequence<GamepadHapticActuator> hapticActuators;
 };
new file mode 100644
--- /dev/null
+++ b/dom/webidl/GamepadHapticActuator.webidl
@@ -0,0 +1,21 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this IDL file is
+ * https://w3c.github.io/gamepad/extensions.html#gamepadhapticactuator-interface
+ */
+
+enum GamepadHapticActuatorType {
+  "vibration"
+};
+
+[Pref="dom.gamepad.extensions.enabled",
+  HeaderFile="mozilla/dom/GamepadHapticActuator.h"]
+interface GamepadHapticActuator
+{
+  readonly attribute GamepadHapticActuatorType type;
+  [Throws, NewObject]
+  Promise<boolean> pulse(double value, double duration);
+};
--- a/dom/webidl/GamepadPose.webidl
+++ b/dom/webidl/GamepadPose.webidl
@@ -1,12 +1,15 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/.
+ *
+ * The origin of this IDL file is
+ * https://w3c.github.io/gamepad/extensions.html#gamepadpose-interface
  */
 
 [Pref="dom.gamepad.extensions.enabled"]
 interface GamepadPose
 {
   readonly attribute boolean hasOrientation;
   readonly attribute boolean hasPosition;
 
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -537,16 +537,17 @@ WEBIDL_FILES = [
     'FocusEvent.webidl',
     'FontFace.webidl',
     'FontFaceSet.webidl',
     'FontFaceSource.webidl',
     'FormData.webidl',
     'Function.webidl',
     'GainNode.webidl',
     'Gamepad.webidl',
+    'GamepadHapticActuator.webidl',
     'GamepadPose.webidl',
     'GamepadServiceTest.webidl',
     'Geolocation.webidl',
     'GeometryUtils.webidl',
     'GetUserMediaRequest.webidl',
     'Grid.webidl',
     'Headers.webidl',
     'HeapSnapshot.webidl',