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