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 #include "platform/RuntimeEnabledFeatures.h" 39 40 namespace WebCore { 41 42 template<typename T> 43 static void sampleGamepad(unsigned index, T& gamepad, const blink::WebGamepad& webGamepad) 44 { 45 gamepad.setId(webGamepad.id); 46 gamepad.setIndex(index); 47 gamepad.setConnected(webGamepad.connected); 48 gamepad.setTimestamp(webGamepad.timestamp); 49 gamepad.setMapping(webGamepad.mapping); 50 gamepad.setAxes(webGamepad.axesLength, webGamepad.axes); 51 gamepad.setButtons(webGamepad.buttonsLength, webGamepad.buttons); 52 } 53 54 template<typename GamepadType, typename ListType> 55 static void sampleGamepads(ListType* into) 56 { 57 blink::WebGamepads gamepads; 58 59 GamepadDispatcher::instance().sampleGamepads(gamepads); 60 61 for (unsigned i = 0; i < blink::WebGamepads::itemsLengthCap; ++i) { 62 blink::WebGamepad& webGamepad = gamepads.items[i]; 63 if (i < gamepads.length && webGamepad.connected) { 64 GamepadType* gamepad = into->item(i); 65 if (!gamepad) 66 gamepad = GamepadType::create(); 67 sampleGamepad(i, *gamepad, webGamepad); 68 into->set(i, gamepad); 69 } else { 70 into->set(i, 0); 71 } 72 } 73 } 74 75 NavigatorGamepad* NavigatorGamepad::from(Document& document) 76 { 77 if (!document.frame() || !document.frame()->domWindow()) 78 return 0; 79 Navigator& navigator = document.frame()->domWindow()->navigator(); 80 return &from(navigator); 81 } 82 83 NavigatorGamepad& NavigatorGamepad::from(Navigator& navigator) 84 { 85 NavigatorGamepad* supplement = static_cast<NavigatorGamepad*>(WillBeHeapSupplement<Navigator>::from(navigator, supplementName())); 86 if (!supplement) { 87 supplement = new NavigatorGamepad(navigator.frame()); 88 provideTo(navigator, supplementName(), adoptPtrWillBeNoop(supplement)); 89 } 90 return *supplement; 91 } 92 93 WebKitGamepadList* NavigatorGamepad::webkitGetGamepads(Navigator& navigator) 94 { 95 return NavigatorGamepad::from(navigator).webkitGamepads(); 96 } 97 98 GamepadList* NavigatorGamepad::getGamepads(Navigator& navigator) 99 { 100 return NavigatorGamepad::from(navigator).gamepads(); 101 } 102 103 WebKitGamepadList* NavigatorGamepad::webkitGamepads() 104 { 105 if (!m_webkitGamepads) 106 m_webkitGamepads = WebKitGamepadList::create(); 107 if (window()) { 108 startUpdating(); 109 sampleGamepads<WebKitGamepad>(m_webkitGamepads.get()); 110 } 111 return m_webkitGamepads.get(); 112 } 113 114 GamepadList* NavigatorGamepad::gamepads() 115 { 116 if (!m_gamepads) 117 m_gamepads = GamepadList::create(); 118 if (window()) { 119 startUpdating(); 120 sampleGamepads<Gamepad>(m_gamepads.get()); 121 } 122 return m_gamepads.get(); 123 } 124 125 void NavigatorGamepad::trace(Visitor* visitor) 126 { 127 visitor->trace(m_gamepads); 128 visitor->trace(m_webkitGamepads); 129 WillBeHeapSupplement<Navigator>::trace(visitor); 130 } 131 132 void NavigatorGamepad::didUpdateData() 133 { 134 // We should stop listening once we detached. 135 ASSERT(window()); 136 137 // We register to the dispatcher before sampling gamepads so we need to check if we actually have an event listener. 138 if (!m_hasEventListener) 139 return; 140 141 if (window()->document()->activeDOMObjectsAreStopped() || window()->document()->activeDOMObjectsAreSuspended()) 142 return; 143 144 const GamepadDispatcher::ConnectionChange& change = GamepadDispatcher::instance().latestConnectionChange(); 145 146 if (!m_gamepads) 147 m_gamepads = GamepadList::create(); 148 149 Gamepad* gamepad = m_gamepads->item(change.index); 150 if (!gamepad) 151 gamepad = Gamepad::create(); 152 sampleGamepad(change.index, *gamepad, change.pad); 153 m_gamepads->set(change.index, gamepad); 154 155 const AtomicString& eventName = change.pad.connected ? EventTypeNames::gamepadconnected : EventTypeNames::gamepaddisconnected; 156 window()->dispatchEvent(GamepadEvent::create(eventName, false, true, gamepad)); 157 } 158 159 NavigatorGamepad::NavigatorGamepad(LocalFrame* frame) 160 : DOMWindowProperty(frame) 161 , DeviceEventControllerBase(frame ? frame->page() : 0) 162 , DOMWindowLifecycleObserver(frame ? frame->domWindow() : 0) 163 { 164 } 165 166 NavigatorGamepad::~NavigatorGamepad() 167 { 168 } 169 170 const char* NavigatorGamepad::supplementName() 171 { 172 return "NavigatorGamepad"; 173 } 174 175 void NavigatorGamepad::willDestroyGlobalObjectInFrame() 176 { 177 stopUpdating(); 178 DOMWindowProperty::willDestroyGlobalObjectInFrame(); 179 } 180 181 void NavigatorGamepad::willDetachGlobalObjectFromFrame() 182 { 183 stopUpdating(); 184 DOMWindowProperty::willDetachGlobalObjectFromFrame(); 185 } 186 187 void NavigatorGamepad::registerWithDispatcher() 188 { 189 GamepadDispatcher::instance().addController(this); 190 } 191 192 void NavigatorGamepad::unregisterWithDispatcher() 193 { 194 GamepadDispatcher::instance().removeController(this); 195 } 196 197 bool NavigatorGamepad::hasLastData() 198 { 199 // Gamepad data is polled instead of pushed. 200 return false; 201 } 202 203 static bool isGamepadEvent(const AtomicString& eventType) 204 { 205 return eventType == EventTypeNames::gamepadconnected || eventType == EventTypeNames::gamepaddisconnected; 206 } 207 208 void NavigatorGamepad::didAddEventListener(LocalDOMWindow*, const AtomicString& eventType) 209 { 210 if (RuntimeEnabledFeatures::gamepadEnabled() && isGamepadEvent(eventType)) { 211 if (page() && page()->visibilityState() == PageVisibilityStateVisible) 212 startUpdating(); 213 m_hasEventListener = true; 214 } 215 } 216 217 void NavigatorGamepad::didRemoveEventListener(LocalDOMWindow* window, const AtomicString& eventType) 218 { 219 if (isGamepadEvent(eventType) 220 && !window->hasEventListeners(EventTypeNames::gamepadconnected) 221 && !window->hasEventListeners(EventTypeNames::gamepaddisconnected)) { 222 m_hasEventListener = false; 223 } 224 } 225 226 void NavigatorGamepad::didRemoveAllEventListeners(LocalDOMWindow*) 227 { 228 m_hasEventListener = false; 229 } 230 231 } // namespace WebCore 232