| Index: Source/core/fetch/Resource.cpp
|
| diff --git a/Source/core/fetch/Resource.cpp b/Source/core/fetch/Resource.cpp
|
| index 936193b9f27e34bf19c43464216663076a2e5162..78945ed00200717585fa7e8f470ed9e9fe6994b4 100644
|
| --- a/Source/core/fetch/Resource.cpp
|
| +++ b/Source/core/fetch/Resource.cpp
|
| @@ -478,7 +478,7 @@ void Resource::removeClient(ResourceClient* client)
|
| if (m_clientsAwaitingCallback.isEmpty())
|
| ResourceCallback::callbackHandler()->cancel(this);
|
| } else {
|
| - ASSERT(m_clients.contains(client));
|
| + RELEASE_ASSERT(m_clients.contains(client));
|
| m_clients.remove(client);
|
| didRemoveClient(client);
|
| }
|
| @@ -563,12 +563,35 @@ void Resource::didAccessDecodedData()
|
|
|
| void Resource::finishPendingClients()
|
| {
|
| - while (!m_clientsAwaitingCallback.isEmpty()) {
|
| - ResourceClient* client = m_clientsAwaitingCallback.begin()->key;
|
| - m_clientsAwaitingCallback.remove(client);
|
| + // We're going to notify clients one by one. It is simple if the client does nothing.
|
| + // However there are a couple other things that can happen.
|
| + //
|
| + // 1. Clients can be added during the loop. Make sure they are not processed.
|
| + // 2. Clients can be removed during the loop. Make sure they are always available to be
|
| + // removed. Also don't call removed clients or add them back.
|
| +
|
| + // Handle case (1) by saving a list of clients to notify. A separate list also ensure
|
| + // a client is either in m_clients or m_clientsAwaitingCallback.
|
| + Vector<ResourceClient*> clientsToNotify;
|
| + copyToVector(m_clientsAwaitingCallback, clientsToNotify);
|
| +
|
| + for (size_t i = 0; i < clientsToNotify.size(); ++i) {
|
| + ResourceClient* client = clientsToNotify[i];
|
| +
|
| + // Handle case (2) to skip removed clients.
|
| + if (!m_clientsAwaitingCallback.remove(client))
|
| + continue;
|
| m_clients.add(client);
|
| didAddClient(client);
|
| }
|
| +
|
| + bool scheduled = ResourceCallback::callbackHandler()->isScheduled(this);
|
| + // It is a critical problem if a callback is scheduled but there is no client waiting for it.
|
| + // Such a callback cannot be cancelled. It is better to crash the renderer now.
|
| + RELEASE_ASSERT(!scheduled || !m_clientsAwaitingCallback.isEmpty());
|
| +
|
| + // Prevent the case when there are clients waiting but no callback scheduled.
|
| + ASSERT(m_clientsAwaitingCallback.isEmpty() || scheduled);
|
| }
|
|
|
| void Resource::prune()
|
|
|