Sat, 03 Jan 2015 20:18:00 +0100
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