| 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 |