summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/loader/ResourceLoadScheduler.cpp
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/loader/ResourceLoadScheduler.cpp
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_webkit-cad810f21b803229eb11403f9209855525a25d57.zip
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/loader/ResourceLoadScheduler.cpp')
-rw-r--r--Source/WebCore/loader/ResourceLoadScheduler.cpp289
1 files changed, 289 insertions, 0 deletions
diff --git a/Source/WebCore/loader/ResourceLoadScheduler.cpp b/Source/WebCore/loader/ResourceLoadScheduler.cpp
new file mode 100644
index 0000000..8cf2c18
--- /dev/null
+++ b/Source/WebCore/loader/ResourceLoadScheduler.cpp
@@ -0,0 +1,289 @@
+/*
+ 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 "Frame.h"
+#include "FrameLoader.h"
+#include "InspectorInstrumentation.h"
+#include "KURL.h"
+#include "Logging.h"
+#include "NetscapePlugInStreamLoader.h"
+#include "ResourceLoader.h"
+#include "ResourceRequest.h"
+#include "SubresourceLoader.h"
+#include <wtf/text/CString.h>
+
+#define REQUEST_MANAGEMENT_ENABLED 1
+
+namespace WebCore {
+
+#if REQUEST_MANAGEMENT_ENABLED
+static const unsigned maxRequestsInFlightForNonHTTPProtocols = 20;
+// Match the parallel connection count used by the networking layer.
+static unsigned maxRequestsInFlightPerHost;
+#else
+static const unsigned maxRequestsInFlightForNonHTTPProtocols = 10000;
+static const unsigned maxRequestsInFlightPerHost = 10000;
+#endif
+
+ResourceLoadScheduler::HostInformation* ResourceLoadScheduler::hostForURL(const KURL& url, CreateHostPolicy createHostPolicy)
+{
+ if (!url.protocolInHTTPFamily())
+ 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());
+ DEFINE_STATIC_LOCAL(ResourceLoadScheduler, resourceLoadScheduler, ());
+ return &resourceLoadScheduler;
+}
+
+ResourceLoadScheduler::ResourceLoadScheduler()
+ : m_nonHTTPProtocolHost(new HostInformation(String(), maxRequestsInFlightForNonHTTPProtocols))
+ , m_requestTimer(this, &ResourceLoadScheduler::requestTimerFired)
+ , m_isSuspendingPendingRequests(false)
+ , m_isSerialLoadingEnabled(false)
+{
+#if REQUEST_MANAGEMENT_ENABLED
+ maxRequestsInFlightPerHost = initializeMaximumHTTPConnectionCountPerHost();
+#endif
+}
+
+PassRefPtr<SubresourceLoader> ResourceLoadScheduler::scheduleSubresourceLoad(Frame* frame, SubresourceLoaderClient* client, const ResourceRequest& request, ResourceLoadPriority priority, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks, bool shouldContentSniff)
+{
+ PassRefPtr<SubresourceLoader> loader = SubresourceLoader::create(frame, client, request, securityCheck, sendResourceLoadCallbacks, shouldContentSniff);
+ if (loader)
+ scheduleLoad(loader.get(), priority);
+ return loader;
+}
+
+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::addMainResourceLoad(ResourceLoader* resourceLoader)
+{
+ hostForURL(resourceLoader->url(), CreateIfNotFound)->addLoadInProgress(resourceLoader);
+}
+
+void ResourceLoadScheduler::scheduleLoad(ResourceLoader* resourceLoader, ResourceLoadPriority priority)
+{
+ ASSERT(resourceLoader);
+ ASSERT(priority != ResourceLoadPriorityUnresolved);
+#if !REQUEST_MANAGEMENT_ENABLED
+ priority = ResourceLoadPriorityHighest;
+#endif
+
+ LOG(ResourceLoading, "ResourceLoadScheduler::load resource %p '%s'", resourceLoader, resourceLoader->url().string().latin1().data());
+ HostInformation* host = hostForURL(resourceLoader->url(), CreateIfNotFound);
+ bool hadRequests = host->hasRequests();
+ host->schedule(resourceLoader, priority);
+
+ if (priority > ResourceLoadPriorityLow || !resourceLoader->url().protocolInHTTPFamily() || (priority == ResourceLoadPriorityLow && !hadRequests)) {
+ // Try to request important resources immediately.
+ servePendingRequests(host, priority);
+ } else {
+ // Handle asynchronously so early low priority requests don't get scheduled before later high priority ones.
+ InspectorInstrumentation::didScheduleResourceRequest(resourceLoader->frameLoader() ? resourceLoader->frameLoader()->frame()->document() : 0, resourceLoader->url());
+ scheduleServePendingRequests();
+ }
+}
+
+void ResourceLoadScheduler::remove(ResourceLoader* resourceLoader)
+{
+ ASSERT(resourceLoader);
+
+ HostInformation* host = hostForURL(resourceLoader->url());
+ if (host)
+ host->remove(resourceLoader);
+ scheduleServePendingRequests();
+}
+
+void ResourceLoadScheduler::crossOriginRedirectReceived(ResourceLoader* resourceLoader, const KURL& redirectURL)
+{
+ HostInformation* oldHost = hostForURL(resourceLoader->url());
+ ASSERT(oldHost);
+ 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_isSuspendingPendingRequests=%d", m_isSuspendingPendingRequests);
+ if (m_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->second);
+
+ 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())
+ return;
+
+ requestsPending.removeFirst();
+ host->addLoadInProgress(resourceLoader.get());
+ resourceLoader->start();
+ }
+ }
+}
+
+void ResourceLoadScheduler::suspendPendingRequests()
+{
+ ASSERT(!m_isSuspendingPendingRequests);
+ m_isSuspendingPendingRequests = true;
+}
+
+void ResourceLoadScheduler::resumePendingRequests()
+{
+ ASSERT(m_isSuspendingPendingRequests);
+ m_isSuspendingPendingRequests = false;
+ 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.contains(resourceLoader)) {
+ 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() const
+{
+ return m_requestsLoading.size() >= (resourceLoadScheduler()->isSerialLoadingEnabled() ? 1 : m_maxRequestsInFlight);
+}
+
+} // namespace WebCore