[Content Filtering] Determine navigation and content policy before continuing to...
[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 "DiagnosticLoggingClient.h"
34 #include "DiagnosticLoggingKeys.h"
35 #include "Document.h"
36 #include "DocumentLoader.h"
37 #include "Frame.h"
38 #include "FrameLoader.h"
39 #include "Logging.h"
40 #include "MainFrame.h"
41 #include "MemoryCache.h"
42 #include "Page.h"
43 #include "Settings.h"
44 #include <wtf/Ref.h>
45 #include <wtf/RefCountedLeakCounter.h>
46 #include <wtf/StdLibExtras.h>
47 #include <wtf/TemporaryChange.h>
48 #include <wtf/text/CString.h>
49
50 #if PLATFORM(IOS)
51 #include <RuntimeApplicationChecksIOS.h>
52 #endif
53
54 #if ENABLE(CONTENT_EXTENSIONS)
55 #include "ResourceLoadInfo.h"
56 #endif
57
58 namespace WebCore {
59
60 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, subresourceLoaderCounter, ("SubresourceLoader"));
61
62 SubresourceLoader::RequestCountTracker::RequestCountTracker(CachedResourceLoader& cachedResourceLoader, CachedResource* resource)
63     : m_cachedResourceLoader(cachedResourceLoader)
64     , m_resource(resource)
65 {
66     m_cachedResourceLoader.incrementRequestCount(m_resource);
67 }
68
69 SubresourceLoader::RequestCountTracker::~RequestCountTracker()
70 {
71     m_cachedResourceLoader.decrementRequestCount(m_resource);
72 }
73
74 SubresourceLoader::SubresourceLoader(Frame* frame, CachedResource* resource, const ResourceLoaderOptions& options)
75     : ResourceLoader(frame, options)
76     , m_resource(resource)
77     , m_loadingMultipartContent(false)
78     , m_state(Uninitialized)
79     , m_requestCountTracker(std::make_unique<RequestCountTracker>(frame->document()->cachedResourceLoader(), resource))
80 {
81 #ifndef NDEBUG
82     subresourceLoaderCounter.increment();
83 #endif
84 #if ENABLE(CONTENT_EXTENSIONS)
85     m_resourceType = toResourceType(resource->type());
86 #endif
87 }
88
89 SubresourceLoader::~SubresourceLoader()
90 {
91     ASSERT(m_state != Initialized);
92     ASSERT(reachedTerminalState());
93 #ifndef NDEBUG
94     subresourceLoaderCounter.decrement();
95 #endif
96 }
97
98 RefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, CachedResource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options)
99 {
100     RefPtr<SubresourceLoader> subloader(adoptRef(new SubresourceLoader(frame, resource, options)));
101 #if PLATFORM(IOS)
102     if (!applicationIsWebProcess()) {
103         // On iOS, do not invoke synchronous resource load delegates while resource load scheduling
104         // is disabled to avoid re-entering style selection from a different thread (see <rdar://problem/9121719>).
105         // FIXME: This should be fixed for all ports in <https://bugs.webkit.org/show_bug.cgi?id=56647>.
106         subloader->m_iOSOriginalRequest = request;
107         return subloader.release();
108     }
109 #endif
110     if (!subloader->init(request))
111         return nullptr;
112     return subloader;
113 }
114     
115 #if PLATFORM(IOS)
116 bool SubresourceLoader::startLoading()
117 {
118     ASSERT(!applicationIsWebProcess());
119     if (!init(m_iOSOriginalRequest))
120         return false;
121     m_iOSOriginalRequest = ResourceRequest();
122     start();
123     return true;
124 }
125 #endif
126
127 CachedResource* SubresourceLoader::cachedResource()
128 {
129     return m_resource;
130 }
131
132 void SubresourceLoader::cancelIfNotFinishing()
133 {
134     if (m_state != Initialized)
135         return;
136
137     ResourceLoader::cancel();
138 }
139
140 bool SubresourceLoader::init(const ResourceRequest& request)
141 {
142     if (!ResourceLoader::init(request))
143         return false;
144
145     ASSERT(!reachedTerminalState());
146     m_state = Initialized;
147     m_documentLoader->addSubresourceLoader(this);
148     return true;
149 }
150
151 bool SubresourceLoader::isSubresourceLoader()
152 {
153     return true;
154 }
155
156 void SubresourceLoader::willSendRequestInternal(ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
157 {
158     // Store the previous URL because the call to ResourceLoader::willSendRequest will modify it.
159     URL previousURL = request().url();
160     Ref<SubresourceLoader> protect(*this);
161
162     ASSERT(!newRequest.isNull());
163     if (!redirectResponse.isNull()) {
164         // CachedResources are keyed off their original request URL.
165         // Requesting the same original URL a second time can redirect to a unique second resource.
166         // Therefore, if a redirect to a different destination URL occurs, we should no longer consider this a revalidation of the first resource.
167         // Doing so would have us reusing the resource from the first request if the second request's revalidation succeeds.
168         if (newRequest.isConditional() && m_resource->resourceToRevalidate() && newRequest.url() != m_resource->resourceToRevalidate()->response().url()) {
169             newRequest.makeUnconditional();
170             MemoryCache::singleton().revalidationFailed(*m_resource);
171             if (m_frame)
172                 m_frame->mainFrame().diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultFail, ShouldSample::Yes);
173         }
174         
175         if (!m_documentLoader->cachedResourceLoader().canRequest(m_resource->type(), newRequest.url(), options())) {
176             cancel();
177             return;
178         }
179         if (m_resource->isImage() && m_documentLoader->cachedResourceLoader().shouldDeferImageLoad(newRequest.url())) {
180             cancel();
181             return;
182         }
183         m_resource->redirectReceived(newRequest, redirectResponse);
184     }
185
186     if (newRequest.isNull() || reachedTerminalState())
187         return;
188
189     ResourceLoader::willSendRequestInternal(newRequest, redirectResponse);
190     if (newRequest.isNull())
191         cancel();
192 }
193
194 void SubresourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
195 {
196     ASSERT(m_state == Initialized);
197     Ref<SubresourceLoader> protect(*this);
198     m_resource->didSendData(bytesSent, totalBytesToBeSent);
199 }
200
201 void SubresourceLoader::didReceiveResponse(const ResourceResponse& response)
202 {
203     ASSERT(!response.isNull());
204     ASSERT(m_state == Initialized);
205
206     // Reference the object in this method since the additional processing can do
207     // anything including removing the last reference to this object; one example of this is 3266216.
208     Ref<SubresourceLoader> protect(*this);
209
210     if (shouldIncludeCertificateInfo())
211         response.includeCertificateInfo();
212
213     if (m_resource->resourceToRevalidate()) {
214         if (response.httpStatusCode() == 304) {
215             // 304 Not modified / Use local copy
216             // Existing resource is ok, just use it updating the expiration time.
217             m_resource->setResponse(response);
218             MemoryCache::singleton().revalidationSucceeded(*m_resource, response);
219             if (m_frame)
220                 m_frame->mainFrame().diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultPass, ShouldSample::Yes);
221             if (!reachedTerminalState())
222                 ResourceLoader::didReceiveResponse(response);
223             return;
224         }
225         // Did not get 304 response, continue as a regular resource load.
226         MemoryCache::singleton().revalidationFailed(*m_resource);
227         if (m_frame)
228             m_frame->mainFrame().diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultFail, ShouldSample::Yes);
229     }
230
231     m_resource->responseReceived(response);
232     if (reachedTerminalState())
233         return;
234
235     ResourceLoader::didReceiveResponse(response);
236     if (reachedTerminalState())
237         return;
238
239     // FIXME: Main resources have a different set of rules for multipart than images do.
240     // Hopefully we can merge those 2 paths.
241     if (response.isMultipart() && m_resource->type() != CachedResource::MainResource) {
242         m_loadingMultipartContent = true;
243
244         // We don't count multiParts in a CachedResourceLoader's request count
245         m_requestCountTracker = nullptr;
246         if (!m_resource->isImage()) {
247             cancel();
248             return;
249         }
250     }
251
252     auto* buffer = resourceData();
253     if (m_loadingMultipartContent && buffer && buffer->size()) {
254         // The resource data will change as the next part is loaded, so we need to make a copy.
255         m_resource->finishLoading(buffer->copy().ptr());
256         clearResourceData();
257         // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once.        
258         // After the first multipart section is complete, signal to delegates that this load is "finished" 
259         m_documentLoader->subresourceLoaderFinishedLoadingOnePart(this);
260         didFinishLoadingOnePart(0);
261     }
262
263     checkForHTTPStatusCodeError();
264 }
265
266 void SubresourceLoader::didReceiveData(const char* data, unsigned length, long long encodedDataLength, DataPayloadType dataPayloadType)
267 {
268     didReceiveDataOrBuffer(data, length, 0, encodedDataLength, dataPayloadType);
269 }
270
271 void SubresourceLoader::didReceiveBuffer(PassRefPtr<SharedBuffer> buffer, long long encodedDataLength, DataPayloadType dataPayloadType)
272 {
273     didReceiveDataOrBuffer(0, 0, buffer, encodedDataLength, dataPayloadType);
274 }
275
276 void SubresourceLoader::didReceiveDataOrBuffer(const char* data, int length, PassRefPtr<SharedBuffer> prpBuffer, long long encodedDataLength, DataPayloadType dataPayloadType)
277 {
278     if (m_resource->response().httpStatusCode() >= 400 && !m_resource->shouldIgnoreHTTPStatusCodeErrors())
279         return;
280     ASSERT(!m_resource->resourceToRevalidate());
281     ASSERT(!m_resource->errorOccurred());
282     ASSERT(m_state == Initialized);
283     // Reference the object in this method since the additional processing can do
284     // anything including removing the last reference to this object; one example of this is 3266216.
285     Ref<SubresourceLoader> protect(*this);
286     RefPtr<SharedBuffer> buffer = prpBuffer;
287     
288     ResourceLoader::didReceiveDataOrBuffer(data, length, buffer, encodedDataLength, dataPayloadType);
289
290     if (!m_loadingMultipartContent) {
291         if (auto* resourceData = this->resourceData())
292             m_resource->addDataBuffer(*resourceData);
293         else
294             m_resource->addData(buffer ? buffer->data() : data, buffer ? buffer->size() : length);
295     }
296 }
297
298 bool SubresourceLoader::checkForHTTPStatusCodeError()
299 {
300     if (m_resource->response().httpStatusCode() < 400 || m_resource->shouldIgnoreHTTPStatusCodeErrors())
301         return false;
302
303     m_state = Finishing;
304     m_resource->error(CachedResource::LoadError);
305     cancel();
306     return true;
307 }
308
309 static void logResourceLoaded(Frame* frame, CachedResource::Type type)
310 {
311     if (!frame)
312         return;
313
314     String resourceType;
315     switch (type) {
316     case CachedResource::MainResource:
317         resourceType = DiagnosticLoggingKeys::mainResourceKey();
318         break;
319     case CachedResource::ImageResource:
320         resourceType = DiagnosticLoggingKeys::imageKey();
321         break;
322 #if ENABLE(XSLT)
323     case CachedResource::XSLStyleSheet:
324 #endif
325     case CachedResource::CSSStyleSheet:
326         resourceType = DiagnosticLoggingKeys::styleSheetKey();
327         break;
328     case CachedResource::Script:
329         resourceType = DiagnosticLoggingKeys::scriptKey();
330         break;
331     case CachedResource::FontResource:
332 #if ENABLE(SVG_FONTS)
333     case CachedResource::SVGFontResource:
334 #endif
335         resourceType = DiagnosticLoggingKeys::fontKey();
336         break;
337     case CachedResource::RawResource:
338         resourceType = DiagnosticLoggingKeys::rawKey();
339         break;
340     case CachedResource::SVGDocumentResource:
341         resourceType = DiagnosticLoggingKeys::svgDocumentKey();
342         break;
343 #if ENABLE(LINK_PREFETCH)
344     case CachedResource::LinkPrefetch:
345     case CachedResource::LinkSubresource:
346 #endif
347 #if ENABLE(VIDEO_TRACK)
348     case CachedResource::TextTrackResource:
349 #endif
350         resourceType = DiagnosticLoggingKeys::otherKey();
351         break;
352     }
353     frame->mainFrame().diagnosticLoggingClient().logDiagnosticMessageWithValue(DiagnosticLoggingKeys::resourceKey(), DiagnosticLoggingKeys::loadedKey(), resourceType, ShouldSample::Yes);
354 }
355
356 void SubresourceLoader::didFinishLoading(double finishTime)
357 {
358     if (m_state != Initialized)
359         return;
360     ASSERT(!reachedTerminalState());
361     ASSERT(!m_resource->resourceToRevalidate());
362     // FIXME (129394): We should cancel the load when a decode error occurs instead of continuing the load to completion.
363     ASSERT(!m_resource->errorOccurred() || m_resource->status() == CachedResource::DecodeError);
364     LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data());
365     logResourceLoaded(m_frame.get(), m_resource->type());
366
367     Ref<SubresourceLoader> protect(*this);
368     CachedResourceHandle<CachedResource> protectResource(m_resource);
369
370     m_state = Finishing;
371     m_resource->setLoadFinishTime(finishTime);
372     m_resource->finishLoading(resourceData());
373
374     if (wasCancelled())
375         return;
376     m_resource->finish();
377     ASSERT(!reachedTerminalState());
378     didFinishLoadingOnePart(finishTime);
379     notifyDone();
380     if (reachedTerminalState())
381         return;
382     releaseResources();
383 }
384
385 void SubresourceLoader::didFail(const ResourceError& error)
386 {
387     if (m_state != Initialized)
388         return;
389     ASSERT(!reachedTerminalState());
390     LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string().latin1().data());
391
392     Ref<SubresourceLoader> protect(*this);
393     CachedResourceHandle<CachedResource> protectResource(m_resource);
394     m_state = Finishing;
395     if (m_resource->resourceToRevalidate())
396         MemoryCache::singleton().revalidationFailed(*m_resource);
397     m_resource->setResourceError(error);
398     if (!m_resource->isPreloaded())
399         MemoryCache::singleton().remove(*m_resource);
400     m_resource->error(CachedResource::LoadError);
401     cleanupForError(error);
402     notifyDone();
403     if (reachedTerminalState())
404         return;
405     releaseResources();
406 }
407
408 void SubresourceLoader::willCancel(const ResourceError& error)
409 {
410 #if PLATFORM(IOS)
411     // Since we defer initialization to scheduling time on iOS but
412     // CachedResourceLoader stores resources in the memory cache immediately,
413     // m_resource might be cached despite its loader not being initialized.
414     if (m_state != Initialized && m_state != Uninitialized)
415 #else
416     if (m_state != Initialized)
417 #endif
418         return;
419     ASSERT(!reachedTerminalState());
420     LOG(ResourceLoading, "Cancelled load of '%s'.\n", m_resource->url().string().latin1().data());
421
422     Ref<SubresourceLoader> protect(*this);
423 #if PLATFORM(IOS)
424     m_state = m_state == Uninitialized ? CancelledWhileInitializing : Finishing;
425 #else
426     m_state = Finishing;
427 #endif
428     auto& memoryCache = MemoryCache::singleton();
429     if (m_resource->resourceToRevalidate())
430         memoryCache.revalidationFailed(*m_resource);
431     m_resource->setResourceError(error);
432     memoryCache.remove(*m_resource);
433 }
434
435 void SubresourceLoader::didCancel(const ResourceError&)
436 {
437     if (m_state == Uninitialized)
438         return;
439
440     m_resource->cancelLoad();
441     notifyDone();
442 }
443
444 void SubresourceLoader::notifyDone()
445 {
446     if (reachedTerminalState())
447         return;
448
449     m_requestCountTracker = nullptr;
450 #if PLATFORM(IOS)
451     m_documentLoader->cachedResourceLoader().loadDone(m_resource, m_state != CancelledWhileInitializing);
452 #else
453     m_documentLoader->cachedResourceLoader().loadDone(m_resource);
454 #endif
455     if (reachedTerminalState())
456         return;
457     m_documentLoader->removeSubresourceLoader(this);
458 }
459
460 void SubresourceLoader::releaseResources()
461 {
462     ASSERT(!reachedTerminalState());
463 #if PLATFORM(IOS)
464     if (m_state != Uninitialized && m_state != CancelledWhileInitializing)
465 #else
466     if (m_state != Uninitialized)
467 #endif
468         m_resource->clearLoader();
469     m_resource = 0;
470     ResourceLoader::releaseResources();
471 }
472
473 }