Bug 1885936 - Part 1: Introduce a chrome only function for getting all gamepads. r=cmartin,webidl,emilio

To collect the gamepad information on the user device, we introduce a
chrome only function called RequestAllGamepads to Navigator. The
function will return a promise which resolves when we get all gamepads
info from the parent process.

Note that we cannot rely on The existing navigator.getGamepads()
function because it cannot allow us collecting gamepads info
without the user interacts with the gamepads.

Differential Revision: https://phabricator.services.mozilla.com/D207979
This commit is contained in:
Tim Huang 2024-05-15 16:48:58 +00:00
parent c887ea40f9
commit 226c880153
11 changed files with 101 additions and 1 deletions

View file

@ -1643,6 +1643,20 @@ GamepadServiceTest* Navigator::RequestGamepadServiceTest(ErrorResult& aRv) {
return mGamepadServiceTest;
}
already_AddRefed<Promise> Navigator::RequestAllGamepads(ErrorResult& aRv) {
if (!mWindow || !mWindow->IsFullyActive()) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
// We need to set the flag to trigger the parent process to start monitoring
// gamepads. Otherwise, we cannot get any gamepad information.
win->SetHasGamepadEventListener(true);
return win->RequestAllGamepads(aRv);
}
already_AddRefed<Promise> Navigator::GetVRDisplays(ErrorResult& aRv) {
if (!mWindow || !mWindow->GetDocShell() || !mWindow->GetExtantDoc()) {
aRv.Throw(NS_ERROR_UNEXPECTED);

View file

@ -177,6 +177,7 @@ class Navigator final : public nsISupports, public nsWrapperCache {
void GetGamepads(nsTArray<RefPtr<Gamepad>>& aGamepads, ErrorResult& aRv);
GamepadServiceTest* RequestGamepadServiceTest(ErrorResult& aRv);
already_AddRefed<Promise> RequestAllGamepads(ErrorResult& aRv);
already_AddRefed<Promise> GetVRDisplays(ErrorResult& aRv);
void FinishGetVRDisplays(bool isWebVRSupportedInwindow, Promise* p);
void GetActiveVRDisplays(nsTArray<RefPtr<VRDisplay>>& aDisplays) const;

View file

@ -6785,6 +6785,18 @@ void nsGlobalWindowInner::GetGamepads(nsTArray<RefPtr<Gamepad>>& aGamepads) {
}
}
already_AddRefed<mozilla::dom::Promise> nsGlobalWindowInner::RequestAllGamepads(
ErrorResult& aRv) {
RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
if (!gamepadManager) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
return gamepadManager->RequestAllGamepads(this, aRv);
}
already_AddRefed<Gamepad> nsGlobalWindowInner::GetGamepad(
GamepadHandle aHandle) {
RefPtr<Gamepad> gamepad;

View file

@ -495,6 +495,8 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
mozilla::dom::Gamepad* aGamepad);
void RemoveGamepad(mozilla::dom::GamepadHandle aHandle);
void GetGamepads(nsTArray<RefPtr<mozilla::dom::Gamepad>>& aGamepads);
already_AddRefed<mozilla::dom::Promise> RequestAllGamepads(
mozilla::ErrorResult& aRv);
already_AddRefed<mozilla::dom::Gamepad> GetGamepad(
mozilla::dom::GamepadHandle aHandle);
void SetHasSeenGamepadInput(bool aHasSeen);

View file

@ -659,4 +659,38 @@ already_AddRefed<Promise> GamepadManager::SetLightIndicatorColor(
++mPromiseID;
return promise.forget();
}
already_AddRefed<Promise> GamepadManager::RequestAllGamepads(
nsIGlobalObject* aGlobal, ErrorResult& aRv) {
RefPtr<Promise> promise = Promise::Create(aGlobal, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
if (!mChannelChild) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
mChannelChild->SendRequestAllGamepads(
[promise](const nsTArray<GamepadAdded>& aAddedGamepads) {
nsTArray<RefPtr<Gamepad>> gamepads;
for (const auto& addedGamepad : aAddedGamepads) {
RefPtr<Gamepad> gamepad = new Gamepad(
nullptr, addedGamepad.id(), 0, GamepadHandle(),
addedGamepad.mapping(), addedGamepad.hand(),
addedGamepad.display_id(), addedGamepad.num_buttons(),
addedGamepad.num_axes(), addedGamepad.num_haptics(),
addedGamepad.num_lights(), addedGamepad.num_touches());
gamepads.AppendElement(gamepad);
}
promise->MaybeResolve(gamepads);
},
[promise](mozilla::ipc::ResponseRejectReason) {
promise->MaybeReject(NS_ERROR_UNEXPECTED);
});
return promise.forget();
}
} // namespace mozilla::dom

View file

@ -83,6 +83,10 @@ class GamepadManager final : public nsIObserver {
nsIGlobalObject* aGlobal,
ErrorResult& aRv);
// Request information of all gamepads from the parent process.
already_AddRefed<Promise> RequestAllGamepads(nsIGlobalObject* aGlobal,
ErrorResult& aRv);
protected:
GamepadManager();
~GamepadManager() = default;

View file

@ -8,6 +8,7 @@
#define mozilla_dom_GamepadPlatformService_h_
#include "mozilla/dom/GamepadBinding.h"
#include "mozilla/dom/GamepadEventTypes.h"
#include "mozilla/dom/GamepadHandle.h"
#include <map>
@ -18,7 +19,6 @@
namespace mozilla::dom {
class GamepadAdded;
class GamepadEventChannelParent;
enum class GamepadLightIndicatorType : uint8_t;
struct GamepadPoseState;
@ -122,6 +122,15 @@ class GamepadPlatformService final {
void MaybeShutdown();
nsTArray<GamepadAdded> GetAllGamePads() {
nsTArray<GamepadAdded> gamepads;
for (const auto& elem : mGamepadAdded) {
gamepads.AppendElement(elem.second);
}
return gamepads;
}
private:
GamepadPlatformService();
~GamepadPlatformService();

View file

@ -100,6 +100,16 @@ mozilla::ipc::IPCResult GamepadEventChannelParent::RecvLightIndicatorColor(
return IPC_FAIL(this, "SendReplyGamepadPromise fail.");
}
mozilla::ipc::IPCResult GamepadEventChannelParent::RecvRequestAllGamepads(
RequestAllGamepadsResolver&& aResolver) {
RefPtr<GamepadPlatformService> service =
GamepadPlatformService::GetParentService();
MOZ_ASSERT(service);
aResolver(service->GetAllGamePads());
return IPC_OK();
}
void GamepadEventChannelParent::DispatchUpdateEvent(
const GamepadChangeEvent& aEvent) {
mBackgroundEventTarget->Dispatch(new SendGamepadUpdateRunnable(this, aEvent),

View file

@ -29,6 +29,9 @@ class GamepadEventChannelParent final : public PGamepadEventChannelParent {
const uint8_t& aGreen, const uint8_t& aBlue, const uint32_t& aPromiseID);
void DispatchUpdateEvent(const GamepadChangeEvent& aEvent);
mozilla::ipc::IPCResult RecvRequestAllGamepads(
RequestAllGamepadsResolver&& aResolver);
GamepadEventChannelParent(const GamepadEventChannelParent&) = delete;
GamepadEventChannelParent(GamepadEventChannelParent&&) = delete;
GamepadEventChannelParent& operator=(const GamepadEventChannelParent&) =

View file

@ -21,6 +21,8 @@ protocol PGamepadEventChannel {
[Tainted] async LightIndicatorColor(GamepadHandle aHandle, uint32_t aLightColorIndex,
[NoTaint=allvalid] uint8_t aRed, [NoTaint=allvalid] uint8_t aGreen,
[NoTaint=allvalid] uint8_t aBlue, [NoTaint=passback] uint32_t aPromiseID);
async RequestAllGamepads()
returns (GamepadAdded[] gamepads);
child:
async GamepadUpdate(GamepadChangeEvent aGamepadEvent);

View file

@ -219,6 +219,15 @@ partial interface Navigator {
GamepadServiceTest requestGamepadServiceTest();
};
// Chrome-only interface for acquiring all gamepads. Normally, a gamepad can
// only become visible if it gets interacted by the user. This function bypasses
// this restriction; it allow requesting all gamepad info without user
// interacting with the gamepads.
partial interface Navigator {
[Throws, ChromeOnly]
Promise<sequence<Gamepad>> requestAllGamepads();
};
// https://immersive-web.github.io/webvr/spec/1.1/#interface-navigator
partial interface Navigator {
[NewObject, SecureContext, Pref="dom.vr.enabled"]