Index: Source/modules/gamepad/NavigatorGamepad.cpp |
diff --git a/Source/modules/gamepad/NavigatorGamepad.cpp b/Source/modules/gamepad/NavigatorGamepad.cpp |
index d1c006ef9a76fdb2b6725626b327a8a675a79c49..38ac468eaba9b6ade51f98a4ea237bd53467cb01 100644 |
--- a/Source/modules/gamepad/NavigatorGamepad.cpp |
+++ b/Source/modules/gamepad/NavigatorGamepad.cpp |
@@ -36,6 +36,7 @@ |
#include "modules/gamepad/GamepadEvent.h" |
#include "modules/gamepad/GamepadList.h" |
#include "modules/gamepad/WebKitGamepadList.h" |
+#include "wtf/TemporaryChange.h" |
namespace WebCore { |
@@ -134,16 +135,10 @@ void NavigatorGamepad::didConnectOrDisconnectGamepad(unsigned index, const blink |
ASSERT(index < blink::WebGamepads::itemsLengthCap); |
ASSERT(connected == webGamepad.connected); |
- // We should stop listening once we detached. |
- ASSERT(window()); |
- |
// We register to the dispatcher before sampling gamepads so we need to check if we actually have an event listener. |
if (!m_hasEventListener) |
return; |
- if (window()->document()->activeDOMObjectsAreStopped() || window()->document()->activeDOMObjectsAreSuspended()) |
- return; |
- |
if (!m_gamepads) |
m_gamepads = GamepadList::create(); |
@@ -154,14 +149,14 @@ void NavigatorGamepad::didConnectOrDisconnectGamepad(unsigned index, const blink |
m_gamepads->set(index, gamepad); |
const AtomicString& eventName = connected ? EventTypeNames::gamepadconnected : EventTypeNames::gamepaddisconnected; |
- RefPtrWillBeRawPtr<GamepadEvent> event = GamepadEvent::create(eventName, false, true, gamepad); |
- window()->dispatchEvent(event); |
+ dispatchDeviceEvent(GamepadEvent::create(eventName, false, true, gamepad)); |
} |
NavigatorGamepad::NavigatorGamepad(LocalFrame* frame) |
: DOMWindowProperty(frame) |
, DeviceSensorEventController(frame ? frame->page() : 0) |
, DOMWindowLifecycleObserver(frame ? frame->domWindow() : 0) |
+ , m_canContinueDispatchingSentinelPtr(0) |
{ |
} |
@@ -177,12 +172,14 @@ const char* NavigatorGamepad::supplementName() |
void NavigatorGamepad::willDestroyGlobalObjectInFrame() |
{ |
stopUpdating(); |
+ invalidateCanContinueDispatchingSentinel(); |
DOMWindowProperty::willDestroyGlobalObjectInFrame(); |
} |
void NavigatorGamepad::willDetachGlobalObjectFromFrame() |
{ |
stopUpdating(); |
+ invalidateCanContinueDispatchingSentinel(); |
DOMWindowProperty::willDetachGlobalObjectFromFrame(); |
} |
@@ -211,8 +208,6 @@ PassRefPtrWillBeRawPtr<Event> NavigatorGamepad::getLastEvent() |
bool NavigatorGamepad::isNullEvent(Event*) |
{ |
- // This is called only when hasLastData() is true. |
- ASSERT_NOT_REACHED(); |
return false; |
} |
@@ -235,10 +230,11 @@ void NavigatorGamepad::didAddEventListener(DOMWindow*, const AtomicString& event |
} |
} |
-void NavigatorGamepad::didRemoveEventListener(DOMWindow*, const AtomicString& eventType) |
+void NavigatorGamepad::didRemoveEventListener(DOMWindow* window, const AtomicString& eventType) |
{ |
- if (isGamepadEvent(eventType)) |
- m_hasEventListener = false; |
+ if (!isGamepadEvent(eventType) || window->hasEventListeners(EventTypeNames::gamepadconnected) || window->hasEventListeners(EventTypeNames::gamepaddisconnected)) |
+ return; |
+ m_hasEventListener = false; |
} |
void NavigatorGamepad::didRemoveAllEventListeners(DOMWindow*) |
@@ -246,4 +242,63 @@ void NavigatorGamepad::didRemoveAllEventListeners(DOMWindow*) |
m_hasEventListener = false; |
} |
+void NavigatorGamepad::willBeDestroyed() |
+{ |
+ invalidateCanContinueDispatchingSentinel(); |
+} |
+ |
+void NavigatorGamepad::pageVisibilityChanged() |
+{ |
+ DeviceSensorEventController::pageVisibilityChanged(); |
+ |
+ if (page()->visibilityState() != PageVisibilityStateVisible || !m_hasEventListener) |
+ return; |
+ |
+ // Tell the page what has changed. m_gamepads contains the state before we became hidden. |
+ // We create a new snapshot and compare them. |
+ GamepadList* oldGamepads = m_gamepads.release(); |
+ gamepads(); |
+ GamepadList* newGamepads = m_gamepads.get(); |
+ ASSERT(newGamepads); |
+ |
+ // Set m_gamepads to null so that if we are polled while dispatching it doesn't mess up our snapshot. |
+ // After we are done, set it back to the new snapshot otherwise we could report these changes multiple |
+ // times after we go hidden and then visible again. |
+ WTF::TemporaryChange<PersistentWillBeMember<GamepadList> > padsScope(m_gamepads, nullptr); |
+ |
+ // We need to dispatch multiple events to the page and we have to deal with the fact that we can be destroyed by the event handler. |
+ // We use a fancy version of the protecting RefPtr idiom because Supplement's are not RefCounted. Before we die we change the value |
+ // of a local variable via our member pointer. After dispatching the event we check this local variable which is safe even if we have died. |
+ // TODO(b.kelemen): simplify this when we can rely on Oilpan. |
+ bool canContinueDispatchingSentinel = true; |
+ WTF::TemporaryChange<bool*> sentinelScope(m_canContinueDispatchingSentinelPtr, &canContinueDispatchingSentinel); |
+ |
+ for (unsigned i = 0; i < blink::WebGamepads::itemsLengthCap; ++i) { |
+ Gamepad* oldGamepad = oldGamepads ? oldGamepads->item(i) : 0; |
+ Gamepad* newGamepad = newGamepads->item(i); |
+ bool oldWasConnected = oldGamepad && oldGamepad->connected(); |
+ bool newIsConnected = newGamepad && newGamepad->connected(); |
+ bool connectedGamepadChanged = oldWasConnected && newIsConnected && oldGamepad->id() != newGamepad->id(); |
+ |
+ if (connectedGamepadChanged || (oldWasConnected && !newIsConnected)) { |
+ const AtomicString& eventName = EventTypeNames::gamepaddisconnected; |
+ dispatchDeviceEvent(GamepadEvent::create(eventName, false, true, oldGamepad)); |
+ if (!canContinueDispatchingSentinel || !m_hasEventListener) |
+ return; |
+ } |
+ if (connectedGamepadChanged || (!oldWasConnected && newIsConnected)) { |
+ const AtomicString& eventName = EventTypeNames::gamepadconnected; |
+ dispatchDeviceEvent(GamepadEvent::create(eventName, false, true, newGamepad)); |
+ if (!canContinueDispatchingSentinel || !m_hasEventListener) |
+ return; |
+ } |
+ } |
+} |
+ |
+void NavigatorGamepad::invalidateCanContinueDispatchingSentinel() |
+{ |
+ if (m_canContinueDispatchingSentinelPtr) |
+ *m_canContinueDispatchingSentinelPtr = false; |
+} |
+ |
} // namespace WebCore |