b84ad02caf5ea648e45acd370b58babcf4279941
[WebKit-https.git] / Source / WebKit2 / WebProcess / Network / WebResourceLoadScheduler.cpp
1 /*
2  * Copyright (C) 2012 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "WebResourceLoadScheduler.h"
28
29 #include "Logging.h"
30 #include "NetworkConnectionToWebProcessMessages.h"
31 #include "NetworkProcessConnection.h"
32 #include "NetworkResourceLoadParameters.h"
33 #include "WebCoreArgumentCoders.h"
34 #include "WebProcess.h"
35 #include "WebResourceLoader.h"
36 #include <WebCore/DocumentLoader.h>
37 #include <WebCore/Frame.h>
38 #include <WebCore/FrameLoader.h>
39 #include <WebCore/NetscapePlugInStreamLoader.h>
40 #include <WebCore/ResourceBuffer.h>
41 #include <WebCore/ResourceLoader.h>
42 #include <WebCore/Settings.h>
43 #include <WebCore/SubresourceLoader.h>
44 #include <wtf/text/CString.h>
45
46 #if ENABLE(NETWORK_PROCESS)
47
48 using namespace WebCore;
49
50 namespace WebKit {
51
52 WebResourceLoadScheduler::WebResourceLoadScheduler()
53     : m_suspendPendingRequestsCount(0)
54 {
55 }
56
57 WebResourceLoadScheduler::~WebResourceLoadScheduler()
58 {
59 }
60
61 PassRefPtr<SubresourceLoader> WebResourceLoadScheduler::scheduleSubresourceLoad(Frame* frame, CachedResource* resource, const ResourceRequest& request, ResourceLoadPriority priority, const ResourceLoaderOptions& options)
62 {
63     RefPtr<SubresourceLoader> loader = SubresourceLoader::create(frame, resource, request, options);
64     if (loader)
65         scheduleLoad(loader.get(), priority);
66     return loader.release();
67 }
68
69 PassRefPtr<NetscapePlugInStreamLoader> WebResourceLoadScheduler::schedulePluginStreamLoad(Frame* frame, NetscapePlugInStreamLoaderClient* client, const ResourceRequest& request)
70 {
71     RefPtr<NetscapePlugInStreamLoader> loader = NetscapePlugInStreamLoader::create(frame, client, request);
72     if (loader)
73         scheduleLoad(loader.get(), ResourceLoadPriorityLow);
74     return loader.release();
75 }
76
77 void WebResourceLoadScheduler::scheduleLoad(ResourceLoader* resourceLoader, ResourceLoadPriority priority)
78 {
79     LOG(NetworkScheduling, "(WebProcess) WebResourceLoadScheduler::scheduleLoad, url '%s' priority %i", resourceLoader->url().string().utf8().data(), priority);
80
81     ASSERT(resourceLoader);
82     ASSERT(priority != ResourceLoadPriorityUnresolved);
83     priority = ResourceLoadPriorityHighest;
84
85     // 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.
86     if (resourceLoader->documentLoader()->archiveResourceForURL(resourceLoader->request().url())) {
87         startResourceLoader(resourceLoader);
88         return;
89     }
90     
91     ResourceLoadIdentifier identifier;
92     
93     ResourceRequest request = resourceLoader->request();
94     
95     // We want the network process involved in scheduling data URL loads but it doesn't need to know the full (often long) URL.
96     if (request.url().protocolIsData()) {
97         DEFINE_STATIC_LOCAL(KURL, dataURL, (KURL(), "data:"));
98         request.setURL(dataURL);
99     }
100     NetworkResourceLoadParameters loadParameters(request, priority, resourceLoader->shouldSniffContent() ? SniffContent : DoNotSniffContent);
101     if (!WebProcess::shared().networkConnection()->connection()->sendSync(Messages::NetworkConnectionToWebProcess::ScheduleResourceLoad(loadParameters), Messages::NetworkConnectionToWebProcess::ScheduleResourceLoad::Reply(identifier), 0)) {
102         // FIXME (NetworkProcess): What should we do if this fails?
103         ASSERT_NOT_REACHED();
104     }
105     
106     resourceLoader->setIdentifier(identifier);
107     m_webResourceLoaders.set(identifier, WebResourceLoader::create(identifier, resourceLoader));
108     
109     notifyDidScheduleResourceRequest(resourceLoader);
110 }
111
112 void WebResourceLoadScheduler::addMainResourceLoad(ResourceLoader* resourceLoader)
113 {
114     LOG(NetworkScheduling, "(WebProcess) WebResourceLoadScheduler::addMainResourceLoad, url '%s'", resourceLoader->url().string().utf8().data());
115
116     ResourceLoadIdentifier identifier;
117
118     if (!WebProcess::shared().networkConnection()->connection()->sendSync(Messages::NetworkConnectionToWebProcess::AddLoadInProgress(resourceLoader->url()), Messages::NetworkConnectionToWebProcess::AddLoadInProgress::Reply(identifier), 0)) {
119         // FIXME (NetworkProcess): What should we do if this fails?
120         ASSERT_NOT_REACHED();
121     }
122
123     resourceLoader->setIdentifier(identifier);
124     
125     m_coreResourceLoaders.set(identifier, resourceLoader);
126 }
127
128 void WebResourceLoadScheduler::remove(ResourceLoader* resourceLoader)
129 {
130     ASSERT(resourceLoader);
131     LOG(NetworkScheduling, "(WebProcess) WebResourceLoadScheduler::remove, url '%s'", resourceLoader->url().string().utf8().data());
132
133     // FIXME (NetworkProcess): It's possible for a resourceLoader to be removed before it ever started,
134     // meaning before it even has an identifier.
135     // We should make this not be possible.
136     // The ResourceLoader code path should always for an identifier to ResourceLoaders.
137     
138     ResourceLoadIdentifier identifier = resourceLoader->identifier();
139     if (!identifier) {
140         LOG_ERROR("WebResourceLoadScheduler removing a ResourceLoader that has no identifier.");
141         return;
142     }
143     
144     // FIXME (NetworkProcess): We should only tell the NetworkProcess to remove load identifiers for ResourceLoaders that were never started.
145     // If a resource load was actually started within the NetworkProcess then the NetworkProcess handles clearing out the identifier.
146     WebProcess::shared().networkConnection()->connection()->send(Messages::NetworkConnectionToWebProcess::RemoveLoadIdentifier(identifier), 0);
147     
148     ASSERT(m_webResourceLoaders.contains(identifier) || m_coreResourceLoaders.contains(identifier));
149     m_webResourceLoaders.remove(identifier);
150     m_coreResourceLoaders.remove(identifier);
151 }
152
153 void WebResourceLoadScheduler::crossOriginRedirectReceived(ResourceLoader*, const KURL&)
154 {
155     // We handle cross origin redirects entirely within the NetworkProcess.
156     // We override this call in the WebProcess to make it a no-op.
157 }
158
159 void WebResourceLoadScheduler::servePendingRequests(ResourceLoadPriority minimumPriority)
160 {
161     LOG(NetworkScheduling, "(WebProcess) WebResourceLoadScheduler::servePendingRequests");
162     
163     // If this WebProcess has its own request suspension count then we don't even
164     // have to bother messaging the NetworkProcess.
165     if (m_suspendPendingRequestsCount)
166         return;
167
168     WebProcess::shared().networkConnection()->connection()->send(Messages::NetworkConnectionToWebProcess::ServePendingRequests(minimumPriority), 0);
169 }
170
171 void WebResourceLoadScheduler::suspendPendingRequests()
172 {
173     WebProcess::shared().networkConnection()->connection()->sendSync(Messages::NetworkConnectionToWebProcess::SuspendPendingRequests(), Messages::NetworkConnectionToWebProcess::SuspendPendingRequests::Reply(), 0);
174
175     ++m_suspendPendingRequestsCount;
176 }
177
178 void WebResourceLoadScheduler::resumePendingRequests()
179 {
180     WebProcess::shared().networkConnection()->connection()->sendSync(Messages::NetworkConnectionToWebProcess::ResumePendingRequests(), Messages::NetworkConnectionToWebProcess::ResumePendingRequests::Reply(), 0);
181
182     ASSERT(m_suspendPendingRequestsCount);
183     --m_suspendPendingRequestsCount;
184 }
185
186 void WebResourceLoadScheduler::setSerialLoadingEnabled(bool enabled)
187 {
188     WebProcess::shared().networkConnection()->connection()->sendSync(Messages::NetworkConnectionToWebProcess::SetSerialLoadingEnabled(enabled), Messages::NetworkConnectionToWebProcess::SetSerialLoadingEnabled::Reply(), 0);
189 }
190
191 void WebResourceLoadScheduler::willSendRequest(ResourceLoadIdentifier identifier, WebCore::ResourceRequest& request, const WebCore::ResourceResponse& redirectResponse)
192 {
193     RefPtr<ResourceLoader> loader = m_webResourceLoaders.get(identifier)->coreLoader();
194     ASSERT(loader);
195
196     LOG(Network, "(WebProcess) WebResourceLoadScheduler::willSendRequest to '%s'", request.url().string().utf8().data());
197     loader->willSendRequest(request, redirectResponse);
198 }
199
200 void WebResourceLoadScheduler::didReceiveResponse(ResourceLoadIdentifier identifier, const WebCore::ResourceResponse& response)
201 {
202     RefPtr<ResourceLoader> loader = m_webResourceLoaders.get(identifier)->coreLoader();
203     ASSERT(loader);
204
205     LOG(Network, "(WebProcess) WebResourceLoadScheduler::didReceiveResponse for '%s'", loader->url().string().utf8().data());
206     loader->didReceiveResponse(response);
207 }
208
209 void WebResourceLoadScheduler::didReceiveData(ResourceLoadIdentifier identifier, const char* data, int size, int64_t encodedDataLength, bool allAtOnce)
210 {
211     RefPtr<ResourceLoader> loader = m_webResourceLoaders.get(identifier)->coreLoader();
212     ASSERT(loader);
213
214     LOG(Network, "(WebProcess) WebResourceLoadScheduler::didReceiveData of size %i for '%s'", size, loader->url().string().utf8().data());
215     loader->didReceiveData(data, size, encodedDataLength, allAtOnce);
216 }
217
218 void WebResourceLoadScheduler::didFinishResourceLoad(ResourceLoadIdentifier identifier, double finishTime)
219 {
220     RefPtr<ResourceLoader> loader = m_webResourceLoaders.get(identifier)->coreLoader();
221     ASSERT(loader);
222
223     LOG(Network, "(WebProcess) WebResourceLoadScheduler::didFinishResourceLoad for '%s'", loader->url().string().utf8().data());
224     loader->didFinishLoading(finishTime);
225 }
226
227 void WebResourceLoadScheduler::didReceiveResource(ResourceLoadIdentifier identifier, const ResourceBuffer& buffer, double finishTime)
228 {    
229     RefPtr<ResourceLoader> loader = m_webResourceLoaders.get(identifier)->coreLoader();
230     ASSERT(loader);
231
232     LOG(Network, "(WebProcess) WebResourceLoadScheduler::didReceiveResource for '%s'", loader->url().string().utf8().data());
233     
234     // Only send data to the didReceiveData callback if it exists.
235     if (!buffer.isEmpty()) {
236         // FIXME (NetworkProcess): Give ResourceLoader the ability to take ResourceBuffer arguments.
237         // That will allow us to pass it along to CachedResources and allow them to hang on to the shared memory behind the scenes.
238         loader->didReceiveData(reinterpret_cast<const char*>(buffer.data()), buffer.size(), -1 /* encodedDataLength */, true);
239     }
240
241     loader->didFinishLoading(finishTime);
242 }
243
244 void WebResourceLoadScheduler::didFailResourceLoad(ResourceLoadIdentifier identifier, const WebCore::ResourceError& error)
245 {
246     RefPtr<ResourceLoader> loader = m_webResourceLoaders.get(identifier)->coreLoader();
247     ASSERT(loader);
248
249     LOG(Network, "(WebProcess) WebResourceLoadScheduler::didFailResourceLoad for '%s'", loader->url().string().utf8().data());
250     
251     loader->didFail(error);
252 }
253
254 } // namespace WebKit
255
256 #endif // ENABLE(NETWORK_PROCESS)