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