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
networkStateNotifier()39 NetworkStateNotifier& networkStateNotifier()
40 {
41 AtomicallyInitializedStatic(NetworkStateNotifier*, networkStateNotifier = new NetworkStateNotifier);
42 return *networkStateNotifier;
43 }
44
setOnLine(bool onLine)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
setWebConnectionType(blink::WebConnectionType type)60 void NetworkStateNotifier::setWebConnectionType(blink::WebConnectionType type)
61 {
62 ASSERT(isMainThread());
63 if (m_testUpdatesOnly)
64 return;
65
66 setWebConnectionTypeImpl(type);
67 }
68
setWebConnectionTypeImpl(blink::WebConnectionType type)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
addObserver(NetworkStateObserver * observer,ExecutionContext * context)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
removeObserver(NetworkStateObserver * observer,ExecutionContext * context)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
setTestUpdatesOnly(bool updatesOnly)118 void NetworkStateNotifier::setTestUpdatesOnly(bool updatesOnly)
119 {
120 ASSERT(isMainThread());
121 m_testUpdatesOnly = updatesOnly;
122 }
123
setWebConnectionTypeForTest(blink::WebConnectionType type)124 void NetworkStateNotifier::setWebConnectionTypeForTest(blink::WebConnectionType type)
125 {
126 ASSERT(isMainThread());
127 ASSERT(m_testUpdatesOnly);
128 setWebConnectionTypeImpl(type);
129 }
130
notifyObserversOnContext(ExecutionContext * context,blink::WebConnectionType type)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
lockAndFindObserverList(ExecutionContext * context)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
collectZeroedObservers(ObserverList * list,ExecutionContext * context)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