Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(163)

Side by Side Diff: third_party/WebKit/Source/core/fetch/ResourceLoader.cpp

Issue 2584423002: Loading: move core/fetch to platform/loader/fetch (Closed)
Patch Set: rebase Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2006, 2007, 2010, 2011 Apple Inc. All rights reserved.
3 * (C) 2007 Graham Dennis (graham.dennis@gmail.com)
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "core/fetch/ResourceLoader.h"
31
32 #include "core/fetch/CrossOriginAccessControl.h"
33 #include "core/fetch/FetchContext.h"
34 #include "core/fetch/Resource.h"
35 #include "core/fetch/ResourceFetcher.h"
36 #include "platform/SharedBuffer.h"
37 #include "platform/exported/WrappedResourceRequest.h"
38 #include "platform/exported/WrappedResourceResponse.h"
39 #include "platform/network/NetworkInstrumentation.h"
40 #include "platform/network/ResourceError.h"
41 #include "public/platform/Platform.h"
42 #include "public/platform/WebCachePolicy.h"
43 #include "public/platform/WebData.h"
44 #include "public/platform/WebURLError.h"
45 #include "public/platform/WebURLRequest.h"
46 #include "public/platform/WebURLResponse.h"
47 #include "wtf/Assertions.h"
48 #include "wtf/CurrentTime.h"
49 #include "wtf/PtrUtil.h"
50 #include "wtf/text/StringBuilder.h"
51 #include <memory>
52
53 namespace blink {
54
55 ResourceLoader* ResourceLoader::create(ResourceFetcher* fetcher,
56 Resource* resource) {
57 return new ResourceLoader(fetcher, resource);
58 }
59
60 ResourceLoader::ResourceLoader(ResourceFetcher* fetcher, Resource* resource)
61 : m_fetcher(fetcher),
62 m_resource(resource),
63 m_isCacheAwareLoadingActivated(false) {
64 DCHECK(m_resource);
65 DCHECK(m_fetcher);
66
67 m_resource->setLoader(this);
68 }
69
70 ResourceLoader::~ResourceLoader() {}
71
72 DEFINE_TRACE(ResourceLoader) {
73 visitor->trace(m_fetcher);
74 visitor->trace(m_resource);
75 }
76
77 void ResourceLoader::start(const ResourceRequest& request) {
78 DCHECK(!m_loader);
79
80 if (m_resource->options().synchronousPolicy == RequestSynchronously &&
81 context().defersLoading()) {
82 cancel();
83 return;
84 }
85
86 m_loader = WTF::wrapUnique(Platform::current()->createURLLoader());
87 DCHECK(m_loader);
88 m_loader->setDefersLoading(context().defersLoading());
89 m_loader->setLoadingTaskRunner(context().loadingTaskRunner().get());
90
91 if (m_isCacheAwareLoadingActivated) {
92 // Override cache policy for cache-aware loading. If this request fails, a
93 // reload with original request will be triggered in didFail().
94 ResourceRequest cacheAwareRequest(request);
95 cacheAwareRequest.setCachePolicy(WebCachePolicy::ReturnCacheDataIfValid);
96 m_loader->loadAsynchronously(WrappedResourceRequest(cacheAwareRequest),
97 this);
98 return;
99 }
100
101 if (m_resource->options().synchronousPolicy == RequestSynchronously)
102 requestSynchronously(request);
103 else
104 m_loader->loadAsynchronously(WrappedResourceRequest(request), this);
105 }
106
107 void ResourceLoader::restart(const ResourceRequest& request) {
108 CHECK_EQ(m_resource->options().synchronousPolicy, RequestAsynchronously);
109
110 m_loader.reset();
111 start(request);
112 }
113
114 void ResourceLoader::setDefersLoading(bool defers) {
115 DCHECK(m_loader);
116
117 m_loader->setDefersLoading(defers);
118 }
119
120 void ResourceLoader::didChangePriority(ResourceLoadPriority loadPriority,
121 int intraPriorityValue) {
122 if (m_loader) {
123 m_loader->didChangePriority(
124 static_cast<WebURLRequest::Priority>(loadPriority), intraPriorityValue);
125 }
126 }
127
128 void ResourceLoader::cancel() {
129 handleError(
130 ResourceError::cancelledError(m_resource->lastResourceRequest().url()));
131 }
132
133 void ResourceLoader::cancelForRedirectAccessCheckError(
134 const KURL& newURL,
135 ResourceRequestBlockedReason blockedReason) {
136 m_resource->willNotFollowRedirect();
137
138 if (m_loader)
139 handleError(
140 ResourceError::cancelledDueToAccessCheckError(newURL, blockedReason));
141 }
142
143 static bool isManualRedirectFetchRequest(const ResourceRequest& request) {
144 return request.fetchRedirectMode() ==
145 WebURLRequest::FetchRedirectModeManual &&
146 request.requestContext() == WebURLRequest::RequestContextFetch;
147 }
148
149 bool ResourceLoader::willFollowRedirect(
150 WebURLRequest& passedNewRequest,
151 const WebURLResponse& passedRedirectResponse) {
152 DCHECK(!passedNewRequest.isNull());
153 DCHECK(!passedRedirectResponse.isNull());
154
155 if (m_isCacheAwareLoadingActivated) {
156 // Fail as cache miss if cached response is a redirect.
157 handleError(
158 ResourceError::cacheMissError(m_resource->lastResourceRequest().url()));
159 return false;
160 }
161
162 ResourceRequest& newRequest(passedNewRequest.toMutableResourceRequest());
163 const ResourceResponse& redirectResponse(
164 passedRedirectResponse.toResourceResponse());
165
166 newRequest.setRedirectStatus(
167 ResourceRequest::RedirectStatus::FollowedRedirect);
168
169 const KURL originalURL = newRequest.url();
170
171 if (!isManualRedirectFetchRequest(m_resource->resourceRequest())) {
172 ResourceRequestBlockedReason blockedReason = context().canRequest(
173 m_resource->getType(), newRequest, newRequest.url(),
174 m_resource->options(), m_resource->isUnusedPreload(),
175 FetchRequest::UseDefaultOriginRestrictionForType);
176 if (blockedReason != ResourceRequestBlockedReason::None) {
177 cancelForRedirectAccessCheckError(newRequest.url(), blockedReason);
178 return false;
179 }
180
181 if (m_resource->options().corsEnabled == IsCORSEnabled) {
182 RefPtr<SecurityOrigin> sourceOrigin =
183 m_resource->options().securityOrigin;
184 if (!sourceOrigin.get())
185 sourceOrigin = context().getSecurityOrigin();
186
187 String errorMessage;
188 StoredCredentials withCredentials =
189 m_resource->lastResourceRequest().allowStoredCredentials()
190 ? AllowStoredCredentials
191 : DoNotAllowStoredCredentials;
192 if (!CrossOriginAccessControl::handleRedirect(
193 sourceOrigin, newRequest, redirectResponse, withCredentials,
194 m_resource->mutableOptions(), errorMessage)) {
195 m_resource->setCORSFailed();
196 context().addConsoleMessage(errorMessage);
197 cancelForRedirectAccessCheckError(newRequest.url(),
198 ResourceRequestBlockedReason::Other);
199 return false;
200 }
201 }
202 if (m_resource->getType() == Resource::Image &&
203 m_fetcher->shouldDeferImageLoad(newRequest.url())) {
204 cancelForRedirectAccessCheckError(newRequest.url(),
205 ResourceRequestBlockedReason::Other);
206 return false;
207 }
208 }
209
210 bool crossOrigin = !SecurityOrigin::areSameSchemeHostPort(
211 redirectResponse.url(), newRequest.url());
212 m_fetcher->recordResourceTimingOnRedirect(m_resource.get(), redirectResponse,
213 crossOrigin);
214
215 newRequest.setAllowStoredCredentials(m_resource->options().allowCredentials ==
216 AllowStoredCredentials);
217
218 context().dispatchWillSendRequest(m_resource->identifier(), newRequest,
219 redirectResponse,
220 m_resource->options().initiatorInfo);
221
222 // ResourceFetcher::willFollowRedirect() may rewrite the URL to
223 // something else not for rejecting redirect but for other reasons.
224 // E.g. WebFrameTestClient::willSendRequest() and
225 // RenderFrameImpl::willSendRequest(). We should reflect the
226 // rewriting but currently we cannot. So, return false to make the
227 // redirect fail.
228 if (newRequest.url() != originalURL) {
229 cancelForRedirectAccessCheckError(newRequest.url(),
230 ResourceRequestBlockedReason::Other);
231 return false;
232 }
233
234 if (!m_resource->willFollowRedirect(newRequest, redirectResponse)) {
235 cancelForRedirectAccessCheckError(newRequest.url(),
236 ResourceRequestBlockedReason::Other);
237 return false;
238 }
239
240 return true;
241 }
242
243 void ResourceLoader::didReceiveCachedMetadata(const char* data, int length) {
244 m_resource->setSerializedCachedMetadata(data, length);
245 }
246
247 void ResourceLoader::didSendData(unsigned long long bytesSent,
248 unsigned long long totalBytesToBeSent) {
249 m_resource->didSendData(bytesSent, totalBytesToBeSent);
250 }
251
252 FetchContext& ResourceLoader::context() const {
253 return m_fetcher->context();
254 }
255
256 ResourceRequestBlockedReason ResourceLoader::canAccessResponse(
257 Resource* resource,
258 const ResourceResponse& response) const {
259 // Redirects can change the response URL different from one of request.
260 bool forPreload = resource->isUnusedPreload();
261 ResourceRequestBlockedReason blockedReason =
262 context().canRequest(resource->getType(), resource->resourceRequest(),
263 response.url(), resource->options(), forPreload,
264 FetchRequest::UseDefaultOriginRestrictionForType);
265 if (blockedReason != ResourceRequestBlockedReason::None)
266 return blockedReason;
267
268 SecurityOrigin* sourceOrigin = resource->options().securityOrigin.get();
269 if (!sourceOrigin)
270 sourceOrigin = context().getSecurityOrigin();
271
272 if (sourceOrigin->canRequestNoSuborigin(response.url()))
273 return ResourceRequestBlockedReason::None;
274
275 // Use the original response instead of the 304 response for a successful
276 // revaldiation.
277 const ResourceResponse& responseForAccessControl =
278 (resource->isCacheValidator() && response.httpStatusCode() == 304)
279 ? resource->response()
280 : response;
281
282 CrossOriginAccessControl::AccessStatus corsStatus =
283 CrossOriginAccessControl::checkAccess(
284 responseForAccessControl, resource->options().allowCredentials,
285 sourceOrigin);
286 if (corsStatus != CrossOriginAccessControl::kAccessAllowed) {
287 resource->setCORSFailed();
288 if (!forPreload) {
289 String resourceType = Resource::resourceTypeToString(
290 resource->getType(), resource->options().initiatorInfo.name);
291 StringBuilder builder;
292 builder.append("Access to ");
293 builder.append(resourceType);
294 builder.append(" at '");
295 builder.append(response.url().getString());
296 builder.append("' from origin '");
297 builder.append(sourceOrigin->toString());
298 builder.append("' has been blocked by CORS policy: ");
299 CrossOriginAccessControl::accessControlErrorString(
300 builder, corsStatus, responseForAccessControl, sourceOrigin,
301 resource->lastResourceRequest().requestContext());
302 context().addConsoleMessage(builder.toString());
303 }
304 return ResourceRequestBlockedReason::Other;
305 }
306 return ResourceRequestBlockedReason::None;
307 }
308
309 void ResourceLoader::didReceiveResponse(
310 const WebURLResponse& webURLResponse,
311 std::unique_ptr<WebDataConsumerHandle> handle) {
312 DCHECK(!webURLResponse.isNull());
313
314 const ResourceResponse& response = webURLResponse.toResourceResponse();
315
316 if (response.wasFetchedViaServiceWorker()) {
317 if (m_resource->options().corsEnabled == IsCORSEnabled &&
318 response.wasFallbackRequiredByServiceWorker()) {
319 ResourceRequest request = m_resource->lastResourceRequest();
320 DCHECK_EQ(request.skipServiceWorker(),
321 WebURLRequest::SkipServiceWorker::None);
322 // This code handles the case when a regular controlling service worker
323 // doesn't handle a cross origin request. When this happens we still want
324 // to give foreign fetch a chance to handle the request, so only skip the
325 // controlling service worker for the fallback request. This is currently
326 // safe because of http://crbug.com/604084 the
327 // wasFallbackRequiredByServiceWorker flag is never set when foreign fetch
328 // handled a request.
329 if (!context().shouldLoadNewResource(m_resource->getType())) {
330 // Cancel the request if we should not trigger a reload now.
331 handleError(ResourceError::cancelledError(response.url()));
332 return;
333 }
334 request.setSkipServiceWorker(
335 WebURLRequest::SkipServiceWorker::Controlling);
336 restart(request);
337 return;
338 }
339
340 // If the response is fetched via ServiceWorker, the original URL of the
341 // response could be different from the URL of the request. We check the URL
342 // not to load the resources which are forbidden by the page CSP.
343 // https://w3c.github.io/webappsec-csp/#should-block-response
344 const KURL& originalURL = response.originalURLViaServiceWorker();
345 if (!originalURL.isEmpty()) {
346 ResourceRequestBlockedReason blockedReason = context().allowResponse(
347 m_resource->getType(), m_resource->resourceRequest(), originalURL,
348 m_resource->options());
349 if (blockedReason != ResourceRequestBlockedReason::None) {
350 handleError(ResourceError::cancelledDueToAccessCheckError(
351 originalURL, blockedReason));
352 return;
353 }
354 }
355 } else if (m_resource->options().corsEnabled == IsCORSEnabled) {
356 ResourceRequestBlockedReason blockedReason =
357 canAccessResponse(m_resource, response);
358 if (blockedReason != ResourceRequestBlockedReason::None) {
359 handleError(ResourceError::cancelledDueToAccessCheckError(response.url(),
360 blockedReason));
361 return;
362 }
363 }
364
365 context().dispatchDidReceiveResponse(
366 m_resource->identifier(), response,
367 m_resource->resourceRequest().frameType(),
368 m_resource->resourceRequest().requestContext(), m_resource);
369
370 m_resource->responseReceived(response, std::move(handle));
371 if (!m_resource->loader())
372 return;
373
374 if (response.httpStatusCode() >= 400 &&
375 !m_resource->shouldIgnoreHTTPStatusCodeErrors())
376 handleError(ResourceError::cancelledError(response.url()));
377 }
378
379 void ResourceLoader::didReceiveResponse(const WebURLResponse& response) {
380 didReceiveResponse(response, nullptr);
381 }
382
383 void ResourceLoader::didDownloadData(int length, int encodedDataLength) {
384 context().dispatchDidDownloadData(m_resource->identifier(), length,
385 encodedDataLength);
386 m_resource->didDownloadData(length);
387 }
388
389 void ResourceLoader::didReceiveData(const char* data, int length) {
390 CHECK_GE(length, 0);
391
392 context().dispatchDidReceiveData(m_resource->identifier(), data, length);
393 m_resource->addToDecodedBodyLength(length);
394 m_resource->appendData(data, length);
395 }
396
397 void ResourceLoader::didReceiveTransferSizeUpdate(int transferSizeDiff) {
398 DCHECK_GT(transferSizeDiff, 0);
399 context().dispatchDidReceiveEncodedData(m_resource->identifier(),
400 transferSizeDiff);
401 }
402
403 void ResourceLoader::didFinishLoadingFirstPartInMultipart() {
404 network_instrumentation::endResourceLoad(
405 m_resource->identifier(),
406 network_instrumentation::RequestOutcome::Success);
407
408 m_fetcher->handleLoaderFinish(m_resource.get(), 0,
409 ResourceFetcher::DidFinishFirstPartInMultipart);
410 }
411
412 void ResourceLoader::didFinishLoading(double finishTime,
413 int64_t encodedDataLength,
414 int64_t encodedBodyLength) {
415 m_resource->setEncodedDataLength(encodedDataLength);
416 m_resource->addToEncodedBodyLength(encodedBodyLength);
417
418 m_loader.reset();
419
420 network_instrumentation::endResourceLoad(
421 m_resource->identifier(),
422 network_instrumentation::RequestOutcome::Success);
423
424 m_fetcher->handleLoaderFinish(m_resource.get(), finishTime,
425 ResourceFetcher::DidFinishLoading);
426 }
427
428 void ResourceLoader::didFail(const WebURLError& error,
429 int64_t encodedDataLength,
430 int64_t encodedBodyLength) {
431 m_resource->setEncodedDataLength(encodedDataLength);
432 m_resource->addToEncodedBodyLength(encodedBodyLength);
433 handleError(error);
434 }
435
436 void ResourceLoader::handleError(const ResourceError& error) {
437 if (m_isCacheAwareLoadingActivated && error.isCacheMiss() &&
438 context().shouldLoadNewResource(m_resource->getType())) {
439 m_resource->willReloadAfterDiskCacheMiss();
440 m_isCacheAwareLoadingActivated = false;
441 restart(m_resource->resourceRequest());
442 return;
443 }
444
445 m_loader.reset();
446
447 network_instrumentation::endResourceLoad(
448 m_resource->identifier(), network_instrumentation::RequestOutcome::Fail);
449
450 m_fetcher->handleLoaderError(m_resource.get(), error);
451 }
452
453 void ResourceLoader::requestSynchronously(const ResourceRequest& request) {
454 // downloadToFile is not supported for synchronous requests.
455 DCHECK(!request.downloadToFile());
456 DCHECK(m_loader);
457 DCHECK_EQ(request.priority(), ResourceLoadPriorityHighest);
458
459 WrappedResourceRequest requestIn(request);
460 WebURLResponse responseOut;
461 WebURLError errorOut;
462 WebData dataOut;
463 int64_t encodedDataLength = WebURLLoaderClient::kUnknownEncodedDataLength;
464 int64_t encodedBodyLength = 0;
465 m_loader->loadSynchronously(requestIn, responseOut, errorOut, dataOut,
466 encodedDataLength, encodedBodyLength);
467
468 // A message dispatched while synchronously fetching the resource
469 // can bring about the cancellation of this load.
470 if (!m_loader)
471 return;
472 if (errorOut.reason) {
473 didFail(errorOut, encodedDataLength, encodedBodyLength);
474 return;
475 }
476 didReceiveResponse(responseOut);
477 if (!m_loader)
478 return;
479 DCHECK_GE(responseOut.toResourceResponse().encodedBodyLength(), 0);
480
481 // Follow the async case convention of not calling didReceiveData or
482 // appending data to m_resource if the response body is empty. Copying the
483 // empty buffer is a noop in most cases, but is destructive in the case of
484 // a 304, where it will overwrite the cached data we should be reusing.
485 if (dataOut.size()) {
486 context().dispatchDidReceiveData(m_resource->identifier(), dataOut.data(),
487 dataOut.size());
488 m_resource->setResourceBuffer(dataOut);
489 }
490 didFinishLoading(monotonicallyIncreasingTime(), encodedDataLength,
491 encodedBodyLength);
492 }
493
494 void ResourceLoader::dispose() {
495 m_loader = nullptr;
496 }
497
498 void ResourceLoader::activateCacheAwareLoadingIfNeeded(
499 const ResourceRequest& request) {
500 DCHECK(!m_isCacheAwareLoadingActivated);
501
502 if (m_resource->options().cacheAwareLoadingEnabled !=
503 IsCacheAwareLoadingEnabled)
504 return;
505
506 // Synchronous requests are not supported.
507 if (m_resource->options().synchronousPolicy == RequestSynchronously)
508 return;
509
510 // Don't activate on Resource revalidation.
511 if (m_resource->isCacheValidator())
512 return;
513
514 // Don't activate if cache policy is explicitly set.
515 if (request.getCachePolicy() != WebCachePolicy::UseProtocolCachePolicy)
516 return;
517
518 m_isCacheAwareLoadingActivated = true;
519 }
520
521 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698