2 * Copyright (C) 2011, 2012 Google Inc. All rights reserved.
3 * Copyright (C) 2013, Intel Corporation
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
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
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.
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.
33 #include "core/loader/DocumentThreadableLoader.h"
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/FrameLoaderClient.h"
50 #include "core/loader/ThreadableLoaderClient.h"
51 #include "platform/SharedBuffer.h"
52 #include "platform/network/ResourceRequest.h"
53 #include "platform/weborigin/SchemeRegistry.h"
54 #include "platform/weborigin/SecurityOrigin.h"
55 #include "public/platform/WebURLRequest.h"
56 #include "wtf/Assertions.h"
60 void DocumentThreadableLoader::loadResourceSynchronously(Document& document, const ResourceRequest& request, ThreadableLoaderClient& client, const ThreadableLoaderOptions& options, const ResourceLoaderOptions& resourceLoaderOptions)
62 // The loader will be deleted as soon as this function exits.
63 RefPtr<DocumentThreadableLoader> loader = adoptRef(new DocumentThreadableLoader(document, &client, LoadSynchronously, request, options, resourceLoaderOptions));
64 ASSERT(loader->hasOneRef());
67 PassRefPtr<DocumentThreadableLoader> DocumentThreadableLoader::create(Document& document, ThreadableLoaderClient* client, const ResourceRequest& request, const ThreadableLoaderOptions& options, const ResourceLoaderOptions& resourceLoaderOptions)
69 RefPtr<DocumentThreadableLoader> loader = adoptRef(new DocumentThreadableLoader(document, client, LoadAsynchronously, request, options, resourceLoaderOptions));
70 if (!loader->resource())
72 return loader.release();
75 DocumentThreadableLoader::DocumentThreadableLoader(Document& document, ThreadableLoaderClient* client, BlockingBehavior blockingBehavior, const ResourceRequest& request, const ThreadableLoaderOptions& options, const ResourceLoaderOptions& resourceLoaderOptions)
77 , m_document(document)
79 , m_resourceLoaderOptions(resourceLoaderOptions)
80 , m_forceDoNotAllowStoredCredentials(false)
81 , m_securityOrigin(m_resourceLoaderOptions.securityOrigin)
82 , m_sameOriginRequest(securityOrigin()->canRequest(request.url()))
83 , m_simpleRequest(true)
84 , m_async(blockingBehavior == LoadAsynchronously)
85 , m_timeoutTimer(this, &DocumentThreadableLoader::didTimeout)
86 , m_requestStartedSeconds(0.0)
89 // Setting an outgoing referer is only supported in the async code path.
90 ASSERT(m_async || request.httpReferrer().isEmpty());
92 if (!m_sameOriginRequest && m_options.crossOriginRequestPolicy == DenyCrossOriginRequests) {
93 m_client->didFail(ResourceError(errorDomainBlinkInternal, 0, request.url().string(), "Cross origin requests are not supported."));
97 m_requestStartedSeconds = monotonicallyIncreasingTime();
99 // Save any CORS simple headers on the request here. If this request redirects cross-origin, we cancel the old request
100 // create a new one, and copy these headers.
101 const HTTPHeaderMap& headerMap = request.httpHeaderFields();
102 HTTPHeaderMap::const_iterator end = headerMap.end();
103 for (HTTPHeaderMap::const_iterator it = headerMap.begin(); it != end; ++it) {
104 if (FetchUtils::isSimpleHeader(it->key, it->value))
105 m_simpleRequestHeaders.add(it->key, it->value);
108 // If the fetch request will be handled by the ServiceWorker, the
109 // FetchRequestMode of the request must be FetchRequestModeCORS or
110 // FetchRequestModeCORSWithForcedPreflight. Otherwise the ServiceWorker can
111 // return a opaque response which is from the other origin site and the
112 // script in the page can read the content.
114 // We assume that ServiceWorker is skipped for sync requests by content/
116 if (m_async && !request.skipServiceWorker() && m_document.fetcher()->isControlledByServiceWorker()) {
117 ResourceRequest newRequest(request);
118 // FetchRequestMode should be set by the caller. But the expected value
119 // of FetchRequestMode is not speced yet except for XHR. So we set here.
120 // FIXME: When we support fetch API in document, this value should not
121 // be overridden here.
122 if (options.preflightPolicy == ForcePreflight)
123 newRequest.setFetchRequestMode(WebURLRequest::FetchRequestModeCORSWithForcedPreflight);
125 newRequest.setFetchRequestMode(WebURLRequest::FetchRequestModeCORS);
127 m_fallbackRequestForServiceWorker = adoptPtr(new ResourceRequest(request));
128 m_fallbackRequestForServiceWorker->setSkipServiceWorker(true);
130 loadRequest(newRequest, m_resourceLoaderOptions);
134 dispatchInitialRequest(request);
137 void DocumentThreadableLoader::dispatchInitialRequest(const ResourceRequest& request)
139 if (m_sameOriginRequest || m_options.crossOriginRequestPolicy == AllowCrossOriginRequests) {
140 loadRequest(request, m_resourceLoaderOptions);
144 ASSERT(m_options.crossOriginRequestPolicy == UseAccessControl);
146 makeCrossOriginAccessRequest(request);
149 void DocumentThreadableLoader::makeCrossOriginAccessRequest(const ResourceRequest& request)
151 ASSERT(m_options.crossOriginRequestPolicy == UseAccessControl);
153 // Cross-origin requests are only allowed certain registered schemes.
154 // We would catch this when checking response headers later, but there
155 // is no reason to send a request, preflighted or not, that's guaranteed
157 if (!SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(request.url().protocol())) {
158 m_client->didFailAccessControlCheck(ResourceError(errorDomainBlinkInternal, 0, request.url().string(), "Cross origin requests are only supported for protocol schemes: " + SchemeRegistry::listOfCORSEnabledURLSchemes() + "."));
162 // We use isSimpleOrForbiddenRequest() here since |request| may have been
163 // modified in the process of loading (not from the user's input). For
164 // example, referrer. We need to accept them. For security, we must reject
165 // forbidden headers/methods at the point we accept user's input. Not here.
166 if ((m_options.preflightPolicy == ConsiderPreflight && FetchUtils::isSimpleOrForbiddenRequest(request.httpMethod(), request.httpHeaderFields())) || m_options.preflightPolicy == PreventPreflight) {
167 ResourceRequest crossOriginRequest(request);
168 ResourceLoaderOptions crossOriginOptions(m_resourceLoaderOptions);
169 updateRequestForAccessControl(crossOriginRequest, securityOrigin(), effectiveAllowCredentials());
170 loadRequest(crossOriginRequest, crossOriginOptions);
172 m_simpleRequest = false;
174 OwnPtr<ResourceRequest> crossOriginRequest = adoptPtr(new ResourceRequest(request));
175 OwnPtr<ResourceLoaderOptions> crossOriginOptions = adoptPtr(new ResourceLoaderOptions(m_resourceLoaderOptions));
176 // Do not set the Origin header for preflight requests.
177 updateRequestForAccessControl(*crossOriginRequest, 0, effectiveAllowCredentials());
178 m_actualRequest = crossOriginRequest.release();
179 m_actualOptions = crossOriginOptions.release();
181 bool shouldForcePreflight = InspectorInstrumentation::shouldForceCORSPreflight(&m_document);
182 bool canSkipPreflight = CrossOriginPreflightResultCache::shared().canSkipPreflight(securityOrigin()->toString(), m_actualRequest->url(), effectiveAllowCredentials(), m_actualRequest->httpMethod(), m_actualRequest->httpHeaderFields());
183 if (canSkipPreflight && !shouldForcePreflight) {
186 ResourceRequest preflightRequest = createAccessControlPreflightRequest(*m_actualRequest, securityOrigin());
187 // Create a ResourceLoaderOptions for preflight.
188 ResourceLoaderOptions preflightOptions = *m_actualOptions;
189 preflightOptions.allowCredentials = DoNotAllowStoredCredentials;
190 loadRequest(preflightRequest, preflightOptions);
195 DocumentThreadableLoader::~DocumentThreadableLoader()
199 void DocumentThreadableLoader::overrideTimeout(unsigned long timeoutMilliseconds)
202 ASSERT(m_requestStartedSeconds > 0.0);
203 m_timeoutTimer.stop();
204 // At the time of this method's implementation, it is only ever called by
205 // XMLHttpRequest, when the timeout attribute is set after sending the
208 // The XHR request says to resolve the time relative to when the request
209 // was initially sent, however other uses of this method may need to
210 // behave differently, in which case this should be re-arranged somehow.
211 if (timeoutMilliseconds) {
212 double elapsedTime = monotonicallyIncreasingTime() - m_requestStartedSeconds;
213 double nextFire = timeoutMilliseconds / 1000.0;
214 double resolvedTime = std::max(nextFire - elapsedTime, 0.0);
215 m_timeoutTimer.startOneShot(resolvedTime, FROM_HERE);
219 void DocumentThreadableLoader::cancel()
221 cancelWithError(ResourceError());
224 void DocumentThreadableLoader::cancelWithError(const ResourceError& error)
226 RefPtr<DocumentThreadableLoader> protect(this);
228 // Cancel can re-enter and m_resource might be null here as a result.
229 if (m_client && resource()) {
230 ResourceError errorForCallback = error;
231 if (errorForCallback.isNull()) {
232 // FIXME: This error is sent to the client in didFail(), so it should not be an internal one. Use FrameLoaderClient::cancelledError() instead.
233 errorForCallback = ResourceError(errorDomainBlinkInternal, 0, resource()->url().string(), "Load cancelled");
234 errorForCallback.setIsCancellation(true);
236 m_client->didFail(errorForCallback);
240 m_requestStartedSeconds = 0.0;
243 void DocumentThreadableLoader::setDefersLoading(bool value)
246 resource()->setDefersLoading(value);
249 void DocumentThreadableLoader::redirectReceived(Resource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse)
252 ASSERT_UNUSED(resource, resource == this->resource());
255 RefPtr<DocumentThreadableLoader> protect(this);
257 // FIXME: Support redirect in Fetch API.
258 if (resource->resourceRequest().requestContext() == blink::WebURLRequest::RequestContextFetch) {
259 m_client->didFailRedirectCheck();
260 request = ResourceRequest();
264 if (!isAllowedByContentSecurityPolicy(request.url())) {
265 m_client->didFailRedirectCheck();
266 request = ResourceRequest();
267 m_requestStartedSeconds = 0.0;
271 // Allow same origin requests to continue after allowing clients to audit the redirect.
272 if (isAllowedRedirect(request.url())) {
273 if (m_client->isDocumentThreadableLoaderClient())
274 static_cast<DocumentThreadableLoaderClient*>(m_client)->willFollowRedirect(request, redirectResponse);
278 // When using access control, only simple cross origin requests are allowed to redirect. The new request URL must have a supported
279 // scheme and not contain the userinfo production. In addition, the redirect response must pass the access control check if the
280 // original request was not same-origin.
281 if (m_options.crossOriginRequestPolicy == UseAccessControl) {
283 InspectorInstrumentation::didReceiveCORSRedirectResponse(m_document.frame(), resource->identifier(), m_document.frame()->loader().documentLoader(), redirectResponse, 0);
285 bool allowRedirect = false;
286 String accessControlErrorDescription;
288 if (m_simpleRequest) {
289 allowRedirect = CrossOriginAccessControl::isLegalRedirectLocation(request.url(), accessControlErrorDescription)
290 && (m_sameOriginRequest || passesAccessControlCheck(redirectResponse, effectiveAllowCredentials(), securityOrigin(), accessControlErrorDescription));
292 accessControlErrorDescription = "The request was redirected to '"+ request.url().string() + "', which is disallowed for cross-origin requests that require preflight.";
296 // FIXME: consider combining this with CORS redirect handling performed by
297 // CrossOriginAccessControl::handleRedirect().
300 RefPtr<SecurityOrigin> originalOrigin = SecurityOrigin::create(redirectResponse.url());
301 RefPtr<SecurityOrigin> requestOrigin = SecurityOrigin::create(request.url());
302 // If the original request wasn't same-origin, then if the request URL origin is not same origin with the original URL origin,
303 // set the source origin to a globally unique identifier. (If the original request was same-origin, the origin of the new request
304 // should be the original URL origin.)
305 if (!m_sameOriginRequest && !originalOrigin->isSameSchemeHostPort(requestOrigin.get()))
306 m_securityOrigin = SecurityOrigin::createUnique();
307 // Force any subsequent requests to use these checks.
308 m_sameOriginRequest = false;
310 // Since the request is no longer same-origin, if the user didn't request credentials in
311 // the first place, update our state so we neither request them nor expect they must be allowed.
312 if (m_resourceLoaderOptions.credentialsRequested == ClientDidNotRequestCredentials)
313 m_forceDoNotAllowStoredCredentials = true;
315 // Remove any headers that may have been added by the network layer that cause access control to fail.
316 request.clearHTTPReferrer();
317 request.clearHTTPOrigin();
318 request.clearHTTPUserAgent();
319 // Add any CORS simple request headers which we previously saved from the original request.
320 HTTPHeaderMap::const_iterator end = m_simpleRequestHeaders.end();
321 for (HTTPHeaderMap::const_iterator it = m_simpleRequestHeaders.begin(); it != end; ++it) {
322 request.setHTTPHeaderField(it->key, it->value);
324 makeCrossOriginAccessRequest(request);
328 ResourceError error(errorDomainBlinkInternal, 0, redirectResponse.url().string(), accessControlErrorDescription);
329 m_client->didFailAccessControlCheck(error);
331 m_client->didFailRedirectCheck();
333 request = ResourceRequest();
334 m_requestStartedSeconds = 0.0;
337 void DocumentThreadableLoader::dataSent(Resource* resource, unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
340 ASSERT_UNUSED(resource, resource == this->resource());
343 m_client->didSendData(bytesSent, totalBytesToBeSent);
346 void DocumentThreadableLoader::dataDownloaded(Resource* resource, int dataLength)
349 ASSERT_UNUSED(resource, resource == this->resource());
350 ASSERT(!m_actualRequest);
353 m_client->didDownloadData(dataLength);
356 void DocumentThreadableLoader::responseReceived(Resource* resource, const ResourceResponse& response, PassOwnPtr<WebDataConsumerHandle> handle)
358 ASSERT_UNUSED(resource, resource == this->resource());
361 handleResponse(resource->identifier(), response, handle);
364 void DocumentThreadableLoader::handlePreflightResponse(const ResourceResponse& response)
366 String accessControlErrorDescription;
368 if (!passesAccessControlCheck(response, effectiveAllowCredentials(), securityOrigin(), accessControlErrorDescription)) {
369 handlePreflightFailure(response.url().string(), accessControlErrorDescription);
373 if (!passesPreflightStatusCheck(response, accessControlErrorDescription)) {
374 handlePreflightFailure(response.url().string(), accessControlErrorDescription);
378 OwnPtr<CrossOriginPreflightResultCacheItem> preflightResult = adoptPtr(new CrossOriginPreflightResultCacheItem(effectiveAllowCredentials()));
379 if (!preflightResult->parse(response, accessControlErrorDescription)
380 || !preflightResult->allowsCrossOriginMethod(m_actualRequest->httpMethod(), accessControlErrorDescription)
381 || !preflightResult->allowsCrossOriginHeaders(m_actualRequest->httpHeaderFields(), accessControlErrorDescription)) {
382 handlePreflightFailure(response.url().string(), accessControlErrorDescription);
386 CrossOriginPreflightResultCache::shared().appendEntry(securityOrigin()->toString(), m_actualRequest->url(), preflightResult.release());
389 void DocumentThreadableLoader::reportResponseReceived(unsigned long identifier, const ResourceResponse& response)
391 DocumentLoader* loader = m_document.frame()->loader().documentLoader();
392 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ResourceReceiveResponse", "data", InspectorReceiveResponseEvent::data(identifier, m_document.frame(), response));
393 LocalFrame* frame = m_document.frame();
394 InspectorInstrumentation::didReceiveResourceResponse(frame, identifier, loader, response, resource() ? resource()->loader() : 0);
395 // It is essential that inspector gets resource response BEFORE console.
396 frame->console().reportResourceResponseReceived(loader, identifier, response);
399 void DocumentThreadableLoader::handleResponse(unsigned long identifier, const ResourceResponse& response, PassOwnPtr<WebDataConsumerHandle> handle)
403 if (m_actualRequest) {
404 reportResponseReceived(identifier, response);
405 handlePreflightResponse(response);
409 if (response.wasFetchedViaServiceWorker()) {
410 ASSERT(m_fallbackRequestForServiceWorker);
411 if (response.wasFallbackRequiredByServiceWorker()) {
412 loadFallbackRequestForServiceWorker();
415 m_fallbackRequestForServiceWorker = nullptr;
416 m_client->didReceiveResponse(identifier, response, handle);
420 ASSERT(!m_fallbackRequestForServiceWorker);
422 if (!m_sameOriginRequest && m_options.crossOriginRequestPolicy == UseAccessControl) {
423 String accessControlErrorDescription;
424 if (!passesAccessControlCheck(response, effectiveAllowCredentials(), securityOrigin(), accessControlErrorDescription)) {
425 reportResponseReceived(identifier, response);
426 m_client->didFailAccessControlCheck(ResourceError(errorDomainBlinkInternal, 0, response.url().string(), accessControlErrorDescription));
431 m_client->didReceiveResponse(identifier, response, handle);
434 void DocumentThreadableLoader::dataReceived(Resource* resource, const char* data, unsigned dataLength)
436 ASSERT_UNUSED(resource, resource == this->resource());
439 handleReceivedData(data, dataLength);
442 void DocumentThreadableLoader::handleReceivedData(const char* data, unsigned dataLength)
446 // Preflight data should be invisible to clients.
450 ASSERT(!m_fallbackRequestForServiceWorker);
452 m_client->didReceiveData(data, dataLength);
455 void DocumentThreadableLoader::notifyFinished(Resource* resource)
458 ASSERT(resource == this->resource());
461 m_timeoutTimer.stop();
463 if (resource->errorOccurred())
464 m_client->didFail(resource->resourceError());
466 handleSuccessfulFinish(resource->identifier(), resource->loadFinishTime());
469 void DocumentThreadableLoader::handleSuccessfulFinish(unsigned long identifier, double finishTime)
471 ASSERT(!m_fallbackRequestForServiceWorker);
473 if (m_actualRequest) {
474 ASSERT(!m_sameOriginRequest);
475 ASSERT(m_options.crossOriginRequestPolicy == UseAccessControl);
478 // FIXME: Should prevent timeout from being overridden after finished loading, without
479 // resetting m_requestStartedSeconds to 0.0
480 m_client->didFinishLoading(identifier, finishTime);
484 void DocumentThreadableLoader::didTimeout(Timer<DocumentThreadableLoader>* timer)
486 ASSERT_UNUSED(timer, timer == &m_timeoutTimer);
488 // Using values from net/base/net_error_list.h ERR_TIMED_OUT,
489 // Same as existing FIXME above - this error should be coming from FrameLoaderClient to be identifiable.
490 static const int timeoutError = -7;
491 ResourceError error("net", timeoutError, resource()->url(), String());
492 error.setIsTimeout(true);
493 cancelWithError(error);
496 void DocumentThreadableLoader::loadFallbackRequestForServiceWorker()
499 OwnPtr<ResourceRequest> fallbackRequest(m_fallbackRequestForServiceWorker.release());
500 dispatchInitialRequest(*fallbackRequest);
503 void DocumentThreadableLoader::loadActualRequest()
505 OwnPtr<ResourceRequest> actualRequest;
506 actualRequest.swap(m_actualRequest);
507 OwnPtr<ResourceLoaderOptions> actualOptions;
508 actualOptions.swap(m_actualOptions);
510 actualRequest->setHTTPOrigin(securityOrigin()->toAtomicString());
514 loadRequest(*actualRequest, *actualOptions);
517 void DocumentThreadableLoader::handlePreflightFailure(const String& url, const String& errorDescription)
519 ResourceError error(errorDomainBlinkInternal, 0, url, errorDescription);
521 // Prevent handleSuccessfulFinish() from bypassing access check.
522 m_actualRequest = nullptr;
524 // FIXME: Should prevent timeout from being overridden after preflight failure, without
525 // resetting m_requestStartedSeconds to 0.0
526 m_client->didFailAccessControlCheck(error);
529 void DocumentThreadableLoader::loadRequest(const ResourceRequest& request, ResourceLoaderOptions resourceLoaderOptions)
531 // Any credential should have been removed from the cross-site requests.
532 const KURL& requestURL = request.url();
533 ASSERT(m_sameOriginRequest || requestURL.user().isEmpty());
534 ASSERT(m_sameOriginRequest || requestURL.pass().isEmpty());
536 // Update resourceLoaderOptions with enforced values.
537 if (m_forceDoNotAllowStoredCredentials)
538 resourceLoaderOptions.allowCredentials = DoNotAllowStoredCredentials;
539 resourceLoaderOptions.securityOrigin = m_securityOrigin;
542 resourceLoaderOptions.dataBufferingPolicy = BufferData;
544 if (m_options.timeoutMilliseconds > 0)
545 m_timeoutTimer.startOneShot(m_options.timeoutMilliseconds / 1000.0, FROM_HERE);
547 FetchRequest newRequest(request, m_options.initiator, resourceLoaderOptions);
548 if (m_options.crossOriginRequestPolicy == AllowCrossOriginRequests)
549 newRequest.setOriginRestriction(FetchRequest::NoOriginRestriction);
551 if (request.requestContext() == blink::WebURLRequest::RequestContextVideo || request.requestContext() == blink::WebURLRequest::RequestContextAudio)
552 setResource(m_document.fetcher()->fetchMedia(newRequest));
554 setResource(m_document.fetcher()->fetchRawResource(newRequest));
555 if (resource() && resource()->loader()) {
556 unsigned long identifier = resource()->identifier();
557 InspectorInstrumentation::documentThreadableLoaderStartedLoadingForClient(&m_document, identifier, m_client);
562 FetchRequest fetchRequest(request, m_options.initiator, resourceLoaderOptions);
563 if (m_options.crossOriginRequestPolicy == AllowCrossOriginRequests)
564 fetchRequest.setOriginRestriction(FetchRequest::NoOriginRestriction);
565 ResourcePtr<Resource> resource = m_document.fetcher()->fetchSynchronously(fetchRequest);
566 ResourceResponse response = resource ? resource->response() : ResourceResponse();
567 unsigned long identifier = resource ? resource->identifier() : std::numeric_limits<unsigned long>::max();
568 ResourceError error = resource ? resource->resourceError() : ResourceError();
570 InspectorInstrumentation::documentThreadableLoaderStartedLoadingForClient(&m_document, identifier, m_client);
573 m_client->didFail(error);
577 // No exception for file:/// resources, see <rdar://problem/4962298>.
578 // Also, if we have an HTTP response, then it wasn't a network error in fact.
579 if (!error.isNull() && !requestURL.isLocalFile() && response.httpStatusCode() <= 0) {
580 m_client->didFail(error);
584 // FIXME: A synchronous request does not tell us whether a redirect happened or not, so we guess by comparing the
585 // request and response URLs. This isn't a perfect test though, since a server can serve a redirect to the same URL that was
586 // requested. Also comparing the request and response URLs as strings will fail if the requestURL still has its credentials.
587 if (requestURL != response.url() && (!isAllowedByContentSecurityPolicy(response.url()) || !isAllowedRedirect(response.url()))) {
588 m_client->didFailRedirectCheck();
592 handleResponse(identifier, response, nullptr);
594 SharedBuffer* data = resource->resourceBuffer();
596 handleReceivedData(data->data(), data->size());
598 handleSuccessfulFinish(identifier, 0.0);
601 bool DocumentThreadableLoader::isAllowedRedirect(const KURL& url) const
603 if (m_options.crossOriginRequestPolicy == AllowCrossOriginRequests)
606 return m_sameOriginRequest && securityOrigin()->canRequest(url);
609 bool DocumentThreadableLoader::isAllowedByContentSecurityPolicy(const KURL& url) const
611 if (m_options.contentSecurityPolicyEnforcement != EnforceConnectSrcDirective)
613 return m_document.contentSecurityPolicy()->allowConnectToSource(url);
616 StoredCredentials DocumentThreadableLoader::effectiveAllowCredentials() const
618 if (m_forceDoNotAllowStoredCredentials)
619 return DoNotAllowStoredCredentials;
620 return m_resourceLoaderOptions.allowCredentials;
623 SecurityOrigin* DocumentThreadableLoader::securityOrigin() const
625 return m_securityOrigin ? m_securityOrigin.get() : m_document.securityOrigin();