initial import
[vuplus_webkit] / Source / WebCore / loader / ResourceLoadScheduler.cpp
1 /*
2     Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
3     Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
4     Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
5     Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
6     Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
7     Copyright (C) 2010 Google Inc. All rights reserved.
8
9     This library is free software; you can redistribute it and/or
10     modify it under the terms of the GNU Library General Public
11     License as published by the Free Software Foundation; either
12     version 2 of the License, or (at your option) any later version.
13
14     This library is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17     Library General Public License for more details.
18
19     You should have received a copy of the GNU Library General Public License
20     along with this library; see the file COPYING.LIB.  If not, write to
21     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22     Boston, MA 02110-1301, USA.
23  */
24
25 #include "config.h"
26 #include "ResourceLoadScheduler.h"
27
28 #include "Document.h"
29 #include "DocumentLoader.h"
30 #include "Frame.h"
31 #include "FrameLoader.h"
32 #include "InspectorInstrumentation.h"
33 #include "KURL.h"
34 #include "Logging.h"
35 #include "NetscapePlugInStreamLoader.h"
36 #include "ResourceLoader.h"
37 #include "ResourceRequest.h"
38 #include "SubresourceLoader.h"
39 #include <wtf/MainThread.h>
40 #include <wtf/text/CString.h>
41
42 #define REQUEST_MANAGEMENT_ENABLED 1
43
44 namespace WebCore {
45
46 #if REQUEST_MANAGEMENT_ENABLED
47 static const unsigned maxRequestsInFlightForNonHTTPProtocols = 20;
48 // Match the parallel connection count used by the networking layer.
49 static unsigned maxRequestsInFlightPerHost;
50 #else
51 static const unsigned maxRequestsInFlightForNonHTTPProtocols = 10000;
52 static const unsigned maxRequestsInFlightPerHost = 10000;
53 #endif
54
55 ResourceLoadScheduler::HostInformation* ResourceLoadScheduler::hostForURL(const KURL& url, CreateHostPolicy createHostPolicy)
56 {
57     if (!url.protocolInHTTPFamily())
58         return m_nonHTTPProtocolHost;
59
60     m_hosts.checkConsistency();
61     String hostName = url.host();
62     HostInformation* host = m_hosts.get(hostName);
63     if (!host && createHostPolicy == CreateIfNotFound) {
64         host = new HostInformation(hostName, maxRequestsInFlightPerHost);
65         m_hosts.add(hostName, host);
66     }
67     return host;
68 }
69
70 ResourceLoadScheduler* resourceLoadScheduler()
71 {
72     ASSERT(isMainThread());
73     DEFINE_STATIC_LOCAL(ResourceLoadScheduler, resourceLoadScheduler, ());
74     return &resourceLoadScheduler;
75 }
76
77 ResourceLoadScheduler::ResourceLoadScheduler()
78     : m_nonHTTPProtocolHost(new HostInformation(String(), maxRequestsInFlightForNonHTTPProtocols))
79     , m_requestTimer(this, &ResourceLoadScheduler::requestTimerFired)
80     , m_isSuspendingPendingRequests(false)
81     , m_isSerialLoadingEnabled(false)
82 {
83 #if REQUEST_MANAGEMENT_ENABLED
84     maxRequestsInFlightPerHost = initializeMaximumHTTPConnectionCountPerHost();
85 #endif
86 }
87
88 PassRefPtr<SubresourceLoader> ResourceLoadScheduler::scheduleSubresourceLoad(Frame* frame, SubresourceLoaderClient* client, const ResourceRequest& request, ResourceLoadPriority priority, SecurityCheckPolicy securityCheck, const ResourceLoaderOptions& options)
89 {
90     RefPtr<SubresourceLoader> loader = SubresourceLoader::create(frame, client, request, securityCheck, options);
91     if (loader)
92         scheduleLoad(loader.get(), priority);
93     return loader.release();
94 }
95
96 PassRefPtr<NetscapePlugInStreamLoader> ResourceLoadScheduler::schedulePluginStreamLoad(Frame* frame, NetscapePlugInStreamLoaderClient* client, const ResourceRequest& request)
97 {
98     PassRefPtr<NetscapePlugInStreamLoader> loader = NetscapePlugInStreamLoader::create(frame, client, request);
99     if (loader)
100         scheduleLoad(loader.get(), ResourceLoadPriorityLow);
101     return loader;
102 }
103
104 void ResourceLoadScheduler::addMainResourceLoad(ResourceLoader* resourceLoader)
105 {
106     hostForURL(resourceLoader->url(), CreateIfNotFound)->addLoadInProgress(resourceLoader);
107 }
108
109 void ResourceLoadScheduler::scheduleLoad(ResourceLoader* resourceLoader, ResourceLoadPriority priority)
110 {
111     ASSERT(resourceLoader);
112     ASSERT(priority != ResourceLoadPriorityUnresolved);
113 #if !REQUEST_MANAGEMENT_ENABLED
114     priority = ResourceLoadPriorityHighest;
115 #endif
116
117     LOG(ResourceLoading, "ResourceLoadScheduler::load resource %p '%s'", resourceLoader, resourceLoader->url().string().latin1().data());
118
119     // 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.
120     if (resourceLoader->documentLoader()->archiveResourceForURL(resourceLoader->request().url())) {
121         resourceLoader->start();
122         return;
123     }
124
125     HostInformation* host = hostForURL(resourceLoader->url(), CreateIfNotFound);    
126     bool hadRequests = host->hasRequests();
127     host->schedule(resourceLoader, priority);
128
129     if (priority > ResourceLoadPriorityLow || !resourceLoader->url().protocolInHTTPFamily() || (priority == ResourceLoadPriorityLow && !hadRequests)) {
130         // Try to request important resources immediately.
131         servePendingRequests(host, priority);
132         return;
133     }
134
135     // Handle asynchronously so early low priority requests don't get scheduled before later high priority ones.
136     InspectorInstrumentation::didScheduleResourceRequest(resourceLoader->frameLoader() ? resourceLoader->frameLoader()->frame()->document() : 0, resourceLoader->url());
137     scheduleServePendingRequests();
138 }
139
140 void ResourceLoadScheduler::remove(ResourceLoader* resourceLoader)
141 {
142     ASSERT(resourceLoader);
143
144     HostInformation* host = hostForURL(resourceLoader->url());
145     if (host)
146         host->remove(resourceLoader);
147     scheduleServePendingRequests();
148 }
149
150 void ResourceLoadScheduler::crossOriginRedirectReceived(ResourceLoader* resourceLoader, const KURL& redirectURL)
151 {
152     HostInformation* oldHost = hostForURL(resourceLoader->url());
153     ASSERT(oldHost);
154     HostInformation* newHost = hostForURL(redirectURL, CreateIfNotFound);
155
156     if (oldHost->name() == newHost->name())
157         return;
158     
159     newHost->addLoadInProgress(resourceLoader);
160     oldHost->remove(resourceLoader);
161 }
162
163 void ResourceLoadScheduler::servePendingRequests(ResourceLoadPriority minimumPriority)
164 {
165     LOG(ResourceLoading, "ResourceLoadScheduler::servePendingRequests. m_isSuspendingPendingRequests=%d", m_isSuspendingPendingRequests); 
166     if (m_isSuspendingPendingRequests)
167         return;
168
169     m_requestTimer.stop();
170     
171     servePendingRequests(m_nonHTTPProtocolHost, minimumPriority);
172
173     Vector<HostInformation*> hostsToServe;
174     m_hosts.checkConsistency();
175     HostMap::iterator end = m_hosts.end();
176     for (HostMap::iterator iter = m_hosts.begin(); iter != end; ++iter)
177         hostsToServe.append(iter->second);
178
179     int size = hostsToServe.size();
180     for (int i = 0; i < size; ++i) {
181         HostInformation* host = hostsToServe[i];
182         if (host->hasRequests())
183             servePendingRequests(host, minimumPriority);
184         else
185             delete m_hosts.take(host->name());
186     }
187 }
188
189 void ResourceLoadScheduler::servePendingRequests(HostInformation* host, ResourceLoadPriority minimumPriority)
190 {
191     LOG(ResourceLoading, "ResourceLoadScheduler::servePendingRequests HostInformation.m_name='%s'", host->name().latin1().data());
192
193     for (int priority = ResourceLoadPriorityHighest; priority >= minimumPriority; --priority) {
194         HostInformation::RequestQueue& requestsPending = host->requestsPending(ResourceLoadPriority(priority));
195
196         while (!requestsPending.isEmpty()) {
197             RefPtr<ResourceLoader> resourceLoader = requestsPending.first();
198
199             // For named hosts - which are only http(s) hosts - we should always enforce the connection limit.
200             // For non-named hosts - everything but http(s) - we should only enforce the limit if the document isn't done parsing 
201             // and we don't know all stylesheets yet.
202             Document* document = resourceLoader->frameLoader() ? resourceLoader->frameLoader()->frame()->document() : 0;
203             bool shouldLimitRequests = !host->name().isNull() || (document && (document->parsing() || !document->haveStylesheetsLoaded()));
204             if (shouldLimitRequests && host->limitRequests(ResourceLoadPriority(priority)))
205                 return;
206
207             requestsPending.removeFirst();
208             host->addLoadInProgress(resourceLoader.get());
209             resourceLoader->start();
210         }
211     }
212 }
213
214 void ResourceLoadScheduler::suspendPendingRequests()
215 {
216     ASSERT(!m_isSuspendingPendingRequests);
217     m_isSuspendingPendingRequests = true;
218 }
219
220 void ResourceLoadScheduler::resumePendingRequests()
221 {
222     ASSERT(m_isSuspendingPendingRequests);
223     m_isSuspendingPendingRequests = false;
224     if (!m_hosts.isEmpty() || m_nonHTTPProtocolHost->hasRequests())
225         scheduleServePendingRequests();
226 }
227     
228 void ResourceLoadScheduler::scheduleServePendingRequests()
229 {
230     LOG(ResourceLoading, "ResourceLoadScheduler::scheduleServePendingRequests, m_requestTimer.isActive()=%u", m_requestTimer.isActive());
231     if (!m_requestTimer.isActive())
232         m_requestTimer.startOneShot(0);
233 }
234
235 void ResourceLoadScheduler::requestTimerFired(Timer<ResourceLoadScheduler>*) 
236 {
237     LOG(ResourceLoading, "ResourceLoadScheduler::requestTimerFired\n");
238     servePendingRequests();
239 }
240
241 ResourceLoadScheduler::HostInformation::HostInformation(const String& name, unsigned maxRequestsInFlight)
242     : m_name(name)
243     , m_maxRequestsInFlight(maxRequestsInFlight)
244 {
245 }
246
247 ResourceLoadScheduler::HostInformation::~HostInformation()
248 {
249     ASSERT(m_requestsLoading.isEmpty());
250     for (unsigned p = 0; p <= ResourceLoadPriorityHighest; p++)
251         ASSERT(m_requestsPending[p].isEmpty());
252 }
253     
254 void ResourceLoadScheduler::HostInformation::schedule(ResourceLoader* resourceLoader, ResourceLoadPriority priority)
255 {
256     m_requestsPending[priority].append(resourceLoader);
257 }
258     
259 void ResourceLoadScheduler::HostInformation::addLoadInProgress(ResourceLoader* resourceLoader)
260 {
261     LOG(ResourceLoading, "HostInformation '%s' loading '%s'. Current count %d", m_name.latin1().data(), resourceLoader->url().string().latin1().data(), m_requestsLoading.size());
262     m_requestsLoading.add(resourceLoader);
263 }
264     
265 void ResourceLoadScheduler::HostInformation::remove(ResourceLoader* resourceLoader)
266 {
267     if (m_requestsLoading.contains(resourceLoader)) {
268         m_requestsLoading.remove(resourceLoader);
269         return;
270     }
271     
272     for (int priority = ResourceLoadPriorityHighest; priority >= ResourceLoadPriorityLowest; --priority) {  
273         RequestQueue::iterator end = m_requestsPending[priority].end();
274         for (RequestQueue::iterator it = m_requestsPending[priority].begin(); it != end; ++it) {
275             if (*it == resourceLoader) {
276                 m_requestsPending[priority].remove(it);
277                 return;
278             }
279         }
280     }
281 }
282
283 bool ResourceLoadScheduler::HostInformation::hasRequests() const
284 {
285     if (!m_requestsLoading.isEmpty())
286         return true;
287     for (unsigned p = 0; p <= ResourceLoadPriorityHighest; p++) {
288         if (!m_requestsPending[p].isEmpty())
289             return true;
290     }
291     return false;
292 }
293
294 bool ResourceLoadScheduler::HostInformation::limitRequests(ResourceLoadPriority priority) const 
295 {
296     if (priority == ResourceLoadPriorityVeryLow && !m_requestsLoading.isEmpty())
297         return true;
298     return m_requestsLoading.size() >= (resourceLoadScheduler()->isSerialLoadingEnabled() ? 1 : m_maxRequestsInFlight);
299 }
300
301 } // namespace WebCore