8cf2c1895880aaac1652d363b1b4679d53b27d5a
[WebKit-https.git] / 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 "Frame.h"
30 #include "FrameLoader.h"
31 #include "InspectorInstrumentation.h"
32 #include "KURL.h"
33 #include "Logging.h"
34 #include "NetscapePlugInStreamLoader.h"
35 #include "ResourceLoader.h"
36 #include "ResourceRequest.h"
37 #include "SubresourceLoader.h"
38 #include <wtf/text/CString.h>
39
40 #define REQUEST_MANAGEMENT_ENABLED 1
41
42 namespace WebCore {
43
44 #if REQUEST_MANAGEMENT_ENABLED
45 static const unsigned maxRequestsInFlightForNonHTTPProtocols = 20;
46 // Match the parallel connection count used by the networking layer.
47 static unsigned maxRequestsInFlightPerHost;
48 #else
49 static const unsigned maxRequestsInFlightForNonHTTPProtocols = 10000;
50 static const unsigned maxRequestsInFlightPerHost = 10000;
51 #endif
52
53 ResourceLoadScheduler::HostInformation* ResourceLoadScheduler::hostForURL(const KURL& url, CreateHostPolicy createHostPolicy)
54 {
55     if (!url.protocolInHTTPFamily())
56         return m_nonHTTPProtocolHost;
57
58     m_hosts.checkConsistency();
59     String hostName = url.host();
60     HostInformation* host = m_hosts.get(hostName);
61     if (!host && createHostPolicy == CreateIfNotFound) {
62         host = new HostInformation(hostName, maxRequestsInFlightPerHost);
63         m_hosts.add(hostName, host);
64     }
65     return host;
66 }
67
68 ResourceLoadScheduler* resourceLoadScheduler()
69 {
70     ASSERT(isMainThread());
71     DEFINE_STATIC_LOCAL(ResourceLoadScheduler, resourceLoadScheduler, ());
72     return &resourceLoadScheduler;
73 }
74
75 ResourceLoadScheduler::ResourceLoadScheduler()
76     : m_nonHTTPProtocolHost(new HostInformation(String(), maxRequestsInFlightForNonHTTPProtocols))
77     , m_requestTimer(this, &ResourceLoadScheduler::requestTimerFired)
78     , m_isSuspendingPendingRequests(false)
79     , m_isSerialLoadingEnabled(false)
80 {
81 #if REQUEST_MANAGEMENT_ENABLED
82     maxRequestsInFlightPerHost = initializeMaximumHTTPConnectionCountPerHost();
83 #endif
84 }
85
86 PassRefPtr<SubresourceLoader> ResourceLoadScheduler::scheduleSubresourceLoad(Frame* frame, SubresourceLoaderClient* client, const ResourceRequest& request, ResourceLoadPriority priority, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks, bool shouldContentSniff)
87 {
88     PassRefPtr<SubresourceLoader> loader = SubresourceLoader::create(frame, client, request, securityCheck, sendResourceLoadCallbacks, shouldContentSniff);
89     if (loader)
90         scheduleLoad(loader.get(), priority);
91     return loader;
92 }
93     
94 PassRefPtr<NetscapePlugInStreamLoader> ResourceLoadScheduler::schedulePluginStreamLoad(Frame* frame, NetscapePlugInStreamLoaderClient* client, const ResourceRequest& request)
95 {
96     PassRefPtr<NetscapePlugInStreamLoader> loader = NetscapePlugInStreamLoader::create(frame, client, request);
97     if (loader)
98         scheduleLoad(loader.get(), ResourceLoadPriorityLow);
99     return loader;
100 }
101
102 void ResourceLoadScheduler::addMainResourceLoad(ResourceLoader* resourceLoader)
103 {
104     hostForURL(resourceLoader->url(), CreateIfNotFound)->addLoadInProgress(resourceLoader);
105 }
106
107 void ResourceLoadScheduler::scheduleLoad(ResourceLoader* resourceLoader, ResourceLoadPriority priority)
108 {
109     ASSERT(resourceLoader);
110     ASSERT(priority != ResourceLoadPriorityUnresolved);
111 #if !REQUEST_MANAGEMENT_ENABLED
112     priority = ResourceLoadPriorityHighest;
113 #endif
114
115     LOG(ResourceLoading, "ResourceLoadScheduler::load resource %p '%s'", resourceLoader, resourceLoader->url().string().latin1().data());
116     HostInformation* host = hostForURL(resourceLoader->url(), CreateIfNotFound);    
117     bool hadRequests = host->hasRequests();
118     host->schedule(resourceLoader, priority);
119
120     if (priority > ResourceLoadPriorityLow || !resourceLoader->url().protocolInHTTPFamily() || (priority == ResourceLoadPriorityLow && !hadRequests)) {
121         // Try to request important resources immediately.
122         servePendingRequests(host, priority);
123     } else {
124         // Handle asynchronously so early low priority requests don't get scheduled before later high priority ones.
125         InspectorInstrumentation::didScheduleResourceRequest(resourceLoader->frameLoader() ? resourceLoader->frameLoader()->frame()->document() : 0, resourceLoader->url());
126         scheduleServePendingRequests();
127     }
128 }
129
130 void ResourceLoadScheduler::remove(ResourceLoader* resourceLoader)
131 {
132     ASSERT(resourceLoader);
133
134     HostInformation* host = hostForURL(resourceLoader->url());
135     if (host)
136         host->remove(resourceLoader);
137     scheduleServePendingRequests();
138 }
139
140 void ResourceLoadScheduler::crossOriginRedirectReceived(ResourceLoader* resourceLoader, const KURL& redirectURL)
141 {
142     HostInformation* oldHost = hostForURL(resourceLoader->url());
143     ASSERT(oldHost);
144     HostInformation* newHost = hostForURL(redirectURL, CreateIfNotFound);
145
146     if (oldHost->name() == newHost->name())
147         return;
148     
149     newHost->addLoadInProgress(resourceLoader);
150     oldHost->remove(resourceLoader);
151 }
152
153 void ResourceLoadScheduler::servePendingRequests(ResourceLoadPriority minimumPriority)
154 {
155     LOG(ResourceLoading, "ResourceLoadScheduler::servePendingRequests. m_isSuspendingPendingRequests=%d", m_isSuspendingPendingRequests); 
156     if (m_isSuspendingPendingRequests)
157         return;
158
159     m_requestTimer.stop();
160     
161     servePendingRequests(m_nonHTTPProtocolHost, minimumPriority);
162
163     Vector<HostInformation*> hostsToServe;
164     m_hosts.checkConsistency();
165     HostMap::iterator end = m_hosts.end();
166     for (HostMap::iterator iter = m_hosts.begin(); iter != end; ++iter)
167         hostsToServe.append(iter->second);
168
169     int size = hostsToServe.size();
170     for (int i = 0; i < size; ++i) {
171         HostInformation* host = hostsToServe[i];
172         if (host->hasRequests())
173             servePendingRequests(host, minimumPriority);
174         else
175             delete m_hosts.take(host->name());
176     }
177 }
178
179 void ResourceLoadScheduler::servePendingRequests(HostInformation* host, ResourceLoadPriority minimumPriority)
180 {
181     LOG(ResourceLoading, "ResourceLoadScheduler::servePendingRequests HostInformation.m_name='%s'", host->name().latin1().data());
182
183     for (int priority = ResourceLoadPriorityHighest; priority >= minimumPriority; --priority) {
184         HostInformation::RequestQueue& requestsPending = host->requestsPending((ResourceLoadPriority) priority);
185
186         while (!requestsPending.isEmpty()) {
187             RefPtr<ResourceLoader> resourceLoader = requestsPending.first();
188
189             // For named hosts - which are only http(s) hosts - we should always enforce the connection limit.
190             // For non-named hosts - everything but http(s) - we should only enforce the limit if the document isn't done parsing 
191             // and we don't know all stylesheets yet.
192             Document* document = resourceLoader->frameLoader() ? resourceLoader->frameLoader()->frame()->document() : 0;
193             bool shouldLimitRequests = !host->name().isNull() || (document && (document->parsing() || !document->haveStylesheetsLoaded()));
194             if (shouldLimitRequests && host->limitRequests())
195                 return;
196
197             requestsPending.removeFirst();
198             host->addLoadInProgress(resourceLoader.get());
199             resourceLoader->start();
200         }
201     }
202 }
203
204 void ResourceLoadScheduler::suspendPendingRequests()
205 {
206     ASSERT(!m_isSuspendingPendingRequests);
207     m_isSuspendingPendingRequests = true;
208 }
209
210 void ResourceLoadScheduler::resumePendingRequests()
211 {
212     ASSERT(m_isSuspendingPendingRequests);
213     m_isSuspendingPendingRequests = false;
214     if (!m_hosts.isEmpty() || m_nonHTTPProtocolHost->hasRequests())
215         scheduleServePendingRequests();
216 }
217     
218 void ResourceLoadScheduler::scheduleServePendingRequests()
219 {
220     LOG(ResourceLoading, "ResourceLoadScheduler::scheduleServePendingRequests, m_requestTimer.isActive()=%u", m_requestTimer.isActive());
221     if (!m_requestTimer.isActive())
222         m_requestTimer.startOneShot(0);
223 }
224
225 void ResourceLoadScheduler::requestTimerFired(Timer<ResourceLoadScheduler>*) 
226 {
227     LOG(ResourceLoading, "ResourceLoadScheduler::requestTimerFired\n");
228     servePendingRequests();
229 }
230
231 ResourceLoadScheduler::HostInformation::HostInformation(const String& name, unsigned maxRequestsInFlight)
232     : m_name(name)
233     , m_maxRequestsInFlight(maxRequestsInFlight)
234 {
235 }
236
237 ResourceLoadScheduler::HostInformation::~HostInformation()
238 {
239     ASSERT(m_requestsLoading.isEmpty());
240     for (unsigned p = 0; p <= ResourceLoadPriorityHighest; p++)
241         ASSERT(m_requestsPending[p].isEmpty());
242 }
243     
244 void ResourceLoadScheduler::HostInformation::schedule(ResourceLoader* resourceLoader, ResourceLoadPriority priority)
245 {
246     m_requestsPending[priority].append(resourceLoader);
247 }
248     
249 void ResourceLoadScheduler::HostInformation::addLoadInProgress(ResourceLoader* resourceLoader)
250 {
251     LOG(ResourceLoading, "HostInformation '%s' loading '%s'. Current count %d", m_name.latin1().data(), resourceLoader->url().string().latin1().data(), m_requestsLoading.size());
252     m_requestsLoading.add(resourceLoader);
253 }
254     
255 void ResourceLoadScheduler::HostInformation::remove(ResourceLoader* resourceLoader)
256 {
257     if (m_requestsLoading.contains(resourceLoader)) {
258         m_requestsLoading.remove(resourceLoader);
259         return;
260     }
261     
262     for (int priority = ResourceLoadPriorityHighest; priority >= ResourceLoadPriorityLowest; --priority) {  
263         RequestQueue::iterator end = m_requestsPending[priority].end();
264         for (RequestQueue::iterator it = m_requestsPending[priority].begin(); it != end; ++it) {
265             if (*it == resourceLoader) {
266                 m_requestsPending[priority].remove(it);
267                 return;
268             }
269         }
270     }
271 }
272
273 bool ResourceLoadScheduler::HostInformation::hasRequests() const
274 {
275     if (!m_requestsLoading.isEmpty())
276         return true;
277     for (unsigned p = 0; p <= ResourceLoadPriorityHighest; p++) {
278         if (!m_requestsPending[p].isEmpty())
279             return true;
280     }
281     return false;
282 }
283
284 bool ResourceLoadScheduler::HostInformation::limitRequests() const 
285
286     return m_requestsLoading.size() >= (resourceLoadScheduler()->isSerialLoadingEnabled() ? 1 : m_maxRequestsInFlight);
287 }
288
289 } // namespace WebCore