Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / loader / DocumentThreadableLoader.cpp
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