Bug 1299937 - Part 1: Implement GamepadHapticActuator in Gamepad API; r?qdot draft
authorDaosheng Mu <daoshengmu@gmail.com>
Thu, 02 Feb 2017 14:30:58 +0800
changeset 503536 27d15c244de4b38bd40ce6667d797076a5a01355
parent 501295 1b9293be51637f841275541d8991314ca56561a5
child 503537 cd2c3cf2d3ca08f439a8bf7413dfd834bfbb3b5f
push id50611
push userbmo:dmu@mozilla.com
push dateThu, 23 Mar 2017 09:11:47 +0000
reviewersqdot
bugs1299937
milestone55.0a1
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
@@ -534,16 +534,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',