#include "config.h" #include "NetworkResourceLoadScheduler.h" #include "HostRecord.h" #include "Logging.h" #include "NetworkConnectionToWebProcess.h" #include "NetworkProcessconnectionMessages.h" #include "NetworkResourceLoadParameters.h" #include "NetworkResourceLoader.h" #include #include #if ENABLE(NETWORK_PROCESS) using namespace WebCore; namespace WebKit { static const unsigned maxRequestsInFlightForNonHTTPProtocols = 20; static unsigned maxRequestsInFlightPerHost; static ResourceLoadIdentifier s_currentResourceLoadIdentifier; NetworkResourceLoadScheduler::NetworkResourceLoadScheduler() : m_nonHTTPProtocolHost(new HostRecord(String(), maxRequestsInFlightForNonHTTPProtocols)) , m_requestTimer(this, &NetworkResourceLoadScheduler::requestTimerFired) { maxRequestsInFlightPerHost = platformInitializeMaximumHTTPConnectionCountPerHost(); } void NetworkResourceLoadScheduler::scheduleServePendingRequests() { if (!m_requestTimer.isActive()) m_requestTimer.startOneShot(0); } void NetworkResourceLoadScheduler::requestTimerFired(WebCore::Timer*) { servePendingRequests(); } ResourceLoadIdentifier NetworkResourceLoadScheduler::scheduleResourceLoad(const NetworkResourceLoadParameters& loadParameters, NetworkConnectionToWebProcess* connection) { ResourceLoadPriority priority = loadParameters.priority(); const ResourceRequest& resourceRequest = loadParameters.request(); ResourceLoadIdentifier identifier = ++s_currentResourceLoadIdentifier; RefPtr loader = NetworkResourceLoader::create(loadParameters, identifier, connection); m_resourceLoaders.add(identifier, loader); LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::scheduleNetworkResourceRequest resource %llu '%s'", identifier, resourceRequest.url().string().utf8().data()); HostRecord* host = hostForURL(resourceRequest.url(), CreateIfNotFound); bool hadRequests = host->hasRequests(); host->schedule(loader); m_identifiers.add(identifier, host); if (priority > ResourceLoadPriorityLow || !resourceRequest.url().protocolIsInHTTPFamily() || (priority == ResourceLoadPriorityLow && !hadRequests)) { // Try to request important resources immediately. servePendingRequestsForHost(host, priority); return identifier; } // Handle asynchronously so early low priority requests don't get scheduled before later high priority ones. scheduleServePendingRequests(); return identifier; } ResourceLoadIdentifier NetworkResourceLoadScheduler::addLoadInProgress(const WebCore::KURL& url) { ResourceLoadIdentifier identifier = ++s_currentResourceLoadIdentifier; LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::addLoadInProgress resource %llu with url '%s'", identifier, url.string().utf8().data()); HostRecord* host = hostForURL(url, CreateIfNotFound); host->addLoadInProgress(identifier); m_identifiers.add(identifier, host); return identifier; } HostRecord* NetworkResourceLoadScheduler::hostForURL(const WebCore::KURL& url, CreateHostPolicy createHostPolicy) { if (!url.protocolIsInHTTPFamily()) return m_nonHTTPProtocolHost; m_hosts.checkConsistency(); String hostName = url.host(); HostRecord* host = m_hosts.get(hostName); if (!host && createHostPolicy == CreateIfNotFound) { host = new HostRecord(hostName, maxRequestsInFlightPerHost); m_hosts.add(hostName, host); } return host; } void NetworkResourceLoadScheduler::removeLoadIdentifier(ResourceLoadIdentifier identifier) { ASSERT(isMainThread()); ASSERT(identifier); LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::removeLoadIdentifier removing load identifier %llu", identifier); HostRecord* host = m_identifiers.take(identifier); // Due to a race condition the WebProcess might have messaged the NetworkProcess to remove this identifier // after the NetworkProcess has already removed it internally. // In this situation we might not have a HostRecord to clean up. if (host) host->remove(identifier); m_resourceLoaders.remove(identifier); scheduleServePendingRequests(); } NetworkResourceLoader* NetworkResourceLoadScheduler::networkResourceLoaderForIdentifier(ResourceLoadIdentifier identifier) { ASSERT(m_resourceLoaders.get(identifier)); return m_resourceLoaders.get(identifier).get(); } void NetworkResourceLoadScheduler::receivedRedirect(ResourceLoadIdentifier identifier, const WebCore::KURL& redirectURL) { ASSERT(isMainThread()); LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::receivedRedirect resource %llu redirected to '%s'", identifier, redirectURL.string().utf8().data()); HostRecord* oldHost = m_identifiers.get(identifier); HostRecord* newHost = hostForURL(redirectURL, CreateIfNotFound); ASSERT(oldHost); if (oldHost->name() == newHost->name()) return; newHost->addLoadInProgress(identifier); m_identifiers.set(identifier, newHost); oldHost->remove(identifier); } void NetworkResourceLoadScheduler::servePendingRequests(ResourceLoadPriority minimumPriority) { if (m_suspendPendingRequestsCount) return; LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::servePendingRequests Serving requests for up to %i hosts", m_hosts.size()); m_requestTimer.stop(); servePendingRequestsForHost(m_nonHTTPProtocolHost, minimumPriority); m_hosts.checkConsistency(); Vector hostsToServe; copyValuesToVector(m_hosts, hostsToServe); size_t size = hostsToServe.size(); for (size_t i = 0; i < size; ++i) { HostRecord* host = hostsToServe[i]; if (host->hasRequests()) servePendingRequestsForHost(host, minimumPriority); else delete m_hosts.take(host->name()); } } void NetworkResourceLoadScheduler::servePendingRequestsForHost(HostRecord* host, ResourceLoadPriority minimumPriority) { LOG(NetworkScheduling, "NetworkResourceLoadScheduler::servePendingRequests Host name='%s'", host->name().utf8().data()); for (int priority = ResourceLoadPriorityHighest; priority >= minimumPriority; --priority) { HostRecord::LoaderQueue& loadersPending = host->loadersPending(ResourceLoadPriority(priority)); while (!loadersPending.isEmpty()) { RefPtr loader = loadersPending.first(); // This request might be from WebProcess we've lost our connection to. // If so we should just skip it. if (!loader->connectionToWebProcess()) { loadersPending.removeFirst(); continue; } // For named hosts - which are only http(s) hosts - we should always enforce the connection limit. // For non-named hosts - everything but http(s) - we should only enforce the limit if the document // isn't done parsing and we don't know all stylesheets yet. // FIXME (NetworkProcess): The above comment about document parsing and stylesheets is a holdover // from the WebCore::ResourceLoadScheduler. // The behavior described was at one time important for WebCore's single threadedness. // It's possible that we don't care about it with the NetworkProcess. // We should either decide it's not important and change the above comment, or decide it is // still important and somehow account for it. bool shouldLimitRequests = !host->name().isNull(); if (shouldLimitRequests && host->limitRequests(ResourceLoadPriority(priority), loader->connectionToWebProcess()->isSerialLoadingEnabled())) return; loadersPending.removeFirst(); host->addLoadInProgress(loader->identifier()); loader->start(); } } } void NetworkResourceLoadScheduler::suspendPendingRequests() { ++m_suspendPendingRequestsCount; } void NetworkResourceLoadScheduler::resumePendingRequests() { ASSERT(m_suspendPendingRequestsCount); --m_suspendPendingRequestsCount; if (m_suspendPendingRequestsCount) return; if (!m_hosts.isEmpty() || m_nonHTTPProtocolHost->hasRequests()) scheduleServePendingRequests(); } static bool removeScheduledLoadIdentifiersCalled = false; void NetworkResourceLoadScheduler::removeScheduledLoadIdentifiers(void* context) { ASSERT(isMainThread()); ASSERT(removeScheduledLoadIdentifiersCalled); NetworkResourceLoadScheduler* scheduler = static_cast(context); scheduler->removeScheduledLoadIdentifiers(); } void NetworkResourceLoadScheduler::removeScheduledLoadIdentifiers() { Vector identifiers; { MutexLocker locker(m_identifiersToRemoveMutex); copyToVector(m_identifiersToRemove, identifiers); m_identifiersToRemove.clear(); removeScheduledLoadIdentifiersCalled = false; } for (size_t i = 0; i < identifiers.size(); ++i) removeLoadIdentifier(identifiers[i]); } void NetworkResourceLoadScheduler::scheduleRemoveLoadIdentifier(ResourceLoadIdentifier identifier) { MutexLocker locker(m_identifiersToRemoveMutex); m_identifiersToRemove.add(identifier); if (!removeScheduledLoadIdentifiersCalled) { removeScheduledLoadIdentifiersCalled = true; callOnMainThread(NetworkResourceLoadScheduler::removeScheduledLoadIdentifiers, this); } } } // namespace WebKit #endif // ENABLE(NETWORK_PROCESS)