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 "config.h" 27#include "core/page/NetworkStateNotifier.h" 28 29#include "core/dom/CrossThreadTask.h" 30#include "core/dom/ExecutionContext.h" 31#include "core/page/Page.h" 32#include "wtf/Assertions.h" 33#include "wtf/Functional.h" 34#include "wtf/MainThread.h" 35#include "wtf/StdLibExtras.h" 36#include "wtf/Threading.h" 37 38namespace blink { 39 40NetworkStateNotifier& networkStateNotifier() 41{ 42 AtomicallyInitializedStatic(NetworkStateNotifier*, networkStateNotifier = new NetworkStateNotifier); 43 return *networkStateNotifier; 44} 45 46void NetworkStateNotifier::setOnLine(bool onLine) 47{ 48 ASSERT(isMainThread()); 49 50 { 51 MutexLocker locker(m_mutex); 52 if (m_isOnLine == onLine) 53 return; 54 55 m_isOnLine = onLine; 56 } 57 58 Page::networkStateChanged(onLine); 59} 60 61void NetworkStateNotifier::setWebConnectionType(blink::WebConnectionType type) 62{ 63 ASSERT(isMainThread()); 64 if (m_testUpdatesOnly) 65 return; 66 67 setWebConnectionTypeImpl(type); 68} 69 70void NetworkStateNotifier::setWebConnectionTypeImpl(blink::WebConnectionType type) 71{ 72 ASSERT(isMainThread()); 73 74 MutexLocker locker(m_mutex); 75 if (m_type == type) 76 return; 77 m_type = type; 78 79 for (ObserverListMap::iterator it = m_observers.begin(); it != m_observers.end(); ++it) { 80 ExecutionContext* context = it->key; 81 context->postTask(createCrossThreadTask(&NetworkStateNotifier::notifyObserversOnContext, this, AllowCrossThreadAccess(context), type)); 82 } 83} 84 85void NetworkStateNotifier::addObserver(NetworkStateObserver* observer, ExecutionContext* context) 86{ 87 ASSERT(context->isContextThread()); 88 ASSERT(observer); 89 90 MutexLocker locker(m_mutex); 91 ObserverListMap::AddResult result = m_observers.add(context, nullptr); 92 if (result.isNewEntry) 93 result.storedValue->value = adoptPtr(new ObserverList); 94 95 ASSERT(result.storedValue->value->observers.find(observer) == kNotFound); 96 result.storedValue->value->observers.append(observer); 97} 98 99void NetworkStateNotifier::removeObserver(NetworkStateObserver* observer, ExecutionContext* context) 100{ 101 ASSERT(context->isContextThread()); 102 ASSERT(observer); 103 104 ObserverList* observerList = lockAndFindObserverList(context); 105 if (!observerList) 106 return; 107 108 Vector<NetworkStateObserver*>& observers = observerList->observers; 109 size_t index = observers.find(observer); 110 if (index != kNotFound) { 111 observers[index] = 0; 112 observerList->zeroedObservers.append(index); 113 } 114 115 if (!observerList->iterating && !observerList->zeroedObservers.isEmpty()) 116 collectZeroedObservers(observerList, context); 117} 118 119void NetworkStateNotifier::setTestUpdatesOnly(bool updatesOnly) 120{ 121 ASSERT(isMainThread()); 122 m_testUpdatesOnly = updatesOnly; 123} 124 125void NetworkStateNotifier::setWebConnectionTypeForTest(blink::WebConnectionType type) 126{ 127 ASSERT(isMainThread()); 128 ASSERT(m_testUpdatesOnly); 129 setWebConnectionTypeImpl(type); 130} 131 132void NetworkStateNotifier::notifyObserversOnContext(ExecutionContext* context, blink::WebConnectionType type) 133{ 134 ObserverList* observerList = lockAndFindObserverList(context); 135 136 // The context could have been removed before the notification task got to run. 137 if (!observerList) 138 return; 139 140 ASSERT(context->isContextThread()); 141 142 observerList->iterating = true; 143 144 for (size_t i = 0; i < observerList->observers.size(); ++i) { 145 // Observers removed during iteration are zeroed out, skip them. 146 if (observerList->observers[i]) 147 observerList->observers[i]->connectionTypeChange(type); 148 } 149 150 observerList->iterating = false; 151 152 if (!observerList->zeroedObservers.isEmpty()) 153 collectZeroedObservers(observerList, context); 154} 155 156NetworkStateNotifier::ObserverList* NetworkStateNotifier::lockAndFindObserverList(ExecutionContext* context) 157{ 158 MutexLocker locker(m_mutex); 159 ObserverListMap::iterator it = m_observers.find(context); 160 return it == m_observers.end() ? 0 : it->value.get(); 161} 162 163void NetworkStateNotifier::collectZeroedObservers(ObserverList* list, ExecutionContext* context) 164{ 165 ASSERT(context->isContextThread()); 166 ASSERT(!list->iterating); 167 168 // If any observers were removed during the iteration they will have 169 // 0 values, clean them up. 170 for (size_t i = 0; i < list->zeroedObservers.size(); ++i) 171 list->observers.remove(list->zeroedObservers[i]); 172 173 list->zeroedObservers.clear(); 174 175 if (list->observers.isEmpty()) { 176 MutexLocker locker(m_mutex); 177 m_observers.remove(context); // deletes list 178 } 179} 180 181} // namespace blink 182