Improve use of NeverDestroyed
[WebKit-https.git] / Source / WebCore / Modules / gamepad / GamepadManager.cpp
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
26 #include "config.h"
27 #include "GamepadManager.h"
28
29 #if ENABLE(GAMEPAD)
30
31 #include "DOMWindow.h"
32 #include "Document.h"
33 #include "EventNames.h"
34 #include "Gamepad.h"
35 #include "GamepadEvent.h"
36 #include "GamepadProvider.h"
37 #include "Logging.h"
38 #include "NavigatorGamepad.h"
39 #include "PlatformGamepad.h"
40 #include <wtf/NeverDestroyed.h>
41
42 namespace WebCore {
43
44 static NavigatorGamepad* navigatorGamepadFromDOMWindow(DOMWindow* window)
45 {
46     Navigator* navigator = window->navigator();
47     if (!navigator)
48         return nullptr;
49
50     return NavigatorGamepad::from(navigator);
51 }
52
53 GamepadManager& GamepadManager::singleton()
54 {
55     static NeverDestroyed<GamepadManager> sharedManager;
56     return sharedManager;
57 }
58
59 GamepadManager::GamepadManager()
60     : m_isMonitoringGamepads(false)
61 {
62 }
63
64 void GamepadManager::platformGamepadConnected(PlatformGamepad& platformGamepad)
65 {
66     // Notify blind Navigators and Windows about all gamepads except for this one.
67     for (auto* gamepad : GamepadProvider::singleton().platformGamepads()) {
68         if (!gamepad || gamepad == &platformGamepad)
69             continue;
70
71         makeGamepadVisible(*gamepad, m_gamepadBlindNavigators, m_gamepadBlindDOMWindows);
72     }
73
74     m_gamepadBlindNavigators.clear();
75     m_gamepadBlindDOMWindows.clear();
76
77     // Notify everyone of this new gamepad.
78     makeGamepadVisible(platformGamepad, m_navigators, m_domWindows);
79 }
80
81 void GamepadManager::platformGamepadDisconnected(PlatformGamepad& platformGamepad)
82 {
83     Vector<WeakPtr<DOMWindow>> weakWindows;
84     for (auto* domWindow : m_domWindows)
85         weakWindows.append(domWindow->createWeakPtr());
86
87     HashSet<NavigatorGamepad*> notifiedNavigators;
88
89     // Handle the disconnect for all DOMWindows with event listeners and their Navigators.
90     for (auto& window : weakWindows) {
91         // Event dispatch might have made this window go away.
92         if (!window)
93             continue;
94
95         // This DOMWindow's Navigator might not be accessible. e.g. The DOMWindow might be in the back/forward cache.
96         // If this happens the DOMWindow will not get this gamepaddisconnected event.
97         NavigatorGamepad* navigator = navigatorGamepadFromDOMWindow(window.get());
98         if (!navigator)
99             continue;
100
101         // If this Navigator hasn't seen gamepads yet then its Window should not get the disconnect event.
102         if (m_gamepadBlindNavigators.contains(navigator))
103             continue;
104
105         Ref<Gamepad> gamepad(navigator->gamepadFromPlatformGamepad(platformGamepad));
106
107         navigator->gamepadDisconnected(platformGamepad);
108         notifiedNavigators.add(navigator);
109
110         window->dispatchEvent(GamepadEvent::create(eventNames().gamepaddisconnectedEvent, gamepad.get()), window->document());
111     }
112
113     // Notify all the Navigators that haven't already been notified.
114     for (auto* navigator : m_navigators) {
115         if (!notifiedNavigators.contains(navigator))
116             navigator->gamepadDisconnected(platformGamepad);
117     }
118 }
119
120 void GamepadManager::platformGamepadInputActivity(bool shouldMakeGamepadVisible)
121 {
122     if (!shouldMakeGamepadVisible)
123         return;
124
125     if (m_gamepadBlindNavigators.isEmpty() && m_gamepadBlindDOMWindows.isEmpty())
126         return;
127
128     for (auto* gamepad : GamepadProvider::singleton().platformGamepads()) {
129         if (gamepad)
130             makeGamepadVisible(*gamepad, m_gamepadBlindNavigators, m_gamepadBlindDOMWindows);
131     }
132
133     m_gamepadBlindNavigators.clear();
134     m_gamepadBlindDOMWindows.clear();
135 }
136
137 void GamepadManager::makeGamepadVisible(PlatformGamepad& platformGamepad, HashSet<NavigatorGamepad*>& navigatorSet, HashSet<DOMWindow*>& domWindowSet)
138 {
139     if (navigatorSet.isEmpty() && domWindowSet.isEmpty())
140         return;
141
142     for (auto* navigator : navigatorSet)
143         navigator->gamepadConnected(platformGamepad);
144
145     Vector<WeakPtr<DOMWindow>> weakWindows;
146     for (auto* domWindow : m_domWindows)
147         weakWindows.append(domWindow->createWeakPtr());
148
149     for (auto& window : weakWindows) {
150         // Event dispatch might have made this window go away.
151         if (!window)
152             continue;
153
154         // This DOMWindow's Navigator might not be accessible. e.g. The DOMWindow might be in the back/forward cache.
155         // If this happens the DOMWindow will not get this gamepadconnected event.
156         // The new gamepad will still be visibile to it once it is restored from the back/forward cache.
157         NavigatorGamepad* navigator = navigatorGamepadFromDOMWindow(window.get());
158         if (!navigator)
159             continue;
160
161         Ref<Gamepad> gamepad(navigator->gamepadFromPlatformGamepad(platformGamepad));
162         window->dispatchEvent(GamepadEvent::create(eventNames().gamepadconnectedEvent, gamepad.get()), window->document());
163     }
164 }
165
166 void GamepadManager::registerNavigator(NavigatorGamepad* navigator)
167 {
168     LOG(Gamepad, "GamepadManager registering NavigatorGamepad %p", navigator);
169
170     ASSERT(!m_navigators.contains(navigator));
171     m_navigators.add(navigator);
172     m_gamepadBlindNavigators.add(navigator);
173
174     maybeStartMonitoringGamepads();
175 }
176
177 void GamepadManager::unregisterNavigator(NavigatorGamepad* navigator)
178 {
179     LOG(Gamepad, "GamepadManager unregistering NavigatorGamepad %p", navigator);
180
181     ASSERT(m_navigators.contains(navigator));
182     m_navigators.remove(navigator);
183     m_gamepadBlindNavigators.remove(navigator);
184
185     maybeStopMonitoringGamepads();
186 }
187
188 void GamepadManager::registerDOMWindow(DOMWindow* window)
189 {
190     LOG(Gamepad, "GamepadManager registering DOMWindow %p", window);
191
192     ASSERT(!m_domWindows.contains(window));
193     m_domWindows.add(window);
194
195     // Anytime we register a DOMWindow, we should also double check that its NavigatorGamepad is registered.
196     NavigatorGamepad* navigator = navigatorGamepadFromDOMWindow(window);
197     ASSERT(navigator);
198
199     if (m_navigators.add(navigator).isNewEntry)
200         m_gamepadBlindNavigators.add(navigator);
201
202     // If this DOMWindow's NavigatorGamepad was already registered but was still blind,
203     // then this DOMWindow should be blind.
204     if (m_gamepadBlindNavigators.contains(navigator))
205         m_gamepadBlindDOMWindows.add(window);
206
207     maybeStartMonitoringGamepads();
208 }
209
210 void GamepadManager::unregisterDOMWindow(DOMWindow* window)
211 {
212     LOG(Gamepad, "GamepadManager unregistering DOMWindow %p", window);
213
214     ASSERT(m_domWindows.contains(window));
215     m_domWindows.remove(window);
216     m_gamepadBlindDOMWindows.remove(window);
217
218     maybeStopMonitoringGamepads();
219 }
220
221 void GamepadManager::maybeStartMonitoringGamepads()
222 {
223     if (m_isMonitoringGamepads)
224         return;
225
226     if (!m_navigators.isEmpty() || !m_domWindows.isEmpty()) {
227         LOG(Gamepad, "GamepadManager has %i NavigatorGamepads and %i DOMWindows registered, is starting gamepad monitoring", m_navigators.size(), m_domWindows.size());
228         m_isMonitoringGamepads = true;
229         GamepadProvider::singleton().startMonitoringGamepads(*this);
230     }
231 }
232
233 void GamepadManager::maybeStopMonitoringGamepads()
234 {
235     if (!m_isMonitoringGamepads)
236         return;
237
238     if (m_navigators.isEmpty() && m_domWindows.isEmpty()) {
239         LOG(Gamepad, "GamepadManager has no NavigatorGamepads or DOMWindows registered, is stopping gamepad monitoring");
240         m_isMonitoringGamepads = false;
241         GamepadProvider::singleton().stopMonitoringGamepads(*this);
242     }
243 }
244
245 } // namespace WebCore
246
247 #endif // ENABLE(GAMEPAD)