Cross-protocol, cross-site scripting (XPSS) using HTML forms
[WebKit-https.git] / Source / WebCore / loader / SubresourceLoader.cpp
1 /*
2  * Copyright (C) 2006-2007, 2009, 2016 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 (response.isHttpVersion0_9()) {
214         if (m_frame) {
215             String message = "Sandboxing '" + response.url().string() + "' because it is using HTTP/0.9.";
216             m_frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message, identifier());
217             frameLoader()->forceSandboxFlags(SandboxScripts | SandboxPlugins);
218         }
219     }
220
221     if (m_resource->resourceToRevalidate()) {
222         if (response.httpStatusCode() == 304) {
223             // 304 Not modified / Use local copy
224             // Existing resource is ok, just use it updating the expiration time.
225             m_resource->setResponse(response);
226             MemoryCache::singleton().revalidationSucceeded(*m_resource, response);
227             if (m_frame)
228                 m_frame->mainFrame().diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultPass, ShouldSample::Yes);
229             if (!reachedTerminalState())
230                 ResourceLoader::didReceiveResponse(response);
231             return;
232         }
233         // Did not get 304 response, continue as a regular resource load.
234         MemoryCache::singleton().revalidationFailed(*m_resource);
235         if (m_frame)
236             m_frame->mainFrame().diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultFail, ShouldSample::Yes);
237     }
238
239     m_resource->responseReceived(response);
240     if (reachedTerminalState())
241         return;
242
243     ResourceLoader::didReceiveResponse(response);
244     if (reachedTerminalState())
245         return;
246
247     // FIXME: Main resources have a different set of rules for multipart than images do.
248     // Hopefully we can merge those 2 paths.
249     if (response.isMultipart() && m_resource->type() != CachedResource::MainResource) {
250         m_loadingMultipartContent = true;
251
252         // We don't count multiParts in a CachedResourceLoader's request count
253         m_requestCountTracker = nullptr;
254         if (!m_resource->isImage()) {
255             cancel();
256             return;
257         }
258     }
259
260     auto* buffer = resourceData();
261     if (m_loadingMultipartContent && buffer && buffer->size()) {
262         // The resource data will change as the next part is loaded, so we need to make a copy.
263         m_resource->finishLoading(buffer->copy().ptr());
264         clearResourceData();
265         // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once.        
266         // After the first multipart section is complete, signal to delegates that this load is "finished" 
267         m_documentLoader->subresourceLoaderFinishedLoadingOnePart(this);
268         didFinishLoadingOnePart(0);
269     }
270
271     checkForHTTPStatusCodeError();
272 }
273
274 void SubresourceLoader::didReceiveData(const char* data, unsigned length, long long encodedDataLength, DataPayloadType dataPayloadType)
275 {
276     didReceiveDataOrBuffer(data, length, 0, encodedDataLength, dataPayloadType);
277 }
278
279 void SubresourceLoader::didReceiveBuffer(PassRefPtr<SharedBuffer> buffer, long long encodedDataLength, DataPayloadType dataPayloadType)
280 {
281     didReceiveDataOrBuffer(0, 0, buffer, encodedDataLength, dataPayloadType);
282 }
283
284 void SubresourceLoader::didReceiveDataOrBuffer(const char* data, int length, PassRefPtr<SharedBuffer> prpBuffer, long long encodedDataLength, DataPayloadType dataPayloadType)
285 {
286     if (m_resource->response().httpStatusCode() >= 400 && !m_resource->shouldIgnoreHTTPStatusCodeErrors())
287         return;
288     ASSERT(!m_resource->resourceToRevalidate());
289     ASSERT(!m_resource->errorOccurred());
290     ASSERT(m_state == Initialized);
291     // Reference the object in this method since the additional processing can do
292     // anything including removing the last reference to this object; one example of this is 3266216.
293     Ref<SubresourceLoader> protect(*this);
294     RefPtr<SharedBuffer> buffer = prpBuffer;
295     
296     ResourceLoader::didReceiveDataOrBuffer(data, length, buffer, encodedDataLength, dataPayloadType);
297
298     if (!m_loadingMultipartContent) {
299         if (auto* resourceData = this->resourceData())
300             m_resource->addDataBuffer(*resourceData);
301         else
302             m_resource->addData(buffer ? buffer->data() : data, buffer ? buffer->size() : length);
303     }
304 }
305
306 bool SubresourceLoader::checkForHTTPStatusCodeError()
307 {
308     if (m_resource->response().httpStatusCode() < 400 || m_resource->shouldIgnoreHTTPStatusCodeErrors())
309         return false;
310
311     m_state = Finishing;
312     m_resource->error(CachedResource::LoadError);
313     cancel();
314     return true;
315 }
316
317 static void logResourceLoaded(Frame* frame, CachedResource::Type type)
318 {
319     if (!frame)
320         return;
321
322     String resourceType;
323     switch (type) {
324     case CachedResource::MainResource:
325         resourceType = DiagnosticLoggingKeys::mainResourceKey();
326         break;
327     case CachedResource::ImageResource:
328         resourceType = DiagnosticLoggingKeys::imageKey();
329         break;
330 #if ENABLE(XSLT)
331     case CachedResource::XSLStyleSheet:
332 #endif
333     case CachedResource::CSSStyleSheet:
334         resourceType = DiagnosticLoggingKeys::styleSheetKey();
335         break;
336     case CachedResource::Script:
337         resourceType = DiagnosticLoggingKeys::scriptKey();
338         break;
339     case CachedResource::FontResource:
340 #if ENABLE(SVG_FONTS)
341     case CachedResource::SVGFontResource:
342 #endif
343         resourceType = DiagnosticLoggingKeys::fontKey();
344         break;
345     case CachedResource::RawResource:
346         resourceType = DiagnosticLoggingKeys::rawKey();
347         break;
348     case CachedResource::SVGDocumentResource:
349         resourceType = DiagnosticLoggingKeys::svgDocumentKey();
350         break;
351 #if ENABLE(LINK_PREFETCH)
352     case CachedResource::LinkPrefetch:
353     case CachedResource::LinkSubresource:
354 #endif
355 #if ENABLE(VIDEO_TRACK)
356     case CachedResource::TextTrackResource:
357 #endif
358         resourceType = DiagnosticLoggingKeys::otherKey();
359         break;
360     }
361     frame->mainFrame().diagnosticLoggingClient().logDiagnosticMessageWithValue(DiagnosticLoggingKeys::resourceKey(), DiagnosticLoggingKeys::loadedKey(), resourceType, ShouldSample::Yes);
362 }
363
364 void SubresourceLoader::didFinishLoading(double finishTime)
365 {
366     if (m_state != Initialized)
367         return;
368     ASSERT(!reachedTerminalState());
369     ASSERT(!m_resource->resourceToRevalidate());
370     // FIXME (129394): We should cancel the load when a decode error occurs instead of continuing the load to completion.
371     ASSERT(!m_resource->errorOccurred() || m_resource->status() == CachedResource::DecodeError);
372     LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data());
373     logResourceLoaded(m_frame.get(), m_resource->type());
374
375     Ref<SubresourceLoader> protect(*this);
376     CachedResourceHandle<CachedResource> protectResource(m_resource);
377
378     m_state = Finishing;
379     m_resource->setLoadFinishTime(finishTime);
380     m_resource->finishLoading(resourceData());
381
382     if (wasCancelled())
383         return;
384     m_resource->finish();
385     ASSERT(!reachedTerminalState());
386     didFinishLoadingOnePart(finishTime);
387     notifyDone();
388     if (reachedTerminalState())
389         return;
390     releaseResources();
391 }
392
393 void SubresourceLoader::didFail(const ResourceError& error)
394 {
395     if (m_state != Initialized)
396         return;
397     ASSERT(!reachedTerminalState());
398     LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string().latin1().data());
399
400     Ref<SubresourceLoader> protect(*this);
401     CachedResourceHandle<CachedResource> protectResource(m_resource);
402     m_state = Finishing;
403     if (m_resource->resourceToRevalidate())
404         MemoryCache::singleton().revalidationFailed(*m_resource);
405     m_resource->setResourceError(error);
406     if (!m_resource->isPreloaded())
407         MemoryCache::singleton().remove(*m_resource);
408     m_resource->error(CachedResource::LoadError);
409     cleanupForError(error);
410     notifyDone();
411     if (reachedTerminalState())
412         return;
413     releaseResources();
414 }
415
416 void SubresourceLoader::willCancel(const ResourceError& error)
417 {
418 #if PLATFORM(IOS)
419     // Since we defer initialization to scheduling time on iOS but
420     // CachedResourceLoader stores resources in the memory cache immediately,
421     // m_resource might be cached despite its loader not being initialized.
422     if (m_state != Initialized && m_state != Uninitialized)
423 #else
424     if (m_state != Initialized)
425 #endif
426         return;
427     ASSERT(!reachedTerminalState());
428     LOG(ResourceLoading, "Cancelled load of '%s'.\n", m_resource->url().string().latin1().data());
429
430     Ref<SubresourceLoader> protect(*this);
431 #if PLATFORM(IOS)
432     m_state = m_state == Uninitialized ? CancelledWhileInitializing : Finishing;
433 #else
434     m_state = Finishing;
435 #endif
436     auto& memoryCache = MemoryCache::singleton();
437     if (m_resource->resourceToRevalidate())
438         memoryCache.revalidationFailed(*m_resource);
439     m_resource->setResourceError(error);
440     memoryCache.remove(*m_resource);
441 }
442
443 void SubresourceLoader::didCancel(const ResourceError&)
444 {
445     if (m_state == Uninitialized)
446         return;
447
448     m_resource->cancelLoad();
449     notifyDone();
450 }
451
452 void SubresourceLoader::notifyDone()
453 {
454     if (reachedTerminalState())
455         return;
456
457     m_requestCountTracker = nullptr;
458 #if PLATFORM(IOS)
459     m_documentLoader->cachedResourceLoader().loadDone(m_resource, m_state != CancelledWhileInitializing);
460 #else
461     m_documentLoader->cachedResourceLoader().loadDone(m_resource);
462 #endif
463     if (reachedTerminalState())
464         return;
465     m_documentLoader->removeSubresourceLoader(this);
466 }
467
468 void SubresourceLoader::releaseResources()
469 {
470     ASSERT(!reachedTerminalState());
471 #if PLATFORM(IOS)
472     if (m_state != Uninitialized && m_state != CancelledWhileInitializing)
473 #else
474     if (m_state != Uninitialized)
475 #endif
476         m_resource->clearLoader();
477     m_resource = nullptr;
478     ResourceLoader::releaseResources();
479 }
480
481 }