Chromium Code Reviews| Index: Source/core/page/NetworkStateNotifier.cpp |
| diff --git a/Source/core/page/NetworkStateNotifier.cpp b/Source/core/page/NetworkStateNotifier.cpp |
| index 02cc4615c96408f4abcfc60bcea128654b09758d..1de39f5224beba2addf9055c7e6d8afbd0053ce1 100644 |
| --- a/Source/core/page/NetworkStateNotifier.cpp |
| +++ b/Source/core/page/NetworkStateNotifier.cpp |
| @@ -26,14 +26,21 @@ |
| #include "config.h" |
| #include "core/page/NetworkStateNotifier.h" |
| +#include "core/dom/ExecutionContext.h" |
| #include "core/page/Page.h" |
| #include "wtf/Assertions.h" |
| +#include "wtf/Functional.h" |
| #include "wtf/MainThread.h" |
| #include "wtf/StdLibExtras.h" |
| #include "wtf/Threading.h" |
| namespace WebCore { |
| +NetworkStateNotifier::~NetworkStateNotifier() |
| +{ |
| + deleteAllValues(m_observers); |
|
adamk
2014/05/21 09:21:31
This won't be needed if you use OwnPtrs as values.
jkarlin
2014/05/21 14:23:17
Done.
|
| +} |
| + |
| NetworkStateNotifier& networkStateNotifier() |
| { |
| AtomicallyInitializedStatic(NetworkStateNotifier*, networkStateNotifier = new NetworkStateNotifier); |
| @@ -55,4 +62,106 @@ void NetworkStateNotifier::setOnLine(bool onLine) |
| Page::networkStateChanged(onLine); |
| } |
| +void NetworkStateNotifier::setWebConnectionType(blink::WebNetworkConnection::ConnectionType type) |
| +{ |
| + ASSERT(isMainThread()); |
| + |
| + MutexLocker locker(m_mutex); |
| + if (m_type == type) |
| + return; |
| + m_type = type; |
| + |
| + for (ObserverListMap::iterator it = m_observers.begin(); it != m_observers.end(); ++it) { |
| + ExecutionContext* context = it->key; |
| + context->postTask(bind(&NetworkStateNotifier::notifyObserversOnContext, this, context, type)); |
| + } |
| +} |
| + |
| +void NetworkStateNotifier::addObserver(NetworkStateObserver* observer, ExecutionContext* context) |
| +{ |
| + ASSERT(context->isContextThread()); |
| + ASSERT(observer); |
| + |
| + MutexLocker locker(m_mutex); |
| + ObserverListMap::iterator it = m_observers.find(context); |
| + if (it == m_observers.end()) { |
| + m_observers.set(context, new ObserverList()); |
| + it = m_observers.find(context); |
|
adamk
2014/05/21 09:21:31
The idiomatic way to handle this song and dance in
jkarlin
2014/05/21 14:23:17
Done.
|
| + } |
| + |
| + it->value->observers.append(observer); |
|
adamk
2014/05/21 09:21:31
Maybe add an ASSERT that the observer isn't alread
jkarlin
2014/05/21 14:23:17
Done.
|
| +} |
| + |
| +void NetworkStateNotifier::removeObserver(NetworkStateObserver* observer, ExecutionContext* context) |
| +{ |
| + ASSERT(context->isContextThread()); |
| + ASSERT(observer); |
| + |
| + ObserverList* observerList = lockAndFindObserverList(context); |
| + if (!observerList) |
| + return; |
| + |
| + Vector<NetworkStateObserver*>& observers = observerList->observers; |
| + size_t index = observers.find(observer); |
| + if (index != kNotFound) { |
| + observers[index] = 0; |
| + observerList->zeroedObservers.append(index); |
| + } |
| + |
| + if (!observerList->iterating && !observerList->zeroedObservers.isEmpty()) |
| + collectZeroedObservers(context); |
|
adamk
2014/05/21 09:21:31
All this zeroed observers logic is pretty tricky.
jkarlin
2014/05/21 14:23:17
Done.
|
| +} |
| + |
| +void NetworkStateNotifier::notifyObserversOnContext(ExecutionContext* context, blink::WebNetworkConnection::ConnectionType type) |
| +{ |
| + ASSERT(context->isContextThread()); |
| + |
| + ObserverList* observerList = lockAndFindObserverList(context); |
| + |
| + // The context could have been removed before the notification task got to run. |
|
adamk
2014/05/21 09:21:31
If this happens then the ASSERT above could crash.
jkarlin
2014/05/21 14:23:17
Done.
|
| + if (!observerList) |
| + return; |
| + |
| + observerList->iterating = true; |
| + |
| + for (size_t i = 0; i < observerList->observers.size(); ++i) { |
| + // Observers removed during iteration are zeroed out, skip them. |
| + if (observerList->observers[i]) |
| + observerList->observers[i]->connectionTypeChange(type); |
| + } |
| + |
| + observerList->iterating = false; |
| + |
| + if (!observerList->zeroedObservers.isEmpty()) |
| + collectZeroedObservers(context); |
| +} |
| + |
| +NetworkStateNotifier::ObserverList* NetworkStateNotifier::lockAndFindObserverList(ExecutionContext* context) |
| +{ |
| + MutexLocker locker(m_mutex); |
| + ObserverListMap::iterator it = m_observers.find(context); |
| + return it == m_observers.end() ? 0 : it->value; |
| } |
| + |
| +void NetworkStateNotifier::collectZeroedObservers(ExecutionContext* context) |
|
adamk
2014/05/21 09:21:31
Please add a context thread assertion here.
jkarlin
2014/05/21 14:23:17
Done.
|
| +{ |
| + // If any observers were removed during the iteration they will have |
| + // 0 values, clean them up. |
| + ObserverList* list = lockAndFindObserverList(context); |
|
adamk
2014/05/21 09:21:31
Maybe the callers should just pass in the list?
jkarlin
2014/05/21 14:23:17
Done.
|
| + ASSERT(list); |
|
adamk
2014/05/21 09:21:31
I think you also want to ASSERT(!list->iterating)
jkarlin
2014/05/21 14:23:17
Done.
|
| + |
| + for (size_t i = 0; i < list->zeroedObservers.size(); ++i) |
| + list->observers.remove(list->zeroedObservers[i]); |
| + |
| + list->zeroedObservers.clear(); |
| + |
| + { |
| + MutexLocker locker(m_mutex); |
| + if (list->observers.isEmpty()) { |
| + m_observers.remove(context); |
| + delete list; |
|
adamk
2014/05/21 09:21:31
This delete can also go away if you use OwnPtrs as
jkarlin
2014/05/21 14:23:17
Done.
|
| + } |
| + } |
| +} |
| + |
| +} // namespace WebCore |