dom/gamepad/GamepadService.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "mozilla/Hal.h"
     6 #include "mozilla/ClearOnShutdown.h"
     7 #include "mozilla/Preferences.h"
     8 #include "mozilla/StaticPtr.h"
    10 #include "GamepadService.h"
    11 #include "Gamepad.h"
    12 #include "nsAutoPtr.h"
    13 #include "nsIDOMEvent.h"
    14 #include "nsIDOMDocument.h"
    15 #include "GeneratedEvents.h"
    16 #include "nsIDOMWindow.h"
    17 #include "nsIObserver.h"
    18 #include "nsIObserverService.h"
    19 #include "nsIServiceManager.h"
    20 #include "nsITimer.h"
    21 #include "nsThreadUtils.h"
    22 #include "mozilla/Services.h"
    24 #include "mozilla/dom/GamepadAxisMoveEvent.h"
    25 #include "mozilla/dom/GamepadButtonEvent.h"
    26 #include "mozilla/dom/GamepadEvent.h"
    28 #include <cstddef>
    30 namespace mozilla {
    31 namespace dom {
    33 namespace {
    34 const char* kGamepadEnabledPref = "dom.gamepad.enabled";
    35 const char* kGamepadEventsEnabledPref =
    36   "dom.gamepad.non_standard_events.enabled";
    37 // Amount of time to wait before cleaning up gamepad resources
    38 // when no pages are listening for events.
    39 const int kCleanupDelayMS = 2000;
    40 const nsTArray<nsRefPtr<nsGlobalWindow> >::index_type NoIndex =
    41     nsTArray<nsRefPtr<nsGlobalWindow> >::NoIndex;
    43 StaticRefPtr<GamepadService> gGamepadServiceSingleton;
    45 } // namespace
    47 bool GamepadService::sShutdown = false;
    49 NS_IMPL_ISUPPORTS(GamepadService, nsIObserver)
    51 GamepadService::GamepadService()
    52   : mStarted(false),
    53     mShuttingDown(false)
    54 {
    55   mEnabled = IsAPIEnabled();
    56   mNonstandardEventsEnabled =
    57     Preferences::GetBool(kGamepadEventsEnabledPref, false);
    58   nsCOMPtr<nsIObserverService> observerService =
    59     mozilla::services::GetObserverService();
    60   observerService->AddObserver(this,
    61                                NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID,
    62                                false);
    63 }
    65 NS_IMETHODIMP
    66 GamepadService::Observe(nsISupports* aSubject,
    67                         const char* aTopic,
    68                         const char16_t* aData)
    69 {
    70   nsCOMPtr<nsIObserverService> observerService =
    71     mozilla::services::GetObserverService();
    72   observerService->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
    74   BeginShutdown();
    75   return NS_OK;
    76 }
    78 void
    79 GamepadService::BeginShutdown()
    80 {
    81   mShuttingDown = true;
    82   if (mTimer) {
    83     mTimer->Cancel();
    84   }
    85   if (mStarted) {
    86     mozilla::hal::StopMonitoringGamepadStatus();
    87     mStarted = false;
    88   }
    89   // Don't let windows call back to unregister during shutdown
    90   for (uint32_t i = 0; i < mListeners.Length(); i++) {
    91     mListeners[i]->SetHasGamepadEventListener(false);
    92   }
    93   mListeners.Clear();
    94   mGamepads.Clear();
    95   sShutdown = true;
    96 }
    98 void
    99 GamepadService::AddListener(nsGlobalWindow* aWindow)
   100 {
   101   if (mShuttingDown) {
   102     return;
   103   }
   105   if (mListeners.IndexOf(aWindow) != NoIndex) {
   106     return; // already exists
   107   }
   109   if (!mStarted && mEnabled) {
   110     mozilla::hal::StartMonitoringGamepadStatus();
   111     mStarted = true;
   112   }
   114   mListeners.AppendElement(aWindow);
   115 }
   117 void
   118 GamepadService::RemoveListener(nsGlobalWindow* aWindow)
   119 {
   120   if (mShuttingDown) {
   121     // Doesn't matter at this point. It's possible we're being called
   122     // as a result of our own destructor here, so just bail out.
   123     return;
   124   }
   126   if (mListeners.IndexOf(aWindow) == NoIndex) {
   127     return; // doesn't exist
   128   }
   130   mListeners.RemoveElement(aWindow);
   132   if (mListeners.Length() == 0 && !mShuttingDown && mStarted) {
   133     StartCleanupTimer();
   134   }
   135 }
   137 uint32_t
   138 GamepadService::AddGamepad(const char* aId,
   139                            GamepadMappingType aMapping,
   140                            uint32_t aNumButtons,
   141                            uint32_t aNumAxes)
   142 {
   143   //TODO: bug 852258: get initial button/axis state
   144   nsRefPtr<Gamepad> gamepad =
   145     new Gamepad(nullptr,
   146                 NS_ConvertUTF8toUTF16(nsDependentCString(aId)),
   147                 0,
   148                 aMapping,
   149                 aNumButtons,
   150                 aNumAxes);
   151   int index = -1;
   152   for (uint32_t i = 0; i < mGamepads.Length(); i++) {
   153     if (!mGamepads[i]) {
   154       mGamepads[i] = gamepad;
   155       index = i;
   156       break;
   157     }
   158   }
   159   if (index == -1) {
   160     mGamepads.AppendElement(gamepad);
   161     index = mGamepads.Length() - 1;
   162   }
   164   gamepad->SetIndex(index);
   165   NewConnectionEvent(index, true);
   167   return index;
   168 }
   170 void
   171 GamepadService::RemoveGamepad(uint32_t aIndex)
   172 {
   173   if (aIndex < mGamepads.Length()) {
   174     mGamepads[aIndex]->SetConnected(false);
   175     NewConnectionEvent(aIndex, false);
   176     // If this is the last entry in the list, just remove it.
   177     if (aIndex == mGamepads.Length() - 1) {
   178       mGamepads.RemoveElementAt(aIndex);
   179     } else {
   180       // Otherwise just null it out and leave it, so the
   181       // indices of the following entries remain valid.
   182       mGamepads[aIndex] = nullptr;
   183     }
   184   }
   185 }
   187 void
   188 GamepadService::NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed)
   189 {
   190   // Synthesize a value: 1.0 for pressed, 0.0 for unpressed.
   191   NewButtonEvent(aIndex, aButton, aPressed, aPressed ? 1.0L : 0.0L);
   192 }
   194 void
   195 GamepadService::NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed,
   196                                double aValue)
   197 {
   198   if (mShuttingDown || aIndex >= mGamepads.Length()) {
   199     return;
   200   }
   202   mGamepads[aIndex]->SetButton(aButton, aPressed, aValue);
   204   // Hold on to listeners in a separate array because firing events
   205   // can mutate the mListeners array.
   206   nsTArray<nsRefPtr<nsGlobalWindow> > listeners(mListeners);
   208   for (uint32_t i = listeners.Length(); i > 0 ; ) {
   209     --i;
   211     // Only send events to non-background windows
   212     if (!listeners[i]->IsCurrentInnerWindow() ||
   213         listeners[i]->GetOuterWindow()->IsBackground()) {
   214       continue;
   215     }
   217     bool first_time = false;
   218     if (!WindowHasSeenGamepad(listeners[i], aIndex)) {
   219       // This window hasn't seen this gamepad before, so
   220       // send a connection event first.
   221       SetWindowHasSeenGamepad(listeners[i], aIndex);
   222       first_time = true;
   223     }
   225     nsRefPtr<Gamepad> gamepad = listeners[i]->GetGamepad(aIndex);
   226     if (gamepad) {
   227       gamepad->SetButton(aButton, aPressed, aValue);
   228       if (first_time) {
   229         FireConnectionEvent(listeners[i], gamepad, true);
   230       }
   231       if (mNonstandardEventsEnabled) {
   232         // Fire event
   233         FireButtonEvent(listeners[i], gamepad, aButton, aValue);
   234       }
   235     }
   236   }
   237 }
   239 void
   240 GamepadService::FireButtonEvent(EventTarget* aTarget,
   241                                 Gamepad* aGamepad,
   242                                 uint32_t aButton,
   243                                 double aValue)
   244 {
   245   nsString name = aValue == 1.0L ? NS_LITERAL_STRING("gamepadbuttondown") :
   246                                    NS_LITERAL_STRING("gamepadbuttonup");
   247   GamepadButtonEventInit init;
   248   init.mBubbles = false;
   249   init.mCancelable = false;
   250   init.mGamepad = aGamepad;
   251   init.mButton = aButton;
   252   nsRefPtr<GamepadButtonEvent> event =
   253     GamepadButtonEvent::Constructor(aTarget, name, init);
   255   event->SetTrusted(true);
   257   bool defaultActionEnabled = true;
   258   aTarget->DispatchEvent(event, &defaultActionEnabled);
   259 }
   261 void
   262 GamepadService::NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis, double aValue)
   263 {
   264   if (mShuttingDown || aIndex >= mGamepads.Length()) {
   265     return;
   266   }
   267   mGamepads[aIndex]->SetAxis(aAxis, aValue);
   269   // Hold on to listeners in a separate array because firing events
   270   // can mutate the mListeners array.
   271   nsTArray<nsRefPtr<nsGlobalWindow> > listeners(mListeners);
   273   for (uint32_t i = listeners.Length(); i > 0 ; ) {
   274     --i;
   276     // Only send events to non-background windows
   277     if (!listeners[i]->IsCurrentInnerWindow() ||
   278         listeners[i]->GetOuterWindow()->IsBackground()) {
   279       continue;
   280     }
   282     bool first_time = false;
   283     if (!WindowHasSeenGamepad(listeners[i], aIndex)) {
   284       // This window hasn't seen this gamepad before, so
   285       // send a connection event first.
   286       SetWindowHasSeenGamepad(listeners[i], aIndex);
   287       first_time = true;
   288     }
   290     nsRefPtr<Gamepad> gamepad = listeners[i]->GetGamepad(aIndex);
   291     if (gamepad) {
   292       gamepad->SetAxis(aAxis, aValue);
   293       if (first_time) {
   294         FireConnectionEvent(listeners[i], gamepad, true);
   295       }
   296       if (mNonstandardEventsEnabled) {
   297         // Fire event
   298         FireAxisMoveEvent(listeners[i], gamepad, aAxis, aValue);
   299       }
   300     }
   301   }
   302 }
   304 void
   305 GamepadService::FireAxisMoveEvent(EventTarget* aTarget,
   306                                   Gamepad* aGamepad,
   307                                   uint32_t aAxis,
   308                                   double aValue)
   309 {
   310   GamepadAxisMoveEventInit init;
   311   init.mBubbles = false;
   312   init.mCancelable = false;
   313   init.mGamepad = aGamepad;
   314   init.mAxis = aAxis;
   315   init.mValue = aValue;
   316   nsRefPtr<GamepadAxisMoveEvent> event =
   317     GamepadAxisMoveEvent::Constructor(aTarget,
   318                                       NS_LITERAL_STRING("gamepadaxismove"),
   319                                       init);
   321   event->SetTrusted(true);
   323   bool defaultActionEnabled = true;
   324   aTarget->DispatchEvent(event, &defaultActionEnabled);
   325 }
   327 void
   328 GamepadService::NewConnectionEvent(uint32_t aIndex, bool aConnected)
   329 {
   330   if (mShuttingDown || aIndex >= mGamepads.Length()) {
   331     return;
   332   }
   334   // Hold on to listeners in a separate array because firing events
   335   // can mutate the mListeners array.
   336   nsTArray<nsRefPtr<nsGlobalWindow> > listeners(mListeners);
   338   if (aConnected) {
   339     for (uint32_t i = listeners.Length(); i > 0 ; ) {
   340       --i;
   342       // Only send events to non-background windows
   343       if (!listeners[i]->IsCurrentInnerWindow() ||
   344           listeners[i]->GetOuterWindow()->IsBackground()) {
   345         continue;
   346       }
   348       // We don't fire a connected event here unless the window
   349       // has seen input from at least one device.
   350       if (!listeners[i]->HasSeenGamepadInput()) {
   351         continue;
   352       }
   354       SetWindowHasSeenGamepad(listeners[i], aIndex);
   356       nsRefPtr<Gamepad> gamepad = listeners[i]->GetGamepad(aIndex);
   357       if (gamepad) {
   358         // Fire event
   359         FireConnectionEvent(listeners[i], gamepad, aConnected);
   360       }
   361     }
   362   } else {
   363     // For disconnection events, fire one at every window that has received
   364     // data from this gamepad.
   365     for (uint32_t i = listeners.Length(); i > 0 ; ) {
   366       --i;
   368       // Even background windows get these events, so we don't have to
   369       // deal with the hassle of syncing the state of removed gamepads.
   371       if (WindowHasSeenGamepad(listeners[i], aIndex)) {
   372         nsRefPtr<Gamepad> gamepad = listeners[i]->GetGamepad(aIndex);
   373         if (gamepad) {
   374           gamepad->SetConnected(false);
   375           // Fire event
   376           FireConnectionEvent(listeners[i], gamepad, false);
   377           listeners[i]->RemoveGamepad(aIndex);
   378         }
   379       }
   380     }
   381   }
   382 }
   384 void
   385 GamepadService::FireConnectionEvent(EventTarget* aTarget,
   386                                     Gamepad* aGamepad,
   387                                     bool aConnected)
   388 {
   389   nsString name = aConnected ? NS_LITERAL_STRING("gamepadconnected") :
   390                                NS_LITERAL_STRING("gamepaddisconnected");
   391   GamepadEventInit init;
   392   init.mBubbles = false;
   393   init.mCancelable = false;
   394   init.mGamepad = aGamepad;
   395   nsRefPtr<GamepadEvent> event =
   396     GamepadEvent::Constructor(aTarget, name, init);
   398   event->SetTrusted(true);
   400   bool defaultActionEnabled = true;
   401   aTarget->DispatchEvent(event, &defaultActionEnabled);
   402 }
   404 void
   405 GamepadService::SyncGamepadState(uint32_t aIndex, Gamepad* aGamepad)
   406 {
   407   if (mShuttingDown || !mEnabled || aIndex > mGamepads.Length()) {
   408     return;
   409   }
   411   aGamepad->SyncState(mGamepads[aIndex]);
   412 }
   414 // static
   415 already_AddRefed<GamepadService>
   416 GamepadService::GetService()
   417 {
   418   if (sShutdown) {
   419     return nullptr;
   420   }
   422   if (!gGamepadServiceSingleton) {
   423     gGamepadServiceSingleton = new GamepadService();
   424     ClearOnShutdown(&gGamepadServiceSingleton);
   425   }
   426   nsRefPtr<GamepadService> service(gGamepadServiceSingleton);
   427   return service.forget();
   428 }
   430 // static
   431 bool
   432 GamepadService::IsAPIEnabled() {
   433   return Preferences::GetBool(kGamepadEnabledPref, false);
   434 }
   436 bool
   437 GamepadService::WindowHasSeenGamepad(nsGlobalWindow* aWindow, uint32_t aIndex)
   438 {
   439   nsRefPtr<Gamepad> gamepad = aWindow->GetGamepad(aIndex);
   440   return gamepad != nullptr;
   441 }
   443 void
   444 GamepadService::SetWindowHasSeenGamepad(nsGlobalWindow* aWindow,
   445                                         uint32_t aIndex,
   446                                         bool aHasSeen)
   447 {
   448   if (mListeners.IndexOf(aWindow) == NoIndex) {
   449     // This window isn't even listening for gamepad events.
   450     return;
   451   }
   453   if (aHasSeen) {
   454     aWindow->SetHasSeenGamepadInput(true);
   455     nsCOMPtr<nsISupports> window = ToSupports(aWindow);
   456     nsRefPtr<Gamepad> gamepad = mGamepads[aIndex]->Clone(window);
   457     aWindow->AddGamepad(aIndex, gamepad);
   458   } else {
   459     aWindow->RemoveGamepad(aIndex);
   460   }
   461 }
   463 // static
   464 void
   465 GamepadService::TimeoutHandler(nsITimer* aTimer, void* aClosure)
   466 {
   467   // the reason that we use self, instead of just using nsITimerCallback or nsIObserver
   468   // is so that subclasses are free to use timers without worry about the base classes's
   469   // usage.
   470   GamepadService* self = reinterpret_cast<GamepadService*>(aClosure);
   471   if (!self) {
   472     NS_ERROR("no self");
   473     return;
   474   }
   476   if (self->mShuttingDown) {
   477     return;
   478   }
   480   if (self->mListeners.Length() == 0) {
   481     mozilla::hal::StopMonitoringGamepadStatus();
   482     self->mStarted = false;
   483     if (!self->mGamepads.IsEmpty()) {
   484       self->mGamepads.Clear();
   485     }
   486   }
   487 }
   489 void
   490 GamepadService::StartCleanupTimer()
   491 {
   492   if (mTimer) {
   493     mTimer->Cancel();
   494   }
   496   mTimer = do_CreateInstance("@mozilla.org/timer;1");
   497   if (mTimer) {
   498     mTimer->InitWithFuncCallback(TimeoutHandler,
   499                                  this,
   500                                  kCleanupDelayMS,
   501                                  nsITimer::TYPE_ONE_SHOT);
   502   }
   503 }
   505 /*
   506  * Implementation of the test service. This is just to provide a simple binding
   507  * of the GamepadService to JavaScript via XPCOM so that we can write Mochitests
   508  * that add and remove fake gamepads, avoiding the platform-specific backends.
   509  */
   510 NS_IMPL_ISUPPORTS(GamepadServiceTest, nsIGamepadServiceTest)
   512 GamepadServiceTest* GamepadServiceTest::sSingleton = nullptr;
   514 // static
   515 already_AddRefed<GamepadServiceTest>
   516 GamepadServiceTest::CreateService()
   517 {
   518   if (sSingleton == nullptr) {
   519     sSingleton = new GamepadServiceTest();
   520   }
   521   nsRefPtr<GamepadServiceTest> service = sSingleton;
   522   return service.forget();
   523 }
   525 GamepadServiceTest::GamepadServiceTest()
   526 {
   527   /* member initializers and constructor code */
   528   nsRefPtr<GamepadService> service = GamepadService::GetService();
   529 }
   531 /* uint32_t addGamepad (in string id, in unsigned long mapping, in unsigned long numButtons, in unsigned long numAxes); */
   532 NS_IMETHODIMP GamepadServiceTest::AddGamepad(const char* aID,
   533                                              uint32_t aMapping,
   534                                              uint32_t aNumButtons,
   535                                              uint32_t aNumAxes,
   536                                              uint32_t* aRetval)
   537 {
   538   *aRetval = gGamepadServiceSingleton->AddGamepad(aID,
   539                                                   static_cast<GamepadMappingType>(aMapping),
   540                                                   aNumButtons,
   541                                                   aNumAxes);
   542   return NS_OK;
   543 }
   545 /* void removeGamepad (in uint32_t index); */
   546 NS_IMETHODIMP GamepadServiceTest::RemoveGamepad(uint32_t aIndex)
   547 {
   548   gGamepadServiceSingleton->RemoveGamepad(aIndex);
   549   return NS_OK;
   550 }
   552 /* void newButtonEvent (in uint32_t index, in uint32_t button,
   553    in boolean pressed); */
   554 NS_IMETHODIMP GamepadServiceTest::NewButtonEvent(uint32_t aIndex,
   555                                                  uint32_t aButton,
   556                                                  bool aPressed)
   557 {
   558   gGamepadServiceSingleton->NewButtonEvent(aIndex, aButton, aPressed);
   559   return NS_OK;
   560 }
   562 /* void newAxisMoveEvent (in uint32_t index, in uint32_t axis,
   563    in double value); */
   564 NS_IMETHODIMP GamepadServiceTest::NewAxisMoveEvent(uint32_t aIndex,
   565                                                    uint32_t aAxis,
   566                                                    double aValue)
   567 {
   568   gGamepadServiceSingleton->NewAxisMoveEvent(aIndex, aAxis, aValue);
   569   return NS_OK;
   570 }
   572 } // namespace dom
   573 } // namespace mozilla

mercurial