1/* 2 * Copyright (C) 2014 Apple 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 6 * are met: 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'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25#include "config.h" 26#include "GamepadManager.h" 27 28#if ENABLE(GAMEPAD) 29 30#include "DOMWindow.h" 31#include "Document.h" 32#include "Gamepad.h" 33#include "GamepadEvent.h" 34#include "GamepadProvider.h" 35#include "Logging.h" 36#include "NavigatorGamepad.h" 37#include "PlatformGamepad.h" 38 39namespace WebCore { 40 41static NavigatorGamepad* navigatorGamepadFromDOMWindow(DOMWindow* window) 42{ 43 Navigator* navigator = window->navigator(); 44 if (!navigator) 45 return nullptr; 46 47 return NavigatorGamepad::from(navigator); 48} 49 50GamepadManager& GamepadManager::shared() 51{ 52 static NeverDestroyed<GamepadManager> sharedManager; 53 return sharedManager; 54} 55 56GamepadManager::GamepadManager() 57 : m_isMonitoringGamepads(false) 58{ 59} 60 61void GamepadManager::platformGamepadConnected(PlatformGamepad& platformGamepad) 62{ 63 // Notify blind Navigators and Windows about all gamepads except for this one. 64 for (auto* gamepad : GamepadProvider::shared().platformGamepads()) { 65 if (!gamepad || gamepad == &platformGamepad) 66 continue; 67 68 makeGamepadVisible(*gamepad, m_gamepadBlindNavigators, m_gamepadBlindDOMWindows); 69 } 70 71 m_gamepadBlindNavigators.clear(); 72 m_gamepadBlindDOMWindows.clear(); 73 74 // Notify everyone of this new gamepad. 75 makeGamepadVisible(platformGamepad, m_navigators, m_domWindows); 76} 77 78void GamepadManager::platformGamepadDisconnected(PlatformGamepad& platformGamepad) 79{ 80 Vector<DOMWindow*> domWindowVector; 81 copyToVector(m_domWindows, domWindowVector); 82 83 HashSet<NavigatorGamepad*> notifiedNavigators; 84 85 // Handle the disconnect for all DOMWindows with event listeners and their Navigators. 86 for (auto* window : domWindowVector) { 87 // Event dispatch might have made this window go away. 88 if (!m_domWindows.contains(window)) 89 continue; 90 91 NavigatorGamepad* navigator = navigatorGamepadFromDOMWindow(window); 92 if (!navigator) 93 continue; 94 95 // If this Navigator hasn't seen gamepads yet then its Window should not get the disconnect event. 96 if (m_gamepadBlindNavigators.contains(navigator)) 97 continue; 98 99 RefPtr<Gamepad> gamepad = navigator->gamepadAtIndex(platformGamepad.index()); 100 ASSERT(gamepad); 101 102 navigator->gamepadDisconnected(platformGamepad); 103 notifiedNavigators.add(navigator); 104 105 window->dispatchEvent(GamepadEvent::create(eventNames().gamepaddisconnectedEvent, gamepad.get()), window->document()); 106 } 107 108 // Notify all the Navigators that haven't already been notified. 109 for (auto* navigator : m_navigators) { 110 if (!notifiedNavigators.contains(navigator)) 111 navigator->gamepadDisconnected(platformGamepad); 112 } 113} 114 115void GamepadManager::platformGamepadInputActivity() 116{ 117 if (m_gamepadBlindNavigators.isEmpty() && m_gamepadBlindDOMWindows.isEmpty()) 118 return; 119 120 for (auto* gamepad : GamepadProvider::shared().platformGamepads()) 121 makeGamepadVisible(*gamepad, m_gamepadBlindNavigators, m_gamepadBlindDOMWindows); 122 123 m_gamepadBlindNavigators.clear(); 124 m_gamepadBlindDOMWindows.clear(); 125} 126 127void GamepadManager::makeGamepadVisible(PlatformGamepad& platformGamepad, HashSet<NavigatorGamepad*>& navigatorSet, HashSet<DOMWindow*>& domWindowSet) 128{ 129 if (navigatorSet.isEmpty() && domWindowSet.isEmpty()) 130 return; 131 132 for (auto* navigator : navigatorSet) 133 navigator->gamepadConnected(platformGamepad); 134 135 Vector<DOMWindow*> domWindowVector; 136 copyToVector(domWindowSet, domWindowVector); 137 138 for (auto* window : domWindowVector) { 139 // Event dispatch might have made this window go away. 140 if (!m_domWindows.contains(window)) 141 continue; 142 143 NavigatorGamepad* navigator = navigatorGamepadFromDOMWindow(window); 144 if (!navigator) 145 continue; 146 147 RefPtr<Gamepad> gamepad = navigator->gamepadAtIndex(platformGamepad.index()); 148 ASSERT(gamepad); 149 150 window->dispatchEvent(GamepadEvent::create(eventNames().gamepadconnectedEvent, gamepad.get()), window->document()); 151 } 152} 153 154void GamepadManager::registerNavigator(NavigatorGamepad* navigator) 155{ 156 LOG(Gamepad, "GamepadManager registering NavigatorGamepad %p", navigator); 157 158 ASSERT(!m_navigators.contains(navigator)); 159 m_navigators.add(navigator); 160 m_gamepadBlindNavigators.add(navigator); 161 162 maybeStartMonitoringGamepads(); 163} 164 165void GamepadManager::unregisterNavigator(NavigatorGamepad* navigator) 166{ 167 LOG(Gamepad, "GamepadManager unregistering NavigatorGamepad %p", navigator); 168 169 ASSERT(m_navigators.contains(navigator)); 170 m_navigators.remove(navigator); 171 m_gamepadBlindNavigators.remove(navigator); 172 173 maybeStopMonitoringGamepads(); 174} 175 176void GamepadManager::registerDOMWindow(DOMWindow* window) 177{ 178 LOG(Gamepad, "GamepadManager registering DOMWindow %p", window); 179 180 ASSERT(!m_domWindows.contains(window)); 181 m_domWindows.add(window); 182 183 // Anytime we register a DOMWindow, we should also double check that its NavigatorGamepad is registered. 184 NavigatorGamepad* navigator = navigatorGamepadFromDOMWindow(window); 185 ASSERT(navigator); 186 187 if (m_navigators.add(navigator).isNewEntry) 188 m_gamepadBlindNavigators.add(navigator); 189 190 // If this DOMWindow's NavigatorGamepad was already registered but was still blind, 191 // then this DOMWindow should be blind. 192 if (m_gamepadBlindNavigators.contains(navigator)) 193 m_gamepadBlindDOMWindows.add(window); 194 195 maybeStartMonitoringGamepads(); 196} 197 198void GamepadManager::unregisterDOMWindow(DOMWindow* window) 199{ 200 LOG(Gamepad, "GamepadManager unregistering DOMWindow %p", window); 201 202 ASSERT(m_domWindows.contains(window)); 203 m_domWindows.remove(window); 204 m_gamepadBlindDOMWindows.remove(window); 205 206 maybeStopMonitoringGamepads(); 207} 208 209void GamepadManager::maybeStartMonitoringGamepads() 210{ 211 if (m_isMonitoringGamepads) 212 return; 213 214 if (!m_navigators.isEmpty() || !m_domWindows.isEmpty()) { 215 LOG(Gamepad, "GamepadManager has %i NavigatorGamepads and %i DOMWindows registered, is starting gamepad monitoring", m_navigators.size(), m_domWindows.size()); 216 m_isMonitoringGamepads = true; 217 GamepadProvider::shared().startMonitoringGamepads(this); 218 } 219} 220 221void GamepadManager::maybeStopMonitoringGamepads() 222{ 223 if (!m_isMonitoringGamepads) 224 return; 225 226 if (m_navigators.isEmpty() && m_domWindows.isEmpty()) { 227 LOG(Gamepad, "GamepadManager has no NavigatorGamepads or DOMWindows registered, is stopping gamepad monitoring"); 228 m_isMonitoringGamepads = false; 229 GamepadProvider::shared().stopMonitoringGamepads(this); 230 } 231} 232 233} // namespace WebCore 234 235#endif // ENABLE(GAMEPAD) 236