Home | History | Annotate | Download | only in loader
      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/Resource.h"
     39 #include "core/fetch/ResourceFetcher.h"
     40 #include "core/frame/LocalFrame.h"
     41 #include "core/frame/csp/ContentSecurityPolicy.h"
     42 #include "core/inspector/InspectorInstrumentation.h"
     43 #include "core/inspector/InspectorTraceEvents.h"
     44 #include "core/loader/CrossOriginPreflightResultCache.h"
     45 #include "core/loader/DocumentThreadableLoaderClient.h"
     46 #include "core/loader/FrameLoader.h"
     47 #include "core/loader/ThreadableLoaderClient.h"
     48 #include "platform/SharedBuffer.h"
     49 #include "platform/network/ResourceRequest.h"
     50 #include "platform/weborigin/SchemeRegistry.h"
     51 #include "platform/weborigin/SecurityOrigin.h"
     52 #include "wtf/Assertions.h"
     53 
     54 namespace WebCore {
     55 
     56 void DocumentThreadableLoader::loadResourceSynchronously(Document& document, const ResourceRequest& request, ThreadableLoaderClient& client, const ThreadableLoaderOptions& options, const ResourceLoaderOptions& resourceLoaderOptions)
     57 {
     58     // The loader will be deleted as soon as this function exits.
     59     RefPtr<DocumentThreadableLoader> loader = adoptRef(new DocumentThreadableLoader(document, &client, LoadSynchronously, request, options, resourceLoaderOptions));
     60     ASSERT(loader->hasOneRef());
     61 }
     62 
     63 PassRefPtr<DocumentThreadableLoader> DocumentThreadableLoader::create(Document& document, ThreadableLoaderClient* client, const ResourceRequest& request, const ThreadableLoaderOptions& options, const ResourceLoaderOptions& resourceLoaderOptions)
     64 {
     65     RefPtr<DocumentThreadableLoader> loader = adoptRef(new DocumentThreadableLoader(document, client, LoadAsynchronously, request, options, resourceLoaderOptions));
     66     if (!loader->resource())
     67         loader = nullptr;
     68     return loader.release();
     69 }
     70 
     71 DocumentThreadableLoader::DocumentThreadableLoader(Document& document, ThreadableLoaderClient* client, BlockingBehavior blockingBehavior, const ResourceRequest& request, const ThreadableLoaderOptions& options, const ResourceLoaderOptions& resourceLoaderOptions)
     72     : m_client(client)
     73     , m_document(document)
     74     , m_options(options)
     75     , m_resourceLoaderOptions(resourceLoaderOptions)
     76     , m_forceDoNotAllowStoredCredentials(false)
     77     , m_securityOrigin(m_resourceLoaderOptions.securityOrigin)
     78     , m_sameOriginRequest(securityOrigin()->canRequest(request.url()))
     79     , m_simpleRequest(true)
     80     , m_async(blockingBehavior == LoadAsynchronously)
     81     , m_timeoutTimer(this, &DocumentThreadableLoader::didTimeout)
     82 {
     83     ASSERT(client);
     84     // Setting an outgoing referer is only supported in the async code path.
     85     ASSERT(m_async || request.httpReferrer().isEmpty());
     86 
     87     // Save any CORS simple headers on the request here. If this request redirects cross-origin, we cancel the old request
     88     // create a new one, and copy these headers.
     89     const HTTPHeaderMap& headerMap = request.httpHeaderFields();
     90     HTTPHeaderMap::const_iterator end = headerMap.end();
     91     for (HTTPHeaderMap::const_iterator it = headerMap.begin(); it != end; ++it) {
     92         if (isOnAccessControlSimpleRequestHeaderWhitelist(it->key, it->value))
     93             m_simpleRequestHeaders.add(it->key, it->value);
     94     }
     95 
     96     if (m_sameOriginRequest || m_options.crossOriginRequestPolicy == AllowCrossOriginRequests) {
     97         loadRequest(request, m_resourceLoaderOptions);
     98         return;
     99     }
    100 
    101     if (m_options.crossOriginRequestPolicy == DenyCrossOriginRequests) {
    102         m_client->didFail(ResourceError(errorDomainBlinkInternal, 0, request.url().string(), "Cross origin requests are not supported."));
    103         return;
    104     }
    105 
    106     makeCrossOriginAccessRequest(request);
    107 }
    108 
    109 void DocumentThreadableLoader::makeCrossOriginAccessRequest(const ResourceRequest& request)
    110 {
    111     ASSERT(m_options.crossOriginRequestPolicy == UseAccessControl);
    112 
    113     if ((m_options.preflightPolicy == ConsiderPreflight && isSimpleCrossOriginAccessRequest(request.httpMethod(), request.httpHeaderFields())) || m_options.preflightPolicy == PreventPreflight) {
    114         // Cross-origin requests are only allowed for HTTP and registered schemes. We would catch this when checking response headers later, but there is no reason to send a request that's guaranteed to be denied.
    115         if (!SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(request.url().protocol())) {
    116             m_client->didFailAccessControlCheck(ResourceError(errorDomainBlinkInternal, 0, request.url().string(), "Cross origin requests are only supported for HTTP."));
    117             return;
    118         }
    119 
    120         ResourceRequest crossOriginRequest(request);
    121         ResourceLoaderOptions crossOriginOptions(m_resourceLoaderOptions);
    122         updateRequestForAccessControl(crossOriginRequest, securityOrigin(), effectiveAllowCredentials());
    123         loadRequest(crossOriginRequest, crossOriginOptions);
    124     } else {
    125         m_simpleRequest = false;
    126 
    127         OwnPtr<ResourceRequest> crossOriginRequest = adoptPtr(new ResourceRequest(request));
    128         OwnPtr<ResourceLoaderOptions> crossOriginOptions = adoptPtr(new ResourceLoaderOptions(m_resourceLoaderOptions));
    129         // Do not set the Origin header for preflight requests.
    130         updateRequestForAccessControl(*crossOriginRequest, 0, effectiveAllowCredentials());
    131         m_actualRequest = crossOriginRequest.release();
    132         m_actualOptions = crossOriginOptions.release();
    133 
    134         if (CrossOriginPreflightResultCache::shared().canSkipPreflight(securityOrigin()->toString(), m_actualRequest->url(), effectiveAllowCredentials(), m_actualRequest->httpMethod(), m_actualRequest->httpHeaderFields())) {
    135             loadActualRequest();
    136         } else {
    137             ResourceRequest preflightRequest = createAccessControlPreflightRequest(*m_actualRequest, securityOrigin());
    138             // Create a ResourceLoaderOptions for preflight.
    139             ResourceLoaderOptions preflightOptions = *m_actualOptions;
    140             preflightOptions.allowCredentials = DoNotAllowStoredCredentials;
    141             loadRequest(preflightRequest, preflightOptions);
    142         }
    143     }
    144 }
    145 
    146 DocumentThreadableLoader::~DocumentThreadableLoader()
    147 {
    148 }
    149 
    150 void DocumentThreadableLoader::cancel()
    151 {
    152     cancelWithError(ResourceError());
    153 }
    154 
    155 void DocumentThreadableLoader::cancelWithError(const ResourceError& error)
    156 {
    157     RefPtr<DocumentThreadableLoader> protect(this);
    158 
    159     // Cancel can re-enter and m_resource might be null here as a result.
    160     if (m_client && resource()) {
    161         ResourceError errorForCallback = error;
    162         if (errorForCallback.isNull()) {
    163             // FIXME: This error is sent to the client in didFail(), so it should not be an internal one. Use FrameLoaderClient::cancelledError() instead.
    164             errorForCallback = ResourceError(errorDomainBlinkInternal, 0, resource()->url().string(), "Load cancelled");
    165             errorForCallback.setIsCancellation(true);
    166         }
    167         m_client->didFail(errorForCallback);
    168     }
    169     clearResource();
    170     m_client = 0;
    171 }
    172 
    173 void DocumentThreadableLoader::setDefersLoading(bool value)
    174 {
    175     if (resource())
    176         resource()->setDefersLoading(value);
    177 }
    178 
    179 void DocumentThreadableLoader::redirectReceived(Resource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse)
    180 {
    181     ASSERT(m_client);
    182     ASSERT_UNUSED(resource, resource == this->resource());
    183 
    184     RefPtr<DocumentThreadableLoader> protect(this);
    185     if (!isAllowedByPolicy(request.url())) {
    186         m_client->didFailRedirectCheck();
    187         request = ResourceRequest();
    188         return;
    189     }
    190 
    191     // Allow same origin requests to continue after allowing clients to audit the redirect.
    192     if (isAllowedRedirect(request.url())) {
    193         if (m_client->isDocumentThreadableLoaderClient())
    194             static_cast<DocumentThreadableLoaderClient*>(m_client)->willSendRequest(request, redirectResponse);
    195         return;
    196     }
    197 
    198     // When using access control, only simple cross origin requests are allowed to redirect. The new request URL must have a supported
    199     // scheme and not contain the userinfo production. In addition, the redirect response must pass the access control check if the
    200     // original request was not same-origin.
    201     if (m_options.crossOriginRequestPolicy == UseAccessControl) {
    202 
    203         InspectorInstrumentation::didReceiveCORSRedirectResponse(m_document.frame(), resource->identifier(), m_document.frame()->loader().documentLoader(), redirectResponse, 0);
    204 
    205         bool allowRedirect = false;
    206         String accessControlErrorDescription;
    207 
    208         if (m_simpleRequest) {
    209             allowRedirect = CrossOriginAccessControl::isLegalRedirectLocation(request.url(), accessControlErrorDescription)
    210                 && (m_sameOriginRequest || passesAccessControlCheck(redirectResponse, effectiveAllowCredentials(), securityOrigin(), accessControlErrorDescription));
    211         } else {
    212             accessControlErrorDescription = "The request was redirected to '"+ request.url().string() + "', which is disallowed for cross-origin requests that require preflight.";
    213         }
    214 
    215         if (allowRedirect) {
    216             // FIXME: consider combining this with CORS redirect handling performed by
    217             // CrossOriginAccessControl::handleRedirect().
    218             clearResource();
    219 
    220             RefPtr<SecurityOrigin> originalOrigin = SecurityOrigin::create(redirectResponse.url());
    221             RefPtr<SecurityOrigin> requestOrigin = SecurityOrigin::create(request.url());
    222             // If the original request wasn't same-origin, then if the request URL origin is not same origin with the original URL origin,
    223             // set the source origin to a globally unique identifier. (If the original request was same-origin, the origin of the new request
    224             // should be the original URL origin.)
    225             if (!m_sameOriginRequest && !originalOrigin->isSameSchemeHostPort(requestOrigin.get()))
    226                 m_securityOrigin = SecurityOrigin::createUnique();
    227             // Force any subsequent requests to use these checks.
    228             m_sameOriginRequest = false;
    229 
    230             // Since the request is no longer same-origin, if the user didn't request credentials in
    231             // the first place, update our state so we neither request them nor expect they must be allowed.
    232             if (m_resourceLoaderOptions.credentialsRequested == ClientDidNotRequestCredentials)
    233                 m_forceDoNotAllowStoredCredentials = true;
    234 
    235             // Remove any headers that may have been added by the network layer that cause access control to fail.
    236             request.clearHTTPReferrer();
    237             request.clearHTTPOrigin();
    238             request.clearHTTPUserAgent();
    239             // Add any CORS simple request headers which we previously saved from the original request.
    240             HTTPHeaderMap::const_iterator end = m_simpleRequestHeaders.end();
    241             for (HTTPHeaderMap::const_iterator it = m_simpleRequestHeaders.begin(); it != end; ++it) {
    242                 request.setHTTPHeaderField(it->key, it->value);
    243             }
    244             makeCrossOriginAccessRequest(request);
    245             return;
    246         }
    247 
    248         ResourceError error(errorDomainBlinkInternal, 0, redirectResponse.url().string(), accessControlErrorDescription);
    249         m_client->didFailAccessControlCheck(error);
    250     } else {
    251         m_client->didFailRedirectCheck();
    252     }
    253     request = ResourceRequest();
    254 }
    255 
    256 void DocumentThreadableLoader::dataSent(Resource* resource, unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
    257 {
    258     ASSERT(m_client);
    259     ASSERT_UNUSED(resource, resource == this->resource());
    260     m_client->didSendData(bytesSent, totalBytesToBeSent);
    261 }
    262 
    263 void DocumentThreadableLoader::dataDownloaded(Resource* resource, int dataLength)
    264 {
    265     ASSERT(m_client);
    266     ASSERT_UNUSED(resource, resource == this->resource());
    267     ASSERT(!m_actualRequest);
    268 
    269     m_client->didDownloadData(dataLength);
    270 }
    271 
    272 void DocumentThreadableLoader::responseReceived(Resource* resource, const ResourceResponse& response)
    273 {
    274     ASSERT_UNUSED(resource, resource == this->resource());
    275     handleResponse(resource->identifier(), response);
    276 }
    277 
    278 void DocumentThreadableLoader::handlePreflightResponse(unsigned long identifier, const ResourceResponse& response)
    279 {
    280     // Notifying the inspector here is necessary because a call to handlePreflightFailure() might synchronously
    281     // cause the underlying ResourceLoader to be cancelled before it tells the inspector about the response.
    282     // In that case, if we don't tell the inspector about the response now, the resource type in the inspector
    283     // will default to "other" instead of something more descriptive.
    284     DocumentLoader* loader = m_document.frame()->loader().documentLoader();
    285     TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ResourceReceiveResponse", "data", InspectorReceiveResponseEvent::data(identifier, m_document.frame(), response));
    286     // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
    287     InspectorInstrumentation::didReceiveResourceResponse(m_document.frame(), identifier, loader, response, resource() ? resource()->loader() : 0);
    288 
    289     String accessControlErrorDescription;
    290 
    291     if (!passesAccessControlCheck(response, effectiveAllowCredentials(), securityOrigin(), accessControlErrorDescription)) {
    292         handlePreflightFailure(response.url().string(), accessControlErrorDescription);
    293         return;
    294     }
    295 
    296     if (!passesPreflightStatusCheck(response, accessControlErrorDescription)) {
    297         handlePreflightFailure(response.url().string(), accessControlErrorDescription);
    298         return;
    299     }
    300 
    301     OwnPtr<CrossOriginPreflightResultCacheItem> preflightResult = adoptPtr(new CrossOriginPreflightResultCacheItem(effectiveAllowCredentials()));
    302     if (!preflightResult->parse(response, accessControlErrorDescription)
    303         || !preflightResult->allowsCrossOriginMethod(m_actualRequest->httpMethod(), accessControlErrorDescription)
    304         || !preflightResult->allowsCrossOriginHeaders(m_actualRequest->httpHeaderFields(), accessControlErrorDescription)) {
    305         handlePreflightFailure(response.url().string(), accessControlErrorDescription);
    306         return;
    307     }
    308 
    309     CrossOriginPreflightResultCache::shared().appendEntry(securityOrigin()->toString(), m_actualRequest->url(), preflightResult.release());
    310 }
    311 
    312 void DocumentThreadableLoader::handleResponse(unsigned long identifier, const ResourceResponse& response)
    313 {
    314     ASSERT(m_client);
    315 
    316     if (m_actualRequest) {
    317         handlePreflightResponse(identifier, response);
    318         return;
    319     }
    320 
    321     if (!m_sameOriginRequest && m_options.crossOriginRequestPolicy == UseAccessControl) {
    322         String accessControlErrorDescription;
    323         if (!passesAccessControlCheck(response, effectiveAllowCredentials(), securityOrigin(), accessControlErrorDescription)) {
    324             m_client->didFailAccessControlCheck(ResourceError(errorDomainBlinkInternal, 0, response.url().string(), accessControlErrorDescription));
    325             return;
    326         }
    327     }
    328 
    329     m_client->didReceiveResponse(identifier, response);
    330 }
    331 
    332 void DocumentThreadableLoader::dataReceived(Resource* resource, const char* data, int dataLength)
    333 {
    334     ASSERT_UNUSED(resource, resource == this->resource());
    335     handleReceivedData(data, dataLength);
    336 }
    337 
    338 void DocumentThreadableLoader::handleReceivedData(const char* data, int dataLength)
    339 {
    340     ASSERT(m_client);
    341     // Preflight data should be invisible to clients.
    342     if (!m_actualRequest)
    343         m_client->didReceiveData(data, dataLength);
    344 }
    345 
    346 void DocumentThreadableLoader::notifyFinished(Resource* resource)
    347 {
    348     ASSERT(m_client);
    349     ASSERT(resource == this->resource());
    350 
    351     m_timeoutTimer.stop();
    352 
    353     if (resource->errorOccurred())
    354         m_client->didFail(resource->resourceError());
    355     else
    356         handleSuccessfulFinish(resource->identifier(), resource->loadFinishTime());
    357 }
    358 
    359 void DocumentThreadableLoader::handleSuccessfulFinish(unsigned long identifier, double finishTime)
    360 {
    361     if (m_actualRequest) {
    362         ASSERT(!m_sameOriginRequest);
    363         ASSERT(m_options.crossOriginRequestPolicy == UseAccessControl);
    364         loadActualRequest();
    365     } else
    366         m_client->didFinishLoading(identifier, finishTime);
    367 }
    368 
    369 void DocumentThreadableLoader::didTimeout(Timer<DocumentThreadableLoader>* timer)
    370 {
    371     ASSERT_UNUSED(timer, timer == &m_timeoutTimer);
    372 
    373     // Using values from net/base/net_error_list.h ERR_TIMED_OUT,
    374     // Same as existing FIXME above - this error should be coming from FrameLoaderClient to be identifiable.
    375     static const int timeoutError = -7;
    376     ResourceError error("net", timeoutError, resource()->url(), String());
    377     error.setIsTimeout(true);
    378     cancelWithError(error);
    379 }
    380 
    381 void DocumentThreadableLoader::loadActualRequest()
    382 {
    383     OwnPtr<ResourceRequest> actualRequest;
    384     actualRequest.swap(m_actualRequest);
    385     OwnPtr<ResourceLoaderOptions> actualOptions;
    386     actualOptions.swap(m_actualOptions);
    387 
    388     actualRequest->setHTTPOrigin(securityOrigin()->toAtomicString());
    389 
    390     clearResource();
    391 
    392     loadRequest(*actualRequest, *actualOptions);
    393 }
    394 
    395 void DocumentThreadableLoader::handlePreflightFailure(const String& url, const String& errorDescription)
    396 {
    397     ResourceError error(errorDomainBlinkInternal, 0, url, errorDescription);
    398 
    399     // Prevent handleSuccessfulFinish() from bypassing access check.
    400     m_actualRequest = nullptr;
    401 
    402     m_client->didFailAccessControlCheck(error);
    403 }
    404 
    405 void DocumentThreadableLoader::loadRequest(const ResourceRequest& request, ResourceLoaderOptions resourceLoaderOptions)
    406 {
    407     // Any credential should have been removed from the cross-site requests.
    408     const KURL& requestURL = request.url();
    409     ASSERT(m_sameOriginRequest || requestURL.user().isEmpty());
    410     ASSERT(m_sameOriginRequest || requestURL.pass().isEmpty());
    411 
    412     // Update resourceLoaderOptions with enforced values.
    413     if (m_forceDoNotAllowStoredCredentials)
    414         resourceLoaderOptions.allowCredentials = DoNotAllowStoredCredentials;
    415     resourceLoaderOptions.securityOrigin = m_securityOrigin;
    416     if (m_async) {
    417         if (m_actualRequest) {
    418             resourceLoaderOptions.sniffContent = DoNotSniffContent;
    419             resourceLoaderOptions.dataBufferingPolicy = BufferData;
    420         }
    421 
    422         if (m_options.timeoutMilliseconds > 0)
    423             m_timeoutTimer.startOneShot(m_options.timeoutMilliseconds / 1000.0, FROM_HERE);
    424 
    425         FetchRequest newRequest(request, m_options.initiator, resourceLoaderOptions);
    426         ASSERT(!resource());
    427         if (request.targetType() == ResourceRequest::TargetIsMedia)
    428             setResource(m_document.fetcher()->fetchMedia(newRequest));
    429         else
    430             setResource(m_document.fetcher()->fetchRawResource(newRequest));
    431         if (resource() && resource()->loader()) {
    432             unsigned long identifier = resource()->identifier();
    433             InspectorInstrumentation::documentThreadableLoaderStartedLoadingForClient(&m_document, identifier, m_client);
    434         }
    435         return;
    436     }
    437 
    438     FetchRequest fetchRequest(request, m_options.initiator, resourceLoaderOptions);
    439     ResourcePtr<Resource> resource = m_document.fetcher()->fetchSynchronously(fetchRequest);
    440     ResourceResponse response = resource ? resource->response() : ResourceResponse();
    441     unsigned long identifier = resource ? resource->identifier() : std::numeric_limits<unsigned long>::max();
    442     ResourceError error = resource ? resource->resourceError() : ResourceError();
    443 
    444     InspectorInstrumentation::documentThreadableLoaderStartedLoadingForClient(&m_document, identifier, m_client);
    445 
    446     if (!resource) {
    447         m_client->didFail(error);
    448         return;
    449     }
    450 
    451     // No exception for file:/// resources, see <rdar://problem/4962298>.
    452     // Also, if we have an HTTP response, then it wasn't a network error in fact.
    453     if (!error.isNull() && !requestURL.isLocalFile() && response.httpStatusCode() <= 0) {
    454         m_client->didFail(error);
    455         return;
    456     }
    457 
    458     // FIXME: A synchronous request does not tell us whether a redirect happened or not, so we guess by comparing the
    459     // request and response URLs. This isn't a perfect test though, since a server can serve a redirect to the same URL that was
    460     // requested. Also comparing the request and response URLs as strings will fail if the requestURL still has its credentials.
    461     if (requestURL != response.url() && (!isAllowedByPolicy(response.url()) || !isAllowedRedirect(response.url()))) {
    462         m_client->didFailRedirectCheck();
    463         return;
    464     }
    465 
    466     handleResponse(identifier, response);
    467 
    468     SharedBuffer* data = resource->resourceBuffer();
    469     if (data)
    470         handleReceivedData(data->data(), data->size());
    471 
    472     handleSuccessfulFinish(identifier, 0.0);
    473 }
    474 
    475 bool DocumentThreadableLoader::isAllowedRedirect(const KURL& url) const
    476 {
    477     if (m_options.crossOriginRequestPolicy == AllowCrossOriginRequests)
    478         return true;
    479 
    480     return m_sameOriginRequest && securityOrigin()->canRequest(url);
    481 }
    482 
    483 bool DocumentThreadableLoader::isAllowedByPolicy(const KURL& url) const
    484 {
    485     if (m_options.contentSecurityPolicyEnforcement != EnforceConnectSrcDirective)
    486         return true;
    487     return m_document.contentSecurityPolicy()->allowConnectToSource(url);
    488 }
    489 
    490 StoredCredentials DocumentThreadableLoader::effectiveAllowCredentials() const
    491 {
    492     if (m_forceDoNotAllowStoredCredentials)
    493         return DoNotAllowStoredCredentials;
    494     return m_resourceLoaderOptions.allowCredentials;
    495 }
    496 
    497 SecurityOrigin* DocumentThreadableLoader::securityOrigin() const
    498 {
    499     return m_securityOrigin ? m_securityOrigin.get() : m_document.securityOrigin();
    500 }
    501 
    502 } // namespace WebCore
    503