OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2008 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. ``AS IS'' AND ANY | |
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | |
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 */ | |
25 | |
26 #include "core/page/NetworkStateNotifier.h" | |
27 | |
28 #include "platform/CrossThreadFunctional.h" | |
29 #include "wtf/Assertions.h" | |
30 #include "wtf/Functional.h" | |
31 #include "wtf/PtrUtil.h" | |
32 #include "wtf/StdLibExtras.h" | |
33 #include "wtf/Threading.h" | |
34 | |
35 namespace blink { | |
36 | |
37 template <> | |
38 struct CrossThreadCopier<NetworkStateNotifier::NetworkState> | |
39 : public CrossThreadCopierPassThrough<NetworkStateNotifier::NetworkState> { | |
40 STATIC_ONLY(CrossThreadCopier); | |
41 }; | |
42 | |
43 NetworkStateNotifier& networkStateNotifier() { | |
44 DEFINE_THREAD_SAFE_STATIC_LOCAL(NetworkStateNotifier, networkStateNotifier, | |
45 new NetworkStateNotifier); | |
46 return networkStateNotifier; | |
47 } | |
48 | |
49 NetworkStateNotifier::ScopedNotifier::ScopedNotifier( | |
50 NetworkStateNotifier& notifier) | |
51 : m_notifier(notifier) { | |
52 DCHECK(isMainThread()); | |
53 m_before = | |
54 m_notifier.m_hasOverride ? m_notifier.m_override : m_notifier.m_state; | |
55 } | |
56 | |
57 NetworkStateNotifier::ScopedNotifier::~ScopedNotifier() { | |
58 DCHECK(isMainThread()); | |
59 const NetworkState& after = | |
60 m_notifier.m_hasOverride ? m_notifier.m_override : m_notifier.m_state; | |
61 if ((after.type != m_before.type || | |
62 after.maxBandwidthMbps != m_before.maxBandwidthMbps) && | |
63 m_before.connectionInitialized) { | |
64 m_notifier.notifyObservers(m_notifier.m_connectionObservers, | |
65 ObserverType::CONNECTION_TYPE, after); | |
66 } | |
67 if (after.onLine != m_before.onLine && m_before.onLineInitialized) { | |
68 m_notifier.notifyObservers(m_notifier.m_onLineStateObservers, | |
69 ObserverType::ONLINE_STATE, after); | |
70 } | |
71 } | |
72 | |
73 void NetworkStateNotifier::setOnLine(bool onLine) { | |
74 DCHECK(isMainThread()); | |
75 ScopedNotifier notifier(*this); | |
76 { | |
77 MutexLocker locker(m_mutex); | |
78 m_state.onLineInitialized = true; | |
79 m_state.onLine = onLine; | |
80 } | |
81 } | |
82 | |
83 void NetworkStateNotifier::setWebConnection(WebConnectionType type, | |
84 double maxBandwidthMbps) { | |
85 DCHECK(isMainThread()); | |
86 ScopedNotifier notifier(*this); | |
87 { | |
88 MutexLocker locker(m_mutex); | |
89 m_state.connectionInitialized = true; | |
90 m_state.type = type; | |
91 m_state.maxBandwidthMbps = maxBandwidthMbps; | |
92 } | |
93 } | |
94 | |
95 void NetworkStateNotifier::addConnectionObserver( | |
96 NetworkStateObserver* observer, | |
97 PassRefPtr<WebTaskRunner> taskRunner) { | |
98 addObserver(m_connectionObservers, observer, std::move(taskRunner)); | |
99 } | |
100 | |
101 void NetworkStateNotifier::addOnLineObserver( | |
102 NetworkStateObserver* observer, | |
103 PassRefPtr<WebTaskRunner> taskRunner) { | |
104 addObserver(m_onLineStateObservers, observer, std::move(taskRunner)); | |
105 } | |
106 | |
107 void NetworkStateNotifier::removeConnectionObserver( | |
108 NetworkStateObserver* observer, | |
109 PassRefPtr<WebTaskRunner> taskRunner) { | |
110 removeObserver(m_connectionObservers, observer, std::move(taskRunner)); | |
111 } | |
112 | |
113 void NetworkStateNotifier::removeOnLineObserver( | |
114 NetworkStateObserver* observer, | |
115 PassRefPtr<WebTaskRunner> taskRunner) { | |
116 removeObserver(m_onLineStateObservers, observer, std::move(taskRunner)); | |
117 } | |
118 | |
119 void NetworkStateNotifier::setOverride(bool onLine, | |
120 WebConnectionType type, | |
121 double maxBandwidthMbps) { | |
122 DCHECK(isMainThread()); | |
123 ScopedNotifier notifier(*this); | |
124 { | |
125 MutexLocker locker(m_mutex); | |
126 m_hasOverride = true; | |
127 m_override.onLineInitialized = true; | |
128 m_override.onLine = onLine; | |
129 m_override.connectionInitialized = true; | |
130 m_override.type = type; | |
131 m_override.maxBandwidthMbps = maxBandwidthMbps; | |
132 } | |
133 } | |
134 | |
135 void NetworkStateNotifier::clearOverride() { | |
136 DCHECK(isMainThread()); | |
137 ScopedNotifier notifier(*this); | |
138 { | |
139 MutexLocker locker(m_mutex); | |
140 m_hasOverride = false; | |
141 } | |
142 } | |
143 | |
144 void NetworkStateNotifier::notifyObservers(ObserverListMap& map, | |
145 ObserverType type, | |
146 const NetworkState& state) { | |
147 DCHECK(isMainThread()); | |
148 MutexLocker locker(m_mutex); | |
149 for (const auto& entry : map) { | |
150 RefPtr<WebTaskRunner> taskRunner = entry.key; | |
151 taskRunner->postTask( | |
152 BLINK_FROM_HERE, | |
153 crossThreadBind(&NetworkStateNotifier::notifyObserversOnTaskRunner, | |
154 crossThreadUnretained(this), | |
155 crossThreadUnretained(&map), type, taskRunner, state)); | |
156 } | |
157 } | |
158 | |
159 void NetworkStateNotifier::notifyObserversOnTaskRunner( | |
160 ObserverListMap* map, | |
161 ObserverType type, | |
162 PassRefPtr<WebTaskRunner> passTaskRunner, | |
163 const NetworkState& state) { | |
164 RefPtr<WebTaskRunner> taskRunner = passTaskRunner; | |
165 ObserverList* observerList = lockAndFindObserverList(*map, taskRunner); | |
166 | |
167 // The context could have been removed before the notification task got to | |
168 // run. | |
169 if (!observerList) | |
170 return; | |
171 | |
172 DCHECK(taskRunner->runsTasksOnCurrentThread()); | |
173 | |
174 observerList->iterating = true; | |
175 | |
176 for (size_t i = 0; i < observerList->observers.size(); ++i) { | |
177 // Observers removed during iteration are zeroed out, skip them. | |
178 if (!observerList->observers[i]) | |
179 continue; | |
180 switch (type) { | |
181 case ObserverType::ONLINE_STATE: | |
182 observerList->observers[i]->onLineStateChange(state.onLine); | |
183 continue; | |
184 case ObserverType::CONNECTION_TYPE: | |
185 observerList->observers[i]->connectionChange(state.type, | |
186 state.maxBandwidthMbps); | |
187 continue; | |
188 } | |
189 NOTREACHED(); | |
190 } | |
191 | |
192 observerList->iterating = false; | |
193 | |
194 if (!observerList->zeroedObservers.isEmpty()) | |
195 collectZeroedObservers(*map, observerList, taskRunner); | |
196 } | |
197 | |
198 void NetworkStateNotifier::addObserver(ObserverListMap& map, | |
199 NetworkStateObserver* observer, | |
200 PassRefPtr<WebTaskRunner> taskRunner) { | |
201 DCHECK(taskRunner->runsTasksOnCurrentThread()); | |
202 DCHECK(observer); | |
203 | |
204 MutexLocker locker(m_mutex); | |
205 ObserverListMap::AddResult result = | |
206 map.insert(std::move(taskRunner), nullptr); | |
207 if (result.isNewEntry) | |
208 result.storedValue->value = WTF::makeUnique<ObserverList>(); | |
209 | |
210 DCHECK(result.storedValue->value->observers.find(observer) == kNotFound); | |
211 result.storedValue->value->observers.push_back(observer); | |
212 } | |
213 | |
214 void NetworkStateNotifier::removeObserver( | |
215 ObserverListMap& map, | |
216 NetworkStateObserver* observer, | |
217 PassRefPtr<WebTaskRunner> passTaskRunner) { | |
218 RefPtr<WebTaskRunner> taskRunner = passTaskRunner; | |
219 DCHECK(taskRunner->runsTasksOnCurrentThread()); | |
220 DCHECK(observer); | |
221 | |
222 ObserverList* observerList = lockAndFindObserverList(map, taskRunner); | |
223 if (!observerList) | |
224 return; | |
225 | |
226 Vector<NetworkStateObserver*>& observers = observerList->observers; | |
227 size_t index = observers.find(observer); | |
228 if (index != kNotFound) { | |
229 observers[index] = 0; | |
230 observerList->zeroedObservers.push_back(index); | |
231 } | |
232 | |
233 if (!observerList->iterating && !observerList->zeroedObservers.isEmpty()) | |
234 collectZeroedObservers(map, observerList, taskRunner); | |
235 } | |
236 | |
237 NetworkStateNotifier::ObserverList* | |
238 NetworkStateNotifier::lockAndFindObserverList( | |
239 ObserverListMap& map, | |
240 PassRefPtr<WebTaskRunner> taskRunner) { | |
241 MutexLocker locker(m_mutex); | |
242 ObserverListMap::iterator it = map.find(taskRunner); | |
243 return it == map.end() ? nullptr : it->value.get(); | |
244 } | |
245 | |
246 void NetworkStateNotifier::collectZeroedObservers( | |
247 ObserverListMap& map, | |
248 ObserverList* list, | |
249 PassRefPtr<WebTaskRunner> taskRunner) { | |
250 DCHECK(taskRunner->runsTasksOnCurrentThread()); | |
251 DCHECK(!list->iterating); | |
252 | |
253 // If any observers were removed during the iteration they will have | |
254 // 0 values, clean them up. | |
255 for (size_t i = 0; i < list->zeroedObservers.size(); ++i) | |
256 list->observers.remove(list->zeroedObservers[i]); | |
257 | |
258 list->zeroedObservers.clear(); | |
259 | |
260 if (list->observers.isEmpty()) { | |
261 MutexLocker locker(m_mutex); | |
262 map.erase(taskRunner); // deletes list | |
263 } | |
264 } | |
265 | |
266 } // namespace blink | |
OLD | NEW |