1 /* 2 * Copyright (C) 2011, 2012 Google Inc. All rights reserved. 3 * Copyright (C) 2013, Intel Corporation 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include "config.h" 33 #include "core/loader/DocumentThreadableLoader.h" 34 35 #include "core/dom/Document.h" 36 #include "core/fetch/CrossOriginAccessControl.h" 37 #include "core/fetch/FetchRequest.h" 38 #include "core/fetch/FetchUtils.h" 39 #include "core/fetch/Resource.h" 40 #include "core/fetch/ResourceFetcher.h" 41 #include "core/frame/FrameConsole.h" 42 #include "core/frame/LocalFrame.h" 43 #include "core/frame/csp/ContentSecurityPolicy.h" 44 #include "core/inspector/InspectorInstrumentation.h" 45 #include "core/inspector/InspectorTraceEvents.h" 46 #include "core/loader/CrossOriginPreflightResultCache.h" 47 #include "core/loader/DocumentThreadableLoaderClient.h" 48 #include "core/loader/FrameLoader.h" 49 #include "core/loader/ThreadableLoaderClient.h" 50 #include "platform/SharedBuffer.h" 51 #include "platform/network/ResourceRequest.h" 52 #include "platform/weborigin/SchemeRegistry.h" 53 #include "platform/weborigin/SecurityOrigin.h" 54 #include "public/platform/WebURLRequest.h" 55 #include "wtf/Assertions.h" 56 57 namespace blink { 58 59 void DocumentThreadableLoader::loadResourceSynchronously(Document& document, const ResourceRequest& request, ThreadableLoaderClient& client, const ThreadableLoaderOptions& options, const ResourceLoaderOptions& resourceLoaderOptions) 60 { 61 // The loader will be deleted as soon as this function exits. 62 RefPtr<DocumentThreadableLoader> loader = adoptRef(new DocumentThreadableLoader(document, &client, LoadSynchronously, request, options, resourceLoaderOptions)); 63 ASSERT(loader->hasOneRef()); 64 } 65 66 PassRefPtr<DocumentThreadableLoader> DocumentThreadableLoader::create(Document& document, ThreadableLoaderClient* client, const ResourceRequest& request, const ThreadableLoaderOptions& options, const ResourceLoaderOptions& resourceLoaderOptions) 67 { 68 RefPtr<DocumentThreadableLoader> loader = adoptRef(new DocumentThreadableLoader(document, client, LoadAsynchronously, request, options, resourceLoaderOptions)); 69 if (!loader->resource()) 70 loader = nullptr; 71 return loader.release(); 72 } 73 74 DocumentThreadableLoader::DocumentThreadableLoader(Document& document, ThreadableLoaderClient* client, BlockingBehavior blockingBehavior, const ResourceRequest& request, const ThreadableLoaderOptions& options, const ResourceLoaderOptions& resourceLoaderOptions) 75 : m_client(client) 76 , m_document(document) 77 , m_options(options) 78 , m_resourceLoaderOptions(resourceLoaderOptions) 79 , m_forceDoNotAllowStoredCredentials(false) 80 , m_securityOrigin(m_resourceLoaderOptions.securityOrigin) 81 , m_sameOriginRequest(securityOrigin()->canRequest(request.url())) 82 , m_simpleRequest(true) 83 , m_async(blockingBehavior == LoadAsynchronously) 84 , m_timeoutTimer(this, &DocumentThreadableLoader::didTimeout) 85 , m_requestStartedSeconds(0.0) 86 { 87 ASSERT(client); 88 // Setting an outgoing referer is only supported in the async code path. 89 ASSERT(m_async || request.httpReferrer().isEmpty()); 90 91 m_requestStartedSeconds = monotonicallyIncreasingTime(); 92 93 // Save any CORS simple headers on the request here. If this request redirects cross-origin, we cancel the old request 94 // create a new one, and copy these headers. 95 const HTTPHeaderMap& headerMap = request.httpHeaderFields(); 96 HTTPHeaderMap::const_iterator end = headerMap.end(); 97 for (HTTPHeaderMap::const_iterator it = headerMap.begin(); it != end; ++it) { 98 if (FetchUtils::isSimpleHeader(it->key, it->value)) 99 m_simpleRequestHeaders.add(it->key, it->value); 100 } 101 102 if (m_sameOriginRequest || m_options.crossOriginRequestPolicy == AllowCrossOriginRequests) { 103 loadRequest(request, m_resourceLoaderOptions); 104 return; 105 } 106 107 if (m_options.crossOriginRequestPolicy == DenyCrossOriginRequests) { 108 m_client->didFail(ResourceError(errorDomainBlinkInternal, 0, request.url().string(), "Cross origin requests are not supported.")); 109 return; 110 } 111 112 makeCrossOriginAccessRequest(request); 113 } 114 115 void DocumentThreadableLoader::makeCrossOriginAccessRequest(const ResourceRequest& request) 116 { 117 ASSERT(m_options.crossOriginRequestPolicy == UseAccessControl); 118 119 // Cross-origin requests are only allowed certain registered schemes. 120 // We would catch this when checking response headers later, but there 121 // is no reason to send a request, preflighted or not, that's guaranteed 122 // to be denied. 123 if (!SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(request.url().protocol())) { 124 m_client->didFailAccessControlCheck(ResourceError(errorDomainBlinkInternal, 0, request.url().string(), "Cross origin requests are only supported for protocol schemes: " + SchemeRegistry::listOfCORSEnabledURLSchemes() + ".")); 125 return; 126 } 127 128 if ((m_options.preflightPolicy == ConsiderPreflight && FetchUtils::isSimpleOrForbiddenRequest(request.httpMethod(), request.httpHeaderFields())) || m_options.preflightPolicy == PreventPreflight) { 129 ResourceRequest crossOriginRequest(request); 130 ResourceLoaderOptions crossOriginOptions(m_resourceLoaderOptions); 131 updateRequestForAccessControl(crossOriginRequest, securityOrigin(), effectiveAllowCredentials()); 132 loadRequest(crossOriginRequest, crossOriginOptions); 133 } else { 134 m_simpleRequest = false; 135 136 OwnPtr<ResourceRequest> crossOriginRequest = adoptPtr(new ResourceRequest(request)); 137 OwnPtr<ResourceLoaderOptions> crossOriginOptions = adoptPtr(new ResourceLoaderOptions(m_resourceLoaderOptions)); 138 // Do not set the Origin header for preflight requests. 139 updateRequestForAccessControl(*crossOriginRequest, 0, effectiveAllowCredentials()); 140 m_actualRequest = crossOriginRequest.release(); 141 m_actualOptions = crossOriginOptions.release(); 142 143 bool shouldForcePreflight = InspectorInstrumentation::shouldForceCORSPreflight(&m_document); 144 bool canSkipPreflight = CrossOriginPreflightResultCache::shared().canSkipPreflight(securityOrigin()->toString(), m_actualRequest->url(), effectiveAllowCredentials(), m_actualRequest->httpMethod(), m_actualRequest->httpHeaderFields()); 145 if (canSkipPreflight && !shouldForcePreflight) { 146 loadActualRequest(); 147 } else { 148 ResourceRequest preflightRequest = createAccessControlPreflightRequest(*m_actualRequest, securityOrigin()); 149 // Create a ResourceLoaderOptions for preflight. 150 ResourceLoaderOptions preflightOptions = *m_actualOptions; 151 preflightOptions.allowCredentials = DoNotAllowStoredCredentials; 152 loadRequest(preflightRequest, preflightOptions); 153 } 154 } 155 } 156 157 DocumentThreadableLoader::~DocumentThreadableLoader() 158 { 159 } 160 161 void DocumentThreadableLoader::overrideTimeout(unsigned long timeoutMilliseconds) 162 { 163 ASSERT(m_async); 164 ASSERT(m_requestStartedSeconds > 0.0); 165 m_timeoutTimer.stop(); 166 // At the time of this method's implementation, it is only ever called by 167 // XMLHttpRequest, when the timeout attribute is set after sending the 168 // request. 169 // 170 // The XHR request says to resolve the time relative to when the request 171 // was initially sent, however other uses of this method may need to 172 // behave differently, in which case this should be re-arranged somehow. 173 if (timeoutMilliseconds) { 174 double elapsedTime = monotonicallyIncreasingTime() - m_requestStartedSeconds; 175 double nextFire = timeoutMilliseconds / 1000.0; 176 double resolvedTime = std::max(nextFire - elapsedTime, 0.0); 177 m_timeoutTimer.startOneShot(resolvedTime, FROM_HERE); 178 } 179 } 180 181 void DocumentThreadableLoader::cancel() 182 { 183 cancelWithError(ResourceError()); 184 } 185 186 void DocumentThreadableLoader::cancelWithError(const ResourceError& error) 187 { 188 RefPtr<DocumentThreadableLoader> protect(this); 189 190 // Cancel can re-enter and m_resource might be null here as a result. 191 if (m_client && resource()) { 192 ResourceError errorForCallback = error; 193 if (errorForCallback.isNull()) { 194 // FIXME: This error is sent to the client in didFail(), so it should not be an internal one. Use FrameLoaderClient::cancelledError() instead. 195 errorForCallback = ResourceError(errorDomainBlinkInternal, 0, resource()->url().string(), "Load cancelled"); 196 errorForCallback.setIsCancellation(true); 197 } 198 m_client->didFail(errorForCallback); 199 } 200 clearResource(); 201 m_client = 0; 202 m_requestStartedSeconds = 0.0; 203 } 204 205 void DocumentThreadableLoader::setDefersLoading(bool value) 206 { 207 if (resource()) 208 resource()->setDefersLoading(value); 209 } 210 211 void DocumentThreadableLoader::redirectReceived(Resource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse) 212 { 213 ASSERT(m_client); 214 ASSERT_UNUSED(resource, resource == this->resource()); 215 216 RefPtr<DocumentThreadableLoader> protect(this); 217 218 // FIXME: Support redirect in Fetch API. 219 if (resource->resourceRequest().requestContext() == blink::WebURLRequest::RequestContextFetch) { 220 m_client->didFailRedirectCheck(); 221 request = ResourceRequest(); 222 return; 223 } 224 225 if (!isAllowedByPolicy(request.url())) { 226 m_client->didFailRedirectCheck(); 227 request = ResourceRequest(); 228 m_requestStartedSeconds = 0.0; 229 return; 230 } 231 232 // Allow same origin requests to continue after allowing clients to audit the redirect. 233 if (isAllowedRedirect(request.url())) { 234 if (m_client->isDocumentThreadableLoaderClient()) 235 static_cast<DocumentThreadableLoaderClient*>(m_client)->willSendRequest(request, redirectResponse); 236 return; 237 } 238 239 // When using access control, only simple cross origin requests are allowed to redirect. The new request URL must have a supported 240 // scheme and not contain the userinfo production. In addition, the redirect response must pass the access control check if the 241 // original request was not same-origin. 242 if (m_options.crossOriginRequestPolicy == UseAccessControl) { 243 244 InspectorInstrumentation::didReceiveCORSRedirectResponse(m_document.frame(), resource->identifier(), m_document.frame()->loader().documentLoader(), redirectResponse, 0); 245 246 bool allowRedirect = false; 247 String accessControlErrorDescription; 248 249 if (m_simpleRequest) { 250 allowRedirect = CrossOriginAccessControl::isLegalRedirectLocation(request.url(), accessControlErrorDescription) 251 && (m_sameOriginRequest || passesAccessControlCheck(redirectResponse, effectiveAllowCredentials(), securityOrigin(), accessControlErrorDescription)); 252 } else { 253 accessControlErrorDescription = "The request was redirected to '"+ request.url().string() + "', which is disallowed for cross-origin requests that require preflight."; 254 } 255 256 if (allowRedirect) { 257 // FIXME: consider combining this with CORS redirect handling performed by 258 // CrossOriginAccessControl::handleRedirect(). 259 clearResource(); 260 261 RefPtr<SecurityOrigin> originalOrigin = SecurityOrigin::create(redirectResponse.url()); 262 RefPtr<SecurityOrigin> requestOrigin = SecurityOrigin::create(request.url()); 263 // If the original request wasn't same-origin, then if the request URL origin is not same origin with the original URL origin, 264 // set the source origin to a globally unique identifier. (If the original request was same-origin, the origin of the new request 265 // should be the original URL origin.) 266 if (!m_sameOriginRequest && !originalOrigin->isSameSchemeHostPort(requestOrigin.get())) 267 m_securityOrigin = SecurityOrigin::createUnique(); 268 // Force any subsequent requests to use these checks. 269 m_sameOriginRequest = false; 270 271 // Since the request is no longer same-origin, if the user didn't request credentials in 272 // the first place, update our state so we neither request them nor expect they must be allowed. 273 if (m_resourceLoaderOptions.credentialsRequested == ClientDidNotRequestCredentials) 274 m_forceDoNotAllowStoredCredentials = true; 275 276 // Remove any headers that may have been added by the network layer that cause access control to fail. 277 request.clearHTTPReferrer(); 278 request.clearHTTPOrigin(); 279 request.clearHTTPUserAgent(); 280 // Add any CORS simple request headers which we previously saved from the original request. 281 HTTPHeaderMap::const_iterator end = m_simpleRequestHeaders.end(); 282 for (HTTPHeaderMap::const_iterator it = m_simpleRequestHeaders.begin(); it != end; ++it) { 283 request.setHTTPHeaderField(it->key, it->value); 284 } 285 makeCrossOriginAccessRequest(request); 286 return; 287 } 288 289 ResourceError error(errorDomainBlinkInternal, 0, redirectResponse.url().string(), accessControlErrorDescription); 290 m_client->didFailAccessControlCheck(error); 291 } else { 292 m_client->didFailRedirectCheck(); 293 } 294 request = ResourceRequest(); 295 m_requestStartedSeconds = 0.0; 296 } 297 298 void DocumentThreadableLoader::dataSent(Resource* resource, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) 299 { 300 ASSERT(m_client); 301 ASSERT_UNUSED(resource, resource == this->resource()); 302 m_client->didSendData(bytesSent, totalBytesToBeSent); 303 } 304 305 void DocumentThreadableLoader::dataDownloaded(Resource* resource, int dataLength) 306 { 307 ASSERT(m_client); 308 ASSERT_UNUSED(resource, resource == this->resource()); 309 ASSERT(!m_actualRequest); 310 311 m_client->didDownloadData(dataLength); 312 } 313 314 void DocumentThreadableLoader::responseReceived(Resource* resource, const ResourceResponse& response) 315 { 316 ASSERT_UNUSED(resource, resource == this->resource()); 317 handleResponse(resource->identifier(), response); 318 } 319 320 void DocumentThreadableLoader::handlePreflightResponse(const ResourceResponse& response) 321 { 322 String accessControlErrorDescription; 323 324 if (!passesAccessControlCheck(response, effectiveAllowCredentials(), securityOrigin(), accessControlErrorDescription)) { 325 handlePreflightFailure(response.url().string(), accessControlErrorDescription); 326 return; 327 } 328 329 if (!passesPreflightStatusCheck(response, accessControlErrorDescription)) { 330 handlePreflightFailure(response.url().string(), accessControlErrorDescription); 331 return; 332 } 333 334 OwnPtr<CrossOriginPreflightResultCacheItem> preflightResult = adoptPtr(new CrossOriginPreflightResultCacheItem(effectiveAllowCredentials())); 335 if (!preflightResult->parse(response, accessControlErrorDescription) 336 || !preflightResult->allowsCrossOriginMethod(m_actualRequest->httpMethod(), accessControlErrorDescription) 337 || !preflightResult->allowsCrossOriginHeaders(m_actualRequest->httpHeaderFields(), accessControlErrorDescription)) { 338 handlePreflightFailure(response.url().string(), accessControlErrorDescription); 339 return; 340 } 341 342 CrossOriginPreflightResultCache::shared().appendEntry(securityOrigin()->toString(), m_actualRequest->url(), preflightResult.release()); 343 } 344 345 void DocumentThreadableLoader::notifyResponseReceived(unsigned long identifier, const ResourceResponse& response) 346 { 347 DocumentLoader* loader = m_document.frame()->loader().documentLoader(); 348 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ResourceReceiveResponse", "data", InspectorReceiveResponseEvent::data(identifier, m_document.frame(), response)); 349 LocalFrame* frame = m_document.frame(); 350 InspectorInstrumentation::didReceiveResourceResponse(frame, identifier, loader, response, resource() ? resource()->loader() : 0); 351 // It is essential that inspector gets resource response BEFORE console. 352 frame->console().reportResourceResponseReceived(loader, identifier, response); 353 } 354 355 void DocumentThreadableLoader::handleResponse(unsigned long identifier, const ResourceResponse& response) 356 { 357 ASSERT(m_client); 358 359 if (m_actualRequest) { 360 notifyResponseReceived(identifier, response); 361 handlePreflightResponse(response); 362 return; 363 } 364 365 // If the response is fetched via ServiceWorker, the original URL of the response could be different from the URL of the request. 366 bool isCrossOriginResponse = false; 367 if (response.wasFetchedViaServiceWorker()) { 368 if (!isAllowedByPolicy(response.url())) { 369 notifyResponseReceived(identifier, response); 370 m_client->didFailRedirectCheck(); 371 return; 372 } 373 isCrossOriginResponse = !securityOrigin()->canRequest(response.url()); 374 if (m_options.crossOriginRequestPolicy == DenyCrossOriginRequests && isCrossOriginResponse) { 375 notifyResponseReceived(identifier, response); 376 m_client->didFail(ResourceError(errorDomainBlinkInternal, 0, response.url().string(), "Cross origin requests are not supported.")); 377 return; 378 } 379 if (isCrossOriginResponse && m_resourceLoaderOptions.credentialsRequested == ClientDidNotRequestCredentials) { 380 // Since the request is no longer same-origin, if the user didn't request credentials in 381 // the first place, update our state so we neither request them nor expect they must be allowed. 382 m_forceDoNotAllowStoredCredentials = true; 383 } 384 } else { 385 isCrossOriginResponse = !m_sameOriginRequest; 386 } 387 if (isCrossOriginResponse && m_options.crossOriginRequestPolicy == UseAccessControl) { 388 String accessControlErrorDescription; 389 if (!passesAccessControlCheck(response, effectiveAllowCredentials(), securityOrigin(), accessControlErrorDescription)) { 390 notifyResponseReceived(identifier, response); 391 m_client->didFailAccessControlCheck(ResourceError(errorDomainBlinkInternal, 0, response.url().string(), accessControlErrorDescription)); 392 return; 393 } 394 } 395 396 m_client->didReceiveResponse(identifier, response); 397 } 398 399 void DocumentThreadableLoader::dataReceived(Resource* resource, const char* data, int dataLength) 400 { 401 ASSERT_UNUSED(resource, resource == this->resource()); 402 handleReceivedData(data, dataLength); 403 } 404 405 void DocumentThreadableLoader::handleReceivedData(const char* data, int dataLength) 406 { 407 ASSERT(m_client); 408 // Preflight data should be invisible to clients. 409 if (!m_actualRequest) 410 m_client->didReceiveData(data, dataLength); 411 } 412 413 void DocumentThreadableLoader::notifyFinished(Resource* resource) 414 { 415 ASSERT(m_client); 416 ASSERT(resource == this->resource()); 417 418 m_timeoutTimer.stop(); 419 420 if (resource->errorOccurred()) 421 m_client->didFail(resource->resourceError()); 422 else 423 handleSuccessfulFinish(resource->identifier(), resource->loadFinishTime()); 424 } 425 426 void DocumentThreadableLoader::handleSuccessfulFinish(unsigned long identifier, double finishTime) 427 { 428 if (m_actualRequest) { 429 ASSERT(!m_sameOriginRequest); 430 ASSERT(m_options.crossOriginRequestPolicy == UseAccessControl); 431 loadActualRequest(); 432 } else { 433 // FIXME: Should prevent timeout from being overridden after finished loading, without 434 // resetting m_requestStartedSeconds to 0.0 435 m_client->didFinishLoading(identifier, finishTime); 436 } 437 } 438 439 void DocumentThreadableLoader::didTimeout(Timer<DocumentThreadableLoader>* timer) 440 { 441 ASSERT_UNUSED(timer, timer == &m_timeoutTimer); 442 443 // Using values from net/base/net_error_list.h ERR_TIMED_OUT, 444 // Same as existing FIXME above - this error should be coming from FrameLoaderClient to be identifiable. 445 static const int timeoutError = -7; 446 ResourceError error("net", timeoutError, resource()->url(), String()); 447 error.setIsTimeout(true); 448 cancelWithError(error); 449 } 450 451 void DocumentThreadableLoader::loadActualRequest() 452 { 453 OwnPtr<ResourceRequest> actualRequest; 454 actualRequest.swap(m_actualRequest); 455 OwnPtr<ResourceLoaderOptions> actualOptions; 456 actualOptions.swap(m_actualOptions); 457 458 actualRequest->setHTTPOrigin(securityOrigin()->toAtomicString()); 459 460 clearResource(); 461 462 loadRequest(*actualRequest, *actualOptions); 463 } 464 465 void DocumentThreadableLoader::handlePreflightFailure(const String& url, const String& errorDescription) 466 { 467 ResourceError error(errorDomainBlinkInternal, 0, url, errorDescription); 468 469 // Prevent handleSuccessfulFinish() from bypassing access check. 470 m_actualRequest = nullptr; 471 472 // FIXME: Should prevent timeout from being overridden after preflight failure, without 473 // resetting m_requestStartedSeconds to 0.0 474 m_client->didFailAccessControlCheck(error); 475 } 476 477 void DocumentThreadableLoader::loadRequest(const ResourceRequest& request, ResourceLoaderOptions resourceLoaderOptions) 478 { 479 // Any credential should have been removed from the cross-site requests. 480 const KURL& requestURL = request.url(); 481 ASSERT(m_sameOriginRequest || requestURL.user().isEmpty()); 482 ASSERT(m_sameOriginRequest || requestURL.pass().isEmpty()); 483 484 // Update resourceLoaderOptions with enforced values. 485 if (m_forceDoNotAllowStoredCredentials) 486 resourceLoaderOptions.allowCredentials = DoNotAllowStoredCredentials; 487 resourceLoaderOptions.securityOrigin = m_securityOrigin; 488 if (m_async) { 489 if (m_actualRequest) 490 resourceLoaderOptions.dataBufferingPolicy = BufferData; 491 492 if (m_options.timeoutMilliseconds > 0) 493 m_timeoutTimer.startOneShot(m_options.timeoutMilliseconds / 1000.0, FROM_HERE); 494 495 FetchRequest newRequest(request, m_options.initiator, resourceLoaderOptions); 496 if (m_options.crossOriginRequestPolicy == AllowCrossOriginRequests) 497 newRequest.setOriginRestriction(FetchRequest::NoOriginRestriction); 498 ASSERT(!resource()); 499 if (request.requestContext() == blink::WebURLRequest::RequestContextVideo || request.requestContext() == blink::WebURLRequest::RequestContextAudio) 500 setResource(m_document.fetcher()->fetchMedia(newRequest)); 501 else 502 setResource(m_document.fetcher()->fetchRawResource(newRequest)); 503 if (resource() && resource()->loader()) { 504 unsigned long identifier = resource()->identifier(); 505 InspectorInstrumentation::documentThreadableLoaderStartedLoadingForClient(&m_document, identifier, m_client); 506 } 507 return; 508 } 509 510 FetchRequest fetchRequest(request, m_options.initiator, resourceLoaderOptions); 511 if (m_options.crossOriginRequestPolicy == AllowCrossOriginRequests) 512 fetchRequest.setOriginRestriction(FetchRequest::NoOriginRestriction); 513 ResourcePtr<Resource> resource = m_document.fetcher()->fetchSynchronously(fetchRequest); 514 ResourceResponse response = resource ? resource->response() : ResourceResponse(); 515 unsigned long identifier = resource ? resource->identifier() : std::numeric_limits<unsigned long>::max(); 516 ResourceError error = resource ? resource->resourceError() : ResourceError(); 517 518 InspectorInstrumentation::documentThreadableLoaderStartedLoadingForClient(&m_document, identifier, m_client); 519 520 if (!resource) { 521 m_client->didFail(error); 522 return; 523 } 524 525 // No exception for file:/// resources, see <rdar://problem/4962298>. 526 // Also, if we have an HTTP response, then it wasn't a network error in fact. 527 if (!error.isNull() && !requestURL.isLocalFile() && response.httpStatusCode() <= 0) { 528 m_client->didFail(error); 529 return; 530 } 531 532 // FIXME: A synchronous request does not tell us whether a redirect happened or not, so we guess by comparing the 533 // request and response URLs. This isn't a perfect test though, since a server can serve a redirect to the same URL that was 534 // requested. Also comparing the request and response URLs as strings will fail if the requestURL still has its credentials. 535 if (requestURL != response.url() && (!isAllowedByPolicy(response.url()) || !isAllowedRedirect(response.url()))) { 536 m_client->didFailRedirectCheck(); 537 return; 538 } 539 540 handleResponse(identifier, response); 541 542 SharedBuffer* data = resource->resourceBuffer(); 543 if (data) 544 handleReceivedData(data->data(), data->size()); 545 546 handleSuccessfulFinish(identifier, 0.0); 547 } 548 549 bool DocumentThreadableLoader::isAllowedRedirect(const KURL& url) const 550 { 551 if (m_options.crossOriginRequestPolicy == AllowCrossOriginRequests) 552 return true; 553 554 return m_sameOriginRequest && securityOrigin()->canRequest(url); 555 } 556 557 bool DocumentThreadableLoader::isAllowedByPolicy(const KURL& url) const 558 { 559 if (m_options.contentSecurityPolicyEnforcement != EnforceConnectSrcDirective) 560 return true; 561 return m_document.contentSecurityPolicy()->allowConnectToSource(url); 562 } 563 564 StoredCredentials DocumentThreadableLoader::effectiveAllowCredentials() const 565 { 566 if (m_forceDoNotAllowStoredCredentials) 567 return DoNotAllowStoredCredentials; 568 return m_resourceLoaderOptions.allowCredentials; 569 } 570 571 SecurityOrigin* DocumentThreadableLoader::securityOrigin() const 572 { 573 return m_securityOrigin ? m_securityOrigin.get() : m_document.securityOrigin(); 574 } 575 576 } // namespace blink 577