Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / modules / gamepad / NavigatorGamepad.cpp
1 /*
2  * Copyright (C) 2011, Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
23  * DAMAGE.
24  */
25
26 #include "config.h"
27 #include "modules/gamepad/NavigatorGamepad.h"
28
29 #include "core/dom/Document.h"
30 #include "core/frame/LocalDOMWindow.h"
31 #include "core/frame/LocalFrame.h"
32 #include "core/frame/Navigator.h"
33 #include "core/page/Page.h"
34 #include "modules/gamepad/GamepadDispatcher.h"
35 #include "modules/gamepad/GamepadEvent.h"
36 #include "modules/gamepad/GamepadList.h"
37 #include "modules/gamepad/WebKitGamepadList.h"
38
39 namespace blink {
40
41 template<typename T>
42 static void sampleGamepad(unsigned index, T& gamepad, const WebGamepad& webGamepad)
43 {
44     gamepad.setId(webGamepad.id);
45     gamepad.setIndex(index);
46     gamepad.setConnected(webGamepad.connected);
47     gamepad.setTimestamp(webGamepad.timestamp);
48     gamepad.setMapping(webGamepad.mapping);
49     gamepad.setAxes(webGamepad.axesLength, webGamepad.axes);
50     gamepad.setButtons(webGamepad.buttonsLength, webGamepad.buttons);
51 }
52
53 template<typename GamepadType, typename ListType>
54 static void sampleGamepads(ListType* into)
55 {
56     WebGamepads gamepads;
57
58     GamepadDispatcher::instance().sampleGamepads(gamepads);
59
60     for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
61         WebGamepad& webGamepad = gamepads.items[i];
62         if (i < gamepads.length && webGamepad.connected) {
63             GamepadType* gamepad = into->item(i);
64             if (!gamepad)
65                 gamepad = GamepadType::create();
66             sampleGamepad(i, *gamepad, webGamepad);
67             into->set(i, gamepad);
68         } else {
69             into->set(i, 0);
70         }
71     }
72 }
73
74 NavigatorGamepad* NavigatorGamepad::from(Document& document)
75 {
76     if (!document.frame() || !document.frame()->domWindow())
77         return 0;
78     Navigator& navigator = *document.frame()->domWindow()->navigator();
79     return &from(navigator);
80 }
81
82 NavigatorGamepad& NavigatorGamepad::from(Navigator& navigator)
83 {
84     NavigatorGamepad* supplement = static_cast<NavigatorGamepad*>(WillBeHeapSupplement<Navigator>::from(navigator, supplementName()));
85     if (!supplement) {
86         supplement = new NavigatorGamepad(navigator.frame());
87         provideTo(navigator, supplementName(), adoptPtrWillBeNoop(supplement));
88     }
89     return *supplement;
90 }
91
92 WebKitGamepadList* NavigatorGamepad::webkitGetGamepads(Navigator& navigator)
93 {
94     return NavigatorGamepad::from(navigator).webkitGamepads();
95 }
96
97 GamepadList* NavigatorGamepad::getGamepads(Navigator& navigator)
98 {
99     return NavigatorGamepad::from(navigator).gamepads();
100 }
101
102 WebKitGamepadList* NavigatorGamepad::webkitGamepads()
103 {
104     if (!m_webkitGamepads)
105         m_webkitGamepads = WebKitGamepadList::create();
106     if (window()) {
107         startUpdating();
108         sampleGamepads<WebKitGamepad>(m_webkitGamepads.get());
109     }
110     return m_webkitGamepads.get();
111 }
112
113 GamepadList* NavigatorGamepad::gamepads()
114 {
115     if (!m_gamepads)
116         m_gamepads = GamepadList::create();
117     if (window()) {
118         startUpdating();
119         sampleGamepads<Gamepad>(m_gamepads.get());
120     }
121     return m_gamepads.get();
122 }
123
124 void NavigatorGamepad::trace(Visitor* visitor)
125 {
126     visitor->trace(m_gamepads);
127     visitor->trace(m_webkitGamepads);
128     visitor->trace(m_pendingEvents);
129     WillBeHeapSupplement<Navigator>::trace(visitor);
130     DOMWindowProperty::trace(visitor);
131     PlatformEventController::trace(visitor);
132 }
133
134 void NavigatorGamepad::didUpdateData()
135 {
136     // We should stop listening once we detached.
137     ASSERT(window());
138
139     // We register to the dispatcher before sampling gamepads so we need to check if we actually have an event listener.
140     if (!m_hasEventListener)
141         return;
142
143     if (window()->document()->activeDOMObjectsAreStopped() || window()->document()->activeDOMObjectsAreSuspended())
144         return;
145
146     const GamepadDispatcher::ConnectionChange& change = GamepadDispatcher::instance().latestConnectionChange();
147
148     if (!m_gamepads)
149         m_gamepads = GamepadList::create();
150
151     Gamepad* gamepad = m_gamepads->item(change.index);
152     if (!gamepad)
153         gamepad = Gamepad::create();
154     sampleGamepad(change.index, *gamepad, change.pad);
155     m_gamepads->set(change.index, gamepad);
156
157     m_pendingEvents.append(gamepad);
158     m_dispatchOneEventRunner.runAsync();
159 }
160
161 void NavigatorGamepad::dispatchOneEvent()
162 {
163     ASSERT(window());
164     ASSERT(!m_pendingEvents.isEmpty());
165
166     Gamepad* gamepad = m_pendingEvents.takeFirst();
167     const AtomicString& eventName = gamepad->connected() ? EventTypeNames::gamepadconnected : EventTypeNames::gamepaddisconnected;
168     window()->dispatchEvent(GamepadEvent::create(eventName, false, true, gamepad));
169
170     if (!m_pendingEvents.isEmpty())
171         m_dispatchOneEventRunner.runAsync();
172 }
173
174 NavigatorGamepad::NavigatorGamepad(LocalFrame* frame)
175     : DOMWindowProperty(frame)
176     , PlatformEventController(frame ? frame->page() : 0)
177     , DOMWindowLifecycleObserver(frame ? frame->domWindow() : 0)
178     , m_dispatchOneEventRunner(this, &NavigatorGamepad::dispatchOneEvent)
179 {
180 }
181
182 NavigatorGamepad::~NavigatorGamepad()
183 {
184 }
185
186 const char* NavigatorGamepad::supplementName()
187 {
188     return "NavigatorGamepad";
189 }
190
191 void NavigatorGamepad::willDestroyGlobalObjectInFrame()
192 {
193     stopUpdating();
194     DOMWindowProperty::willDestroyGlobalObjectInFrame();
195 }
196
197 void NavigatorGamepad::willDetachGlobalObjectFromFrame()
198 {
199     stopUpdating();
200     DOMWindowProperty::willDetachGlobalObjectFromFrame();
201 }
202
203 void NavigatorGamepad::registerWithDispatcher()
204 {
205     GamepadDispatcher::instance().addController(this);
206     m_dispatchOneEventRunner.resume();
207 }
208
209 void NavigatorGamepad::unregisterWithDispatcher()
210 {
211     m_dispatchOneEventRunner.suspend();
212     GamepadDispatcher::instance().removeController(this);
213 }
214
215 bool NavigatorGamepad::hasLastData()
216 {
217     // Gamepad data is polled instead of pushed.
218     return false;
219 }
220
221 static bool isGamepadEvent(const AtomicString& eventType)
222 {
223     return eventType == EventTypeNames::gamepadconnected || eventType == EventTypeNames::gamepaddisconnected;
224 }
225
226 void NavigatorGamepad::didAddEventListener(LocalDOMWindow*, const AtomicString& eventType)
227 {
228     if (isGamepadEvent(eventType)) {
229         if (page() && page()->visibilityState() == PageVisibilityStateVisible)
230             startUpdating();
231         m_hasEventListener = true;
232     }
233 }
234
235 void NavigatorGamepad::didRemoveEventListener(LocalDOMWindow* window, const AtomicString& eventType)
236 {
237     if (isGamepadEvent(eventType)
238         && !window->hasEventListeners(EventTypeNames::gamepadconnected)
239         && !window->hasEventListeners(EventTypeNames::gamepaddisconnected)) {
240         didRemoveGamepadEventListeners();
241     }
242 }
243
244 void NavigatorGamepad::didRemoveAllEventListeners(LocalDOMWindow*)
245 {
246     didRemoveGamepadEventListeners();
247 }
248
249 void NavigatorGamepad::didRemoveGamepadEventListeners()
250 {
251     m_hasEventListener = false;
252     m_dispatchOneEventRunner.stop();
253     m_pendingEvents.clear();
254 }
255
256 void NavigatorGamepad::pageVisibilityChanged()
257 {
258     // Inform the embedder whether it needs to provide gamepad data for us.
259     bool visible = page()->visibilityState() == PageVisibilityStateVisible;
260     if (visible && (m_hasEventListener || m_gamepads || m_webkitGamepads))
261         startUpdating();
262     else
263         stopUpdating();
264
265     if (!visible || !m_hasEventListener)
266         return;
267
268     // Tell the page what has changed. m_gamepads contains the state before we became hidden.
269     // We create a new snapshot and compare them.
270     GamepadList* oldGamepads = m_gamepads.release();
271     gamepads();
272     GamepadList* newGamepads = m_gamepads.get();
273     ASSERT(newGamepads);
274
275     for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
276         Gamepad* oldGamepad = oldGamepads ? oldGamepads->item(i) : 0;
277         Gamepad* newGamepad = newGamepads->item(i);
278         bool oldWasConnected = oldGamepad && oldGamepad->connected();
279         bool newIsConnected = newGamepad && newGamepad->connected();
280         bool connectedGamepadChanged = oldWasConnected && newIsConnected && oldGamepad->id() != newGamepad->id();
281         if (connectedGamepadChanged || (oldWasConnected && !newIsConnected)) {
282             oldGamepad->setConnected(false);
283             m_pendingEvents.append(oldGamepad);
284         }
285         if (connectedGamepadChanged || (!oldWasConnected && newIsConnected)) {
286             m_pendingEvents.append(newGamepad);
287         }
288     }
289
290     if (!m_pendingEvents.isEmpty())
291         m_dispatchOneEventRunner.runAsync();
292 }
293
294 } // namespace blink