4c6db7f1c87ee411932a86db483fedb47e48ebdb
[WebKit-https.git] / Source / WebCore / loader / SubresourceLoader.cpp
1 /*
2  * Copyright (C) 2006, 2007, 2009 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  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "SubresourceLoader.h"
31
32 #include "CachedResourceLoader.h"
33 #include "Document.h"
34 #include "DocumentLoader.h"
35 #include "FeatureCounter.h"
36 #include "Frame.h"
37 #include "FrameLoader.h"
38 #include "Logging.h"
39 #include "MemoryCache.h"
40 #include "Page.h"
41 #include <wtf/Ref.h>
42 #include <wtf/RefCountedLeakCounter.h>
43 #include <wtf/StdLibExtras.h>
44 #include <wtf/text/CString.h>
45
46 #if PLATFORM(IOS)
47 #include <RuntimeApplicationChecksIOS.h>
48 #endif
49
50 namespace WebCore {
51
52 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, subresourceLoaderCounter, ("SubresourceLoader"));
53
54 SubresourceLoader::RequestCountTracker::RequestCountTracker(CachedResourceLoader* cachedResourceLoader, CachedResource* resource)
55     : m_cachedResourceLoader(cachedResourceLoader)
56     , m_resource(resource)
57 {
58     m_cachedResourceLoader->incrementRequestCount(m_resource);
59 }
60
61 SubresourceLoader::RequestCountTracker::~RequestCountTracker()
62 {
63     m_cachedResourceLoader->decrementRequestCount(m_resource);
64 }
65
66 SubresourceLoader::SubresourceLoader(Frame* frame, CachedResource* resource, const ResourceLoaderOptions& options)
67     : ResourceLoader(frame, options)
68     , m_resource(resource)
69     , m_loadingMultipartContent(false)
70     , m_state(Uninitialized)
71     , m_requestCountTracker(std::make_unique<RequestCountTracker>(frame->document()->cachedResourceLoader(), resource))
72 {
73 #ifndef NDEBUG
74     subresourceLoaderCounter.increment();
75 #endif
76 }
77
78 SubresourceLoader::~SubresourceLoader()
79 {
80     ASSERT(m_state != Initialized);
81     ASSERT(reachedTerminalState());
82 #ifndef NDEBUG
83     subresourceLoaderCounter.decrement();
84 #endif
85 }
86
87 PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, CachedResource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options)
88 {
89     RefPtr<SubresourceLoader> subloader(adoptRef(new SubresourceLoader(frame, resource, options)));
90 #if PLATFORM(IOS)
91     if (!applicationIsWebProcess()) {
92         // On iOS, do not invoke synchronous resource load delegates while resource load scheduling
93         // is disabled to avoid re-entering style selection from a different thread (see <rdar://problem/9121719>).
94         // FIXME: This should be fixed for all ports in <https://bugs.webkit.org/show_bug.cgi?id=56647>.
95         subloader->m_iOSOriginalRequest = request;
96         return subloader.release();
97     }
98 #endif
99     if (!subloader->init(request))
100         return nullptr;
101     return subloader.release();
102 }
103     
104 #if PLATFORM(IOS)
105 bool SubresourceLoader::startLoading()
106 {
107     ASSERT(!applicationIsWebProcess());
108     if (!init(m_iOSOriginalRequest))
109         return false;
110     m_iOSOriginalRequest = ResourceRequest();
111     start();
112     return true;
113 }
114 #endif
115
116 CachedResource* SubresourceLoader::cachedResource()
117 {
118     return m_resource;
119 }
120
121 void SubresourceLoader::cancelIfNotFinishing()
122 {
123     if (m_state != Initialized)
124         return;
125
126     ResourceLoader::cancel();
127 }
128
129 bool SubresourceLoader::init(const ResourceRequest& request)
130 {
131     if (!ResourceLoader::init(request))
132         return false;
133
134     ASSERT(!reachedTerminalState());
135     m_state = Initialized;
136     m_documentLoader->addSubresourceLoader(this);
137     return true;
138 }
139
140 bool SubresourceLoader::isSubresourceLoader()
141 {
142     return true;
143 }
144
145 void SubresourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
146 {
147     // Store the previous URL because the call to ResourceLoader::willSendRequest will modify it.
148     URL previousURL = request().url();
149     Ref<SubresourceLoader> protect(*this);
150
151     ASSERT(!newRequest.isNull());
152     if (!redirectResponse.isNull()) {
153         // CachedResources are keyed off their original request URL.
154         // Requesting the same original URL a second time can redirect to a unique second resource.
155         // Therefore, if a redirect to a different destination URL occurs, we should no longer consider this a revalidation of the first resource.
156         // Doing so would have us reusing the resource from the first request if the second request's revalidation succeeds.
157         if (newRequest.isConditional() && m_resource->resourceToRevalidate() && newRequest.url() != m_resource->resourceToRevalidate()->response().url()) {
158             newRequest.makeUnconditional();
159             memoryCache().revalidationFailed(m_resource);
160             FEATURE_COUNTER_INCREMENT_KEY(m_frame ? m_frame->page() : nullptr, FeatureCounterCachedResourceRevalidationFailureKey);
161         }
162         
163         if (!m_documentLoader->cachedResourceLoader().canRequest(m_resource->type(), newRequest.url(), options())) {
164             cancel();
165             return;
166         }
167         if (m_resource->isImage() && m_documentLoader->cachedResourceLoader().shouldDeferImageLoad(newRequest.url())) {
168             cancel();
169             return;
170         }
171         m_resource->willSendRequest(newRequest, redirectResponse);
172     }
173
174     if (newRequest.isNull() || reachedTerminalState())
175         return;
176
177     ResourceLoader::willSendRequest(newRequest, redirectResponse);
178     if (newRequest.isNull())
179         cancel();
180 }
181
182 void SubresourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
183 {
184     ASSERT(m_state == Initialized);
185     Ref<SubresourceLoader> protect(*this);
186     m_resource->didSendData(bytesSent, totalBytesToBeSent);
187 }
188
189 void SubresourceLoader::didReceiveResponse(const ResourceResponse& response)
190 {
191     ASSERT(!response.isNull());
192     ASSERT(m_state == Initialized);
193
194     // Reference the object in this method since the additional processing can do
195     // anything including removing the last reference to this object; one example of this is 3266216.
196     Ref<SubresourceLoader> protect(*this);
197
198     if (shouldIncludeCertificateInfo())
199         response.includeCertificateInfo();
200
201     if (m_resource->resourceToRevalidate()) {
202         if (response.httpStatusCode() == 304) {
203             // 304 Not modified / Use local copy
204             // Existing resource is ok, just use it updating the expiration time.
205             m_resource->setResponse(response);
206             memoryCache().revalidationSucceeded(m_resource, response);
207             FEATURE_COUNTER_INCREMENT_KEY(m_frame ? m_frame->page() : nullptr, FeatureCounterCachedResourceRevalidationSuccessKey);
208             if (!reachedTerminalState())
209                 ResourceLoader::didReceiveResponse(response);
210             return;
211         }
212         // Did not get 304 response, continue as a regular resource load.
213         memoryCache().revalidationFailed(m_resource);
214         FEATURE_COUNTER_INCREMENT_KEY(m_frame ? m_frame->page() : nullptr, FeatureCounterCachedResourceRevalidationFailureKey);
215     }
216
217     m_resource->responseReceived(response);
218     if (reachedTerminalState())
219         return;
220
221     ResourceLoader::didReceiveResponse(response);
222     if (reachedTerminalState())
223         return;
224
225     // FIXME: Main resources have a different set of rules for multipart than images do.
226     // Hopefully we can merge those 2 paths.
227     if (response.isMultipart() && m_resource->type() != CachedResource::MainResource) {
228         m_loadingMultipartContent = true;
229
230         // We don't count multiParts in a CachedResourceLoader's request count
231         m_requestCountTracker = nullptr;
232         if (!m_resource->isImage()) {
233             cancel();
234             return;
235         }
236     }
237
238     auto* buffer = resourceData();
239     if (m_loadingMultipartContent && buffer && buffer->size()) {
240         // The resource data will change as the next part is loaded, so we need to make a copy.
241         m_resource->finishLoading(buffer->copy().get());
242         clearResourceData();
243         // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once.        
244         // After the first multipart section is complete, signal to delegates that this load is "finished" 
245         m_documentLoader->subresourceLoaderFinishedLoadingOnePart(this);
246         didFinishLoadingOnePart(0);
247     }
248
249     checkForHTTPStatusCodeError();
250 }
251
252 void SubresourceLoader::didReceiveData(const char* data, unsigned length, long long encodedDataLength, DataPayloadType dataPayloadType)
253 {
254     didReceiveDataOrBuffer(data, length, 0, encodedDataLength, dataPayloadType);
255 }
256
257 void SubresourceLoader::didReceiveBuffer(PassRefPtr<SharedBuffer> buffer, long long encodedDataLength, DataPayloadType dataPayloadType)
258 {
259     didReceiveDataOrBuffer(0, 0, buffer, encodedDataLength, dataPayloadType);
260 }
261
262 void SubresourceLoader::didReceiveDataOrBuffer(const char* data, int length, PassRefPtr<SharedBuffer> prpBuffer, long long encodedDataLength, DataPayloadType dataPayloadType)
263 {
264     if (m_resource->response().httpStatusCode() >= 400 && !m_resource->shouldIgnoreHTTPStatusCodeErrors())
265         return;
266     ASSERT(!m_resource->resourceToRevalidate());
267     ASSERT(!m_resource->errorOccurred());
268     ASSERT(m_state == Initialized);
269     // Reference the object in this method since the additional processing can do
270     // anything including removing the last reference to this object; one example of this is 3266216.
271     Ref<SubresourceLoader> protect(*this);
272     RefPtr<SharedBuffer> buffer = prpBuffer;
273     
274     ResourceLoader::didReceiveDataOrBuffer(data, length, buffer, encodedDataLength, dataPayloadType);
275
276     if (!m_loadingMultipartContent) {
277         if (auto* resourceData = this->resourceData())
278             m_resource->addDataBuffer(*resourceData);
279         else
280             m_resource->addData(buffer ? buffer->data() : data, buffer ? buffer->size() : length);
281     }
282 }
283
284 bool SubresourceLoader::checkForHTTPStatusCodeError()
285 {
286     if (m_resource->response().httpStatusCode() < 400 || m_resource->shouldIgnoreHTTPStatusCodeErrors())
287         return false;
288
289     m_state = Finishing;
290     m_resource->error(CachedResource::LoadError);
291     cancel();
292     return true;
293 }
294
295 void SubresourceLoader::didFinishLoading(double finishTime)
296 {
297     if (m_state != Initialized)
298         return;
299     ASSERT(!reachedTerminalState());
300     ASSERT(!m_resource->resourceToRevalidate());
301     // FIXME (129394): We should cancel the load when a decode error occurs instead of continuing the load to completion.
302     ASSERT(!m_resource->errorOccurred() || m_resource->status() == CachedResource::DecodeError);
303     LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data());
304
305     Ref<SubresourceLoader> protect(*this);
306     CachedResourceHandle<CachedResource> protectResource(m_resource);
307
308     m_state = Finishing;
309     m_resource->setLoadFinishTime(finishTime);
310     m_resource->finishLoading(resourceData());
311
312     if (wasCancelled())
313         return;
314     m_resource->finish();
315     ASSERT(!reachedTerminalState());
316     didFinishLoadingOnePart(finishTime);
317     notifyDone();
318     if (reachedTerminalState())
319         return;
320     releaseResources();
321 }
322
323 void SubresourceLoader::didFail(const ResourceError& error)
324 {
325     if (m_state != Initialized)
326         return;
327     ASSERT(!reachedTerminalState());
328     LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string().latin1().data());
329
330     Ref<SubresourceLoader> protect(*this);
331     CachedResourceHandle<CachedResource> protectResource(m_resource);
332     m_state = Finishing;
333     if (m_resource->resourceToRevalidate())
334         memoryCache().revalidationFailed(m_resource);
335     m_resource->setResourceError(error);
336     if (!m_resource->isPreloaded())
337         memoryCache().remove(m_resource);
338     m_resource->error(CachedResource::LoadError);
339     cleanupForError(error);
340     notifyDone();
341     if (reachedTerminalState())
342         return;
343     releaseResources();
344 }
345
346 void SubresourceLoader::willCancel(const ResourceError& error)
347 {
348 #if PLATFORM(IOS)
349     // Since we defer initialization to scheduling time on iOS but
350     // CachedResourceLoader stores resources in the memory cache immediately,
351     // m_resource might be cached despite its loader not being initialized.
352     if (m_state != Initialized && m_state != Uninitialized)
353 #else
354     if (m_state != Initialized)
355 #endif
356         return;
357     ASSERT(!reachedTerminalState());
358     LOG(ResourceLoading, "Cancelled load of '%s'.\n", m_resource->url().string().latin1().data());
359
360     Ref<SubresourceLoader> protect(*this);
361 #if PLATFORM(IOS)
362     m_state = m_state == Uninitialized ? CancelledWhileInitializing : Finishing;
363 #else
364     m_state = Finishing;
365 #endif
366     if (m_resource->resourceToRevalidate())
367         memoryCache().revalidationFailed(m_resource);
368     m_resource->setResourceError(error);
369     memoryCache().remove(m_resource);
370 }
371
372 void SubresourceLoader::didCancel(const ResourceError&)
373 {
374     if (m_state == Uninitialized)
375         return;
376
377     m_resource->cancelLoad();
378     notifyDone();
379 }
380
381 void SubresourceLoader::notifyDone()
382 {
383     if (reachedTerminalState())
384         return;
385
386     m_requestCountTracker = nullptr;
387 #if PLATFORM(IOS)
388     m_documentLoader->cachedResourceLoader().loadDone(m_resource, m_state != CancelledWhileInitializing);
389 #else
390     m_documentLoader->cachedResourceLoader().loadDone(m_resource);
391 #endif
392     if (reachedTerminalState())
393         return;
394     m_documentLoader->removeSubresourceLoader(this);
395 }
396
397 void SubresourceLoader::releaseResources()
398 {
399     ASSERT(!reachedTerminalState());
400 #if PLATFORM(IOS)
401     if (m_state != Uninitialized && m_state != CancelledWhileInitializing)
402 #else
403     if (m_state != Uninitialized)
404 #endif
405         m_resource->clearLoader();
406     m_resource = 0;
407     ResourceLoader::releaseResources();
408 }
409
410 }