diff options
Diffstat (limited to 'Source/WebCore/loader/ResourceLoadScheduler.cpp')
-rw-r--r-- | Source/WebCore/loader/ResourceLoadScheduler.cpp | 378 |
1 files changed, 0 insertions, 378 deletions
diff --git a/Source/WebCore/loader/ResourceLoadScheduler.cpp b/Source/WebCore/loader/ResourceLoadScheduler.cpp deleted file mode 100644 index e8fee1d3f..000000000 --- a/Source/WebCore/loader/ResourceLoadScheduler.cpp +++ /dev/null @@ -1,378 +0,0 @@ -/* - Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) - Copyright (C) 2001 Dirk Mueller (mueller@kde.org) - Copyright (C) 2002 Waldo Bastian (bastian@kde.org) - Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. - Copyright (C) 2010 Google Inc. All rights reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. - */ - -#include "config.h" -#include "ResourceLoadScheduler.h" - -#include "Document.h" -#include "DocumentLoader.h" -#include "Frame.h" -#include "FrameLoader.h" -#include "InspectorInstrumentation.h" -#include "URL.h" -#include "LoaderStrategy.h" -#include "Logging.h" -#include "NetscapePlugInStreamLoader.h" -#include "PlatformStrategies.h" -#include "ResourceLoader.h" -#include "ResourceRequest.h" -#include "SubresourceLoader.h" -#include <wtf/MainThread.h> -#include <wtf/TemporaryChange.h> -#include <wtf/text/CString.h> - -#if PLATFORM(IOS) -#include <RuntimeApplicationChecksIOS.h> -#endif - -namespace WebCore { - -// Match the parallel connection count used by the networking layer. -static unsigned maxRequestsInFlightPerHost; -#if !PLATFORM(IOS) -static const unsigned maxRequestsInFlightForNonHTTPProtocols = 20; -#else -// Limiting this seems to regress performance in some local cases so let's just make it large. -static const unsigned maxRequestsInFlightForNonHTTPProtocols = 10000; -#endif - -ResourceLoadScheduler::HostInformation* ResourceLoadScheduler::hostForURL(const URL& url, CreateHostPolicy createHostPolicy) -{ - if (!url.protocolIsInHTTPFamily()) - return m_nonHTTPProtocolHost; - - m_hosts.checkConsistency(); - String hostName = url.host(); - HostInformation* host = m_hosts.get(hostName); - if (!host && createHostPolicy == CreateIfNotFound) { - host = new HostInformation(hostName, maxRequestsInFlightPerHost); - m_hosts.add(hostName, host); - } - return host; -} - -ResourceLoadScheduler* resourceLoadScheduler() -{ - ASSERT(isMainThread()); - static ResourceLoadScheduler* globalScheduler = 0; - - if (!globalScheduler) { - static bool isCallingOutToStrategy = false; - - // If we're re-entering resourceLoadScheduler() while calling out to the LoaderStrategy, - // then the LoaderStrategy is trying to use the default resourceLoadScheduler. - // So we'll create it here and start using it. - if (isCallingOutToStrategy) { - globalScheduler = new ResourceLoadScheduler; - return globalScheduler; - } - - TemporaryChange<bool> recursionGuard(isCallingOutToStrategy, true); - globalScheduler = platformStrategies()->loaderStrategy()->resourceLoadScheduler(); - } - - return globalScheduler; -} - -ResourceLoadScheduler::ResourceLoadScheduler() - : m_nonHTTPProtocolHost(new HostInformation(String(), maxRequestsInFlightForNonHTTPProtocols)) - , m_requestTimer(this, &ResourceLoadScheduler::requestTimerFired) - , m_suspendPendingRequestsCount(0) - , m_isSerialLoadingEnabled(false) -{ - maxRequestsInFlightPerHost = initializeMaximumHTTPConnectionCountPerHost(); -} - -ResourceLoadScheduler::~ResourceLoadScheduler() -{ -} - -PassRefPtr<SubresourceLoader> ResourceLoadScheduler::scheduleSubresourceLoad(Frame* frame, CachedResource* resource, const ResourceRequest& request, ResourceLoadPriority priority, const ResourceLoaderOptions& options) -{ - RefPtr<SubresourceLoader> loader = SubresourceLoader::create(frame, resource, request, options); - if (loader) - scheduleLoad(loader.get(), priority); -#if PLATFORM(IOS) - // Since we defer loader initialization until scheduling on iOS, the frame - // load delegate that would be called in SubresourceLoader::create() on - // other ports might be called in scheduleLoad() instead. Our contract to - // callers of this method is that a null loader is returned if the load was - // cancelled by a frame load delegate. - if (!loader || loader->reachedTerminalState()) - return nullptr; -#endif - return loader.release(); -} - -PassRefPtr<NetscapePlugInStreamLoader> ResourceLoadScheduler::schedulePluginStreamLoad(Frame* frame, NetscapePlugInStreamLoaderClient* client, const ResourceRequest& request) -{ - PassRefPtr<NetscapePlugInStreamLoader> loader = NetscapePlugInStreamLoader::create(frame, client, request); - if (loader) - scheduleLoad(loader.get(), ResourceLoadPriorityLow); - return loader; -} - -void ResourceLoadScheduler::scheduleLoad(ResourceLoader* resourceLoader, ResourceLoadPriority priority) -{ - ASSERT(resourceLoader); - ASSERT(priority != ResourceLoadPriorityUnresolved); - - LOG(ResourceLoading, "ResourceLoadScheduler::load resource %p '%s'", resourceLoader, resourceLoader->url().string().latin1().data()); - -#if PLATFORM(IOS) - // If there's a web archive resource for this URL, we don't need to schedule the load since it will never touch the network. - if (!isSuspendingPendingRequests() && resourceLoader->documentLoader()->archiveResourceForURL(resourceLoader->iOSOriginalRequest().url())) { - resourceLoader->startLoading(); - return; - } -#else - if (resourceLoader->documentLoader()->archiveResourceForURL(resourceLoader->request().url())) { - resourceLoader->start(); - return; - } -#endif - -#if PLATFORM(IOS) - HostInformation* host = hostForURL(resourceLoader->iOSOriginalRequest().url(), CreateIfNotFound); -#else - HostInformation* host = hostForURL(resourceLoader->url(), CreateIfNotFound); -#endif - - bool hadRequests = host->hasRequests(); - host->schedule(resourceLoader, priority); - -#if PLATFORM(MAC) || USE(CFNETWORK) - if (!isSuspendingPendingRequests()) { - // Serve all requests at once to keep the pipeline full at the network layer. - // FIXME: Does this code do anything useful, given that we also set maxRequestsInFlightPerHost to effectively unlimited on these platforms? - servePendingRequests(host, ResourceLoadPriorityVeryLow); - return; - } -#endif - -#if PLATFORM(IOS) - if ((priority > ResourceLoadPriorityLow || !resourceLoader->iOSOriginalRequest().url().protocolIsInHTTPFamily() || (priority == ResourceLoadPriorityLow && !hadRequests)) && !isSuspendingPendingRequests()) { - // Try to request important resources immediately. - servePendingRequests(host, priority); - return; - } -#else - if (priority > ResourceLoadPriorityLow || !resourceLoader->url().protocolIsInHTTPFamily() || (priority == ResourceLoadPriorityLow && !hadRequests)) { - // Try to request important resources immediately. - servePendingRequests(host, priority); - return; - } -#endif - - notifyDidScheduleResourceRequest(resourceLoader); - - // Handle asynchronously so early low priority requests don't - // get scheduled before later high priority ones. - scheduleServePendingRequests(); -} - -void ResourceLoadScheduler::notifyDidScheduleResourceRequest(ResourceLoader* loader) -{ - InspectorInstrumentation::didScheduleResourceRequest(loader->frameLoader() ? loader->frameLoader()->frame().document() : 0, loader->url()); -} - -void ResourceLoadScheduler::remove(ResourceLoader* resourceLoader) -{ - ASSERT(resourceLoader); - - HostInformation* host = hostForURL(resourceLoader->url()); - if (host) - host->remove(resourceLoader); -#if PLATFORM(IOS) - // ResourceLoader::url() doesn't start returning the correct value until the load starts. If we get canceled before that, we need to look for originalRequest url instead. - // FIXME: ResourceLoader::url() should be made to return a sensible value at all times. - if (!resourceLoader->iOSOriginalRequest().isNull()) { - HostInformation* originalHost = hostForURL(resourceLoader->iOSOriginalRequest().url()); - if (originalHost && originalHost != host) - originalHost->remove(resourceLoader); - } -#endif - scheduleServePendingRequests(); -} - -void ResourceLoadScheduler::crossOriginRedirectReceived(ResourceLoader* resourceLoader, const URL& redirectURL) -{ - HostInformation* oldHost = hostForURL(resourceLoader->url()); - ASSERT(oldHost); - if (!oldHost) - return; - - HostInformation* newHost = hostForURL(redirectURL, CreateIfNotFound); - - if (oldHost->name() == newHost->name()) - return; - - newHost->addLoadInProgress(resourceLoader); - oldHost->remove(resourceLoader); -} - -void ResourceLoadScheduler::servePendingRequests(ResourceLoadPriority minimumPriority) -{ - LOG(ResourceLoading, "ResourceLoadScheduler::servePendingRequests. m_suspendPendingRequestsCount=%d", m_suspendPendingRequestsCount); - if (isSuspendingPendingRequests()) - return; - - m_requestTimer.stop(); - - servePendingRequests(m_nonHTTPProtocolHost, minimumPriority); - - Vector<HostInformation*> hostsToServe; - m_hosts.checkConsistency(); - HostMap::iterator end = m_hosts.end(); - for (HostMap::iterator iter = m_hosts.begin(); iter != end; ++iter) - hostsToServe.append(iter->value); - - int size = hostsToServe.size(); - for (int i = 0; i < size; ++i) { - HostInformation* host = hostsToServe[i]; - if (host->hasRequests()) - servePendingRequests(host, minimumPriority); - else - delete m_hosts.take(host->name()); - } -} - -void ResourceLoadScheduler::servePendingRequests(HostInformation* host, ResourceLoadPriority minimumPriority) -{ - LOG(ResourceLoading, "ResourceLoadScheduler::servePendingRequests HostInformation.m_name='%s'", host->name().latin1().data()); - - for (int priority = ResourceLoadPriorityHighest; priority >= minimumPriority; --priority) { - HostInformation::RequestQueue& requestsPending = host->requestsPending(ResourceLoadPriority(priority)); - - while (!requestsPending.isEmpty()) { - RefPtr<ResourceLoader> resourceLoader = requestsPending.first(); - - // 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. - Document* document = resourceLoader->frameLoader() ? resourceLoader->frameLoader()->frame().document() : 0; - bool shouldLimitRequests = !host->name().isNull() || (document && (document->parsing() || !document->haveStylesheetsLoaded())); - if (shouldLimitRequests && host->limitRequests(ResourceLoadPriority(priority))) - return; - - requestsPending.removeFirst(); - host->addLoadInProgress(resourceLoader.get()); -#if PLATFORM(IOS) - if (!applicationIsWebProcess()) { - resourceLoader->startLoading(); - return; - } -#endif - resourceLoader->start(); - } - } -} - -void ResourceLoadScheduler::suspendPendingRequests() -{ - ++m_suspendPendingRequestsCount; -} - -void ResourceLoadScheduler::resumePendingRequests() -{ - ASSERT(m_suspendPendingRequestsCount); - --m_suspendPendingRequestsCount; - if (m_suspendPendingRequestsCount) - return; - if (!m_hosts.isEmpty() || m_nonHTTPProtocolHost->hasRequests()) - scheduleServePendingRequests(); -} - -void ResourceLoadScheduler::scheduleServePendingRequests() -{ - LOG(ResourceLoading, "ResourceLoadScheduler::scheduleServePendingRequests, m_requestTimer.isActive()=%u", m_requestTimer.isActive()); - if (!m_requestTimer.isActive()) - m_requestTimer.startOneShot(0); -} - -void ResourceLoadScheduler::requestTimerFired(Timer<ResourceLoadScheduler>&) -{ - LOG(ResourceLoading, "ResourceLoadScheduler::requestTimerFired\n"); - servePendingRequests(); -} - -ResourceLoadScheduler::HostInformation::HostInformation(const String& name, unsigned maxRequestsInFlight) - : m_name(name) - , m_maxRequestsInFlight(maxRequestsInFlight) -{ -} - -ResourceLoadScheduler::HostInformation::~HostInformation() -{ - ASSERT(m_requestsLoading.isEmpty()); - for (unsigned p = 0; p <= ResourceLoadPriorityHighest; p++) - ASSERT(m_requestsPending[p].isEmpty()); -} - -void ResourceLoadScheduler::HostInformation::schedule(ResourceLoader* resourceLoader, ResourceLoadPriority priority) -{ - m_requestsPending[priority].append(resourceLoader); -} - -void ResourceLoadScheduler::HostInformation::addLoadInProgress(ResourceLoader* resourceLoader) -{ - LOG(ResourceLoading, "HostInformation '%s' loading '%s'. Current count %d", m_name.latin1().data(), resourceLoader->url().string().latin1().data(), m_requestsLoading.size()); - m_requestsLoading.add(resourceLoader); -} - -void ResourceLoadScheduler::HostInformation::remove(ResourceLoader* resourceLoader) -{ - if (m_requestsLoading.remove(resourceLoader)) - return; - - for (int priority = ResourceLoadPriorityHighest; priority >= ResourceLoadPriorityLowest; --priority) { - RequestQueue::iterator end = m_requestsPending[priority].end(); - for (RequestQueue::iterator it = m_requestsPending[priority].begin(); it != end; ++it) { - if (*it == resourceLoader) { - m_requestsPending[priority].remove(it); - return; - } - } - } -} - -bool ResourceLoadScheduler::HostInformation::hasRequests() const -{ - if (!m_requestsLoading.isEmpty()) - return true; - for (unsigned p = 0; p <= ResourceLoadPriorityHighest; p++) { - if (!m_requestsPending[p].isEmpty()) - return true; - } - return false; -} - -bool ResourceLoadScheduler::HostInformation::limitRequests(ResourceLoadPriority priority) const -{ - if (priority == ResourceLoadPriorityVeryLow && !m_requestsLoading.isEmpty()) - return true; - return m_requestsLoading.size() >= (resourceLoadScheduler()->isSerialLoadingEnabled() ? 1 : m_maxRequestsInFlight); -} - -} // namespace WebCore |