| OLD | NEW |
| (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 | |
| OLD | NEW |