| Index: Source/core/loader/DocumentThreadableLoader.cpp
|
| diff --git a/Source/core/loader/DocumentThreadableLoader.cpp b/Source/core/loader/DocumentThreadableLoader.cpp
|
| index 40891afc676d512ddcc161ff7ebda45b34e0d33c..b06ea8c34b2826f8e7d618ec4a85ca98258167ac 100644
|
| --- a/Source/core/loader/DocumentThreadableLoader.cpp
|
| +++ b/Source/core/loader/DocumentThreadableLoader.cpp
|
| @@ -145,7 +145,10 @@ DocumentThreadableLoader::DocumentThreadableLoader(Document& document, Threadabl
|
| ASSERT(m_async || request.httpReferrer().isEmpty());
|
|
|
| if (!m_sameOriginRequest && m_options.crossOriginRequestPolicy == DenyCrossOriginRequests) {
|
| - m_client->didFail(ResourceError(errorDomainBlinkInternal, 0, request.url().string(), "Cross origin requests are not supported."));
|
| + ThreadableLoaderClient* client = m_client;
|
| + clear();
|
| + client->didFail(ResourceError(errorDomainBlinkInternal, 0, request.url().string(), "Cross origin requests are not supported."));
|
| + // |this| may be dead here.
|
| return;
|
| }
|
|
|
| @@ -186,6 +189,7 @@ DocumentThreadableLoader::DocumentThreadableLoader(Document& document, Threadabl
|
| }
|
|
|
| dispatchInitialRequest(request);
|
| + // |this| may be dead here in async mode.
|
| }
|
|
|
| void DocumentThreadableLoader::dispatchInitialRequest(const ResourceRequest& request)
|
| @@ -198,18 +202,24 @@ void DocumentThreadableLoader::dispatchInitialRequest(const ResourceRequest& req
|
| ASSERT(m_options.crossOriginRequestPolicy == UseAccessControl);
|
|
|
| makeCrossOriginAccessRequest(request);
|
| + // |this| may be dead here in async mode.
|
| }
|
|
|
| void DocumentThreadableLoader::makeCrossOriginAccessRequest(const ResourceRequest& request)
|
| {
|
| ASSERT(m_options.crossOriginRequestPolicy == UseAccessControl);
|
| + ASSERT(m_client);
|
| + ASSERT(!resource());
|
|
|
| // Cross-origin requests are only allowed certain registered schemes.
|
| // We would catch this when checking response headers later, but there
|
| // is no reason to send a request, preflighted or not, that's guaranteed
|
| // to be denied.
|
| if (!SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(request.url().protocol())) {
|
| - m_client->didFailAccessControlCheck(ResourceError(errorDomainBlinkInternal, 0, request.url().string(), "Cross origin requests are only supported for protocol schemes: " + SchemeRegistry::listOfCORSEnabledURLSchemes() + "."));
|
| + ThreadableLoaderClient* client = m_client;
|
| + clear();
|
| + client->didFailAccessControlCheck(ResourceError(errorDomainBlinkInternal, 0, request.url().string(), "Cross origin requests are only supported for protocol schemes: " + SchemeRegistry::listOfCORSEnabledURLSchemes() + "."));
|
| + // |this| may be dead here in async mode.
|
| return;
|
| }
|
|
|
| @@ -273,25 +283,28 @@ void DocumentThreadableLoader::overrideTimeout(unsigned long timeoutMilliseconds
|
| void DocumentThreadableLoader::cancel()
|
| {
|
| cancelWithError(ResourceError());
|
| + // |this| may be dead here.
|
| }
|
|
|
| void DocumentThreadableLoader::cancelWithError(const ResourceError& error)
|
| {
|
| - RefPtr<DocumentThreadableLoader> protect(this);
|
| -
|
| // Cancel can re-enter and m_resource might be null here as a result.
|
| - if (m_client && resource()) {
|
| - ResourceError errorForCallback = error;
|
| - if (errorForCallback.isNull()) {
|
| - // FIXME: This error is sent to the client in didFail(), so it should not be an internal one. Use FrameLoaderClient::cancelledError() instead.
|
| - errorForCallback = ResourceError(errorDomainBlinkInternal, 0, resource()->url().string(), "Load cancelled");
|
| - errorForCallback.setIsCancellation(true);
|
| - }
|
| - m_client->didFail(errorForCallback);
|
| + if (!m_client || !resource()) {
|
| + clear();
|
| + return;
|
| }
|
| - clearResource();
|
| - m_client = 0;
|
| - m_requestStartedSeconds = 0.0;
|
| +
|
| + ResourceError errorForCallback = error;
|
| + if (errorForCallback.isNull()) {
|
| + // FIXME: This error is sent to the client in didFail(), so it should not be an internal one. Use FrameLoaderClient::cancelledError() instead.
|
| + errorForCallback = ResourceError(errorDomainBlinkInternal, 0, resource()->url().string(), "Load cancelled");
|
| + errorForCallback.setIsCancellation(true);
|
| + }
|
| +
|
| + ThreadableLoaderClient* client = m_client;
|
| + clear();
|
| + client->didFail(errorForCallback);
|
| + // |this| may be dead here in async mode.
|
| }
|
|
|
| void DocumentThreadableLoader::setDefersLoading(bool value)
|
| @@ -300,6 +313,18 @@ void DocumentThreadableLoader::setDefersLoading(bool value)
|
| resource()->setDefersLoading(value);
|
| }
|
|
|
| +void DocumentThreadableLoader::clear()
|
| +{
|
| + m_client = 0;
|
| +
|
| + if (!m_async)
|
| + return;
|
| +
|
| + clearResource();
|
| + m_timeoutTimer.stop();
|
| + m_requestStartedSeconds = 0.0;
|
| +}
|
| +
|
| // In this method, we can clear |request| to tell content::WebURLLoaderImpl of
|
| // Chromium not to follow the redirect. This works only when this method is
|
| // called by RawResource::willSendRequest(). If called by
|
| @@ -312,22 +337,22 @@ void DocumentThreadableLoader::redirectReceived(Resource* resource, ResourceRequ
|
| ASSERT_UNUSED(resource, resource == this->resource());
|
| ASSERT(m_async);
|
|
|
| - RefPtr<DocumentThreadableLoader> protect(this);
|
| -
|
| if (m_actualRequest) {
|
| reportResponseReceived(resource->identifier(), redirectResponse);
|
|
|
| - clearResource();
|
| - request = ResourceRequest();
|
| -
|
| - m_requestStartedSeconds = 0.0;
|
| -
|
| handlePreflightFailure(redirectResponse.url().string(), "Response for preflight is invalid (redirect)");
|
| + // |this| may be dead here.
|
| +
|
| + request = ResourceRequest();
|
|
|
| return;
|
| }
|
|
|
| if (m_redirectMode == WebURLRequest::FetchRedirectModeManual) {
|
| + // Keep |this| alive even if the client release a reference in
|
| + // responseReceived().
|
| + RefPtr<DocumentThreadableLoader> protect(this);
|
| +
|
| // We use |m_redirectMode| to check the original redirect mode.
|
| // |request| is a new request for redirect. So we don't set the redirect
|
| // mode of it in WebURLLoaderImpl::Context::OnReceivedRedirect().
|
| @@ -339,19 +364,25 @@ void DocumentThreadableLoader::redirectReceived(Resource* resource, ResourceRequ
|
| // will have to read the body. And also HTTPCache changes will be needed
|
| // because it doesn't store the body of redirect responses.
|
| responseReceived(resource, redirectResponse, adoptPtr(new EmptyDataHandle()));
|
| - notifyFinished(resource);
|
| - clearResource();
|
| +
|
| + if (m_client) {
|
| + ASSERT(!m_actualRequest);
|
| + notifyFinished(resource);
|
| + }
|
| +
|
| request = ResourceRequest();
|
| +
|
| return;
|
| }
|
|
|
| if (m_redirectMode == WebURLRequest::FetchRedirectModeError || !isAllowedByContentSecurityPolicy(request.url(), ContentSecurityPolicy::DidRedirect)) {
|
| - m_client->didFailRedirectCheck();
|
| + ThreadableLoaderClient* client = m_client;
|
| + clear();
|
| + client->didFailRedirectCheck();
|
| + // |this| may be dead here.
|
|
|
| - clearResource();
|
| request = ResourceRequest();
|
|
|
| - m_requestStartedSeconds = 0.0;
|
| return;
|
| }
|
|
|
| @@ -363,7 +394,10 @@ void DocumentThreadableLoader::redirectReceived(Resource* resource, ResourceRequ
|
| }
|
|
|
| if (m_corsRedirectLimit <= 0) {
|
| - m_client->didFailRedirectCheck();
|
| + ThreadableLoaderClient* client = m_client;
|
| + clear();
|
| + client->didFailRedirectCheck();
|
| + // |this| may be dead here.
|
| } else if (m_options.crossOriginRequestPolicy == UseAccessControl) {
|
| --m_corsRedirectLimit;
|
|
|
| @@ -411,19 +445,22 @@ void DocumentThreadableLoader::redirectReceived(Resource* resource, ResourceRequ
|
| for (const auto& header : m_simpleRequestHeaders)
|
| request.setHTTPHeaderField(header.key, header.value);
|
| makeCrossOriginAccessRequest(request);
|
| + // |this| may be dead here.
|
| return;
|
| }
|
|
|
| - ResourceError error(errorDomainBlinkInternal, 0, redirectResponse.url().string(), accessControlErrorDescription);
|
| - m_client->didFailAccessControlCheck(error);
|
| + ThreadableLoaderClient* client = m_client;
|
| + clear();
|
| + client->didFailAccessControlCheck(ResourceError(errorDomainBlinkInternal, 0, redirectResponse.url().string(), accessControlErrorDescription));
|
| + // |this| may be dead here.
|
| } else {
|
| - m_client->didFailRedirectCheck();
|
| + ThreadableLoaderClient* client = m_client;
|
| + clear();
|
| + client->didFailRedirectCheck();
|
| + // |this| may be dead here.
|
| }
|
|
|
| - clearResource();
|
| request = ResourceRequest();
|
| -
|
| - m_requestStartedSeconds = 0.0;
|
| }
|
|
|
| void DocumentThreadableLoader::dataSent(Resource* resource, unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
|
| @@ -433,6 +470,7 @@ void DocumentThreadableLoader::dataSent(Resource* resource, unsigned long long b
|
| ASSERT(m_async);
|
|
|
| m_client->didSendData(bytesSent, totalBytesToBeSent);
|
| + // |this| may be dead here.
|
| }
|
|
|
| void DocumentThreadableLoader::dataDownloaded(Resource* resource, int dataLength)
|
| @@ -443,6 +481,7 @@ void DocumentThreadableLoader::dataDownloaded(Resource* resource, int dataLength
|
| ASSERT(m_async);
|
|
|
| m_client->didDownloadData(dataLength);
|
| + // |this| may be dead here.
|
| }
|
|
|
| void DocumentThreadableLoader::didReceiveResourceTiming(Resource* resource, const ResourceTimingInfo& info)
|
| @@ -452,6 +491,7 @@ void DocumentThreadableLoader::didReceiveResourceTiming(Resource* resource, cons
|
| ASSERT(m_async);
|
|
|
| m_client->didReceiveResourceTiming(info);
|
| + // |this| may be dead here.
|
| }
|
|
|
| void DocumentThreadableLoader::responseReceived(Resource* resource, const ResourceResponse& response, PassOwnPtr<WebDataConsumerHandle> handle)
|
| @@ -463,6 +503,7 @@ void DocumentThreadableLoader::responseReceived(Resource* resource, const Resour
|
| m_isUsingDataConsumerHandle = true;
|
|
|
| handleResponse(resource->identifier(), response, handle);
|
| + // |this| may be dead here.
|
| }
|
|
|
| void DocumentThreadableLoader::handlePreflightResponse(const ResourceResponse& response)
|
| @@ -471,11 +512,13 @@ void DocumentThreadableLoader::handlePreflightResponse(const ResourceResponse& r
|
|
|
| if (!passesAccessControlCheck(response, effectiveAllowCredentials(), securityOrigin(), accessControlErrorDescription, m_requestContext)) {
|
| handlePreflightFailure(response.url().string(), "Response to preflight request doesn't pass access control check: " + accessControlErrorDescription);
|
| + // |this| may be dead here in async mode.
|
| return;
|
| }
|
|
|
| if (!passesPreflightStatusCheck(response, accessControlErrorDescription)) {
|
| handlePreflightFailure(response.url().string(), accessControlErrorDescription);
|
| + // |this| may be dead here in async mode.
|
| return;
|
| }
|
|
|
| @@ -484,6 +527,7 @@ void DocumentThreadableLoader::handlePreflightResponse(const ResourceResponse& r
|
| || !preflightResult->allowsCrossOriginMethod(m_actualRequest->httpMethod(), accessControlErrorDescription)
|
| || !preflightResult->allowsCrossOriginHeaders(m_actualRequest->httpHeaderFields(), accessControlErrorDescription)) {
|
| handlePreflightFailure(response.url().string(), accessControlErrorDescription);
|
| + // |this| may be dead here in async mode.
|
| return;
|
| }
|
|
|
| @@ -506,6 +550,7 @@ void DocumentThreadableLoader::handleResponse(unsigned long identifier, const Re
|
| if (m_actualRequest) {
|
| reportResponseReceived(identifier, response);
|
| handlePreflightResponse(response);
|
| + // |this| may be dead here in async mode.
|
| return;
|
| }
|
|
|
| @@ -522,6 +567,7 @@ void DocumentThreadableLoader::handleResponse(unsigned long identifier, const Re
|
| ASSERT(m_fallbackRequestForServiceWorker);
|
| reportResponseReceived(identifier, response);
|
| loadFallbackRequestForServiceWorker();
|
| + // |this| may be dead here in async mode.
|
| return;
|
| }
|
| m_fallbackRequestForServiceWorker = nullptr;
|
| @@ -545,7 +591,11 @@ void DocumentThreadableLoader::handleResponse(unsigned long identifier, const Re
|
| String accessControlErrorDescription;
|
| if (!passesAccessControlCheck(response, effectiveAllowCredentials(), securityOrigin(), accessControlErrorDescription, m_requestContext)) {
|
| reportResponseReceived(identifier, response);
|
| - m_client->didFailAccessControlCheck(ResourceError(errorDomainBlinkInternal, 0, response.url().string(), accessControlErrorDescription));
|
| +
|
| + ThreadableLoaderClient* client = m_client;
|
| + clear();
|
| + client->didFailAccessControlCheck(ResourceError(errorDomainBlinkInternal, 0, response.url().string(), accessControlErrorDescription));
|
| + // |this| may be dead here.
|
| return;
|
| }
|
| }
|
| @@ -558,6 +608,7 @@ void DocumentThreadableLoader::setSerializedCachedMetadata(Resource*, const char
|
| if (m_actualRequest)
|
| return;
|
| m_client->didReceiveCachedMetadata(data, size);
|
| + // |this| may be dead here.
|
| }
|
|
|
| void DocumentThreadableLoader::dataReceived(Resource* resource, const char* data, unsigned dataLength)
|
| @@ -569,6 +620,7 @@ void DocumentThreadableLoader::dataReceived(Resource* resource, const char* data
|
| return;
|
|
|
| handleReceivedData(data, dataLength);
|
| + // |this| may be dead here.
|
| }
|
|
|
| void DocumentThreadableLoader::handleReceivedData(const char* data, unsigned dataLength)
|
| @@ -582,6 +634,7 @@ void DocumentThreadableLoader::handleReceivedData(const char* data, unsigned dat
|
| ASSERT(!m_fallbackRequestForServiceWorker);
|
|
|
| m_client->didReceiveData(data, dataLength);
|
| + // |this| may be dead here in async mode.
|
| }
|
|
|
| void DocumentThreadableLoader::notifyFinished(Resource* resource)
|
| @@ -590,12 +643,13 @@ void DocumentThreadableLoader::notifyFinished(Resource* resource)
|
| ASSERT(resource == this->resource());
|
| ASSERT(m_async);
|
|
|
| - m_timeoutTimer.stop();
|
| -
|
| - if (resource->errorOccurred())
|
| - m_client->didFail(resource->resourceError());
|
| - else
|
| + if (resource->errorOccurred()) {
|
| + handleError(resource->resourceError());
|
| + // |this| may be dead here.
|
| + } else {
|
| handleSuccessfulFinish(resource->identifier(), resource->loadFinishTime());
|
| + // |this| may be dead here.
|
| + }
|
| }
|
|
|
| void DocumentThreadableLoader::handleSuccessfulFinish(unsigned long identifier, double finishTime)
|
| @@ -603,14 +657,25 @@ void DocumentThreadableLoader::handleSuccessfulFinish(unsigned long identifier,
|
| ASSERT(!m_fallbackRequestForServiceWorker);
|
|
|
| if (m_actualRequest) {
|
| + // FIXME: Timeout should be applied to whole fetch, not for each of
|
| + // preflight and actual request.
|
| + m_timeoutTimer.stop();
|
| ASSERT(!m_sameOriginRequest);
|
| ASSERT(m_options.crossOriginRequestPolicy == UseAccessControl);
|
| loadActualRequest();
|
| - } else {
|
| - // FIXME: Should prevent timeout from being overridden after finished loading, without
|
| - // resetting m_requestStartedSeconds to 0.0
|
| - m_client->didFinishLoading(identifier, finishTime);
|
| + return;
|
| + }
|
| +
|
| + ThreadableLoaderClient* client = m_client;
|
| + m_client = 0;
|
| + // Don't clear the resource as the client may need to access the downloaded
|
| + // file which will be released when the resource is destoryed.
|
| + if (m_async) {
|
| + m_timeoutTimer.stop();
|
| + m_requestStartedSeconds = 0.0;
|
| }
|
| + client->didFinishLoading(identifier, finishTime);
|
| + // |this| may be dead here in async mode.
|
| }
|
|
|
| void DocumentThreadableLoader::didTimeout(Timer<DocumentThreadableLoader>* timer)
|
| @@ -623,6 +688,7 @@ void DocumentThreadableLoader::didTimeout(Timer<DocumentThreadableLoader>* timer
|
| ResourceError error("net", timeoutError, resource()->url(), String());
|
| error.setIsTimeout(true);
|
| cancelWithError(error);
|
| + // |this| may be dead here.
|
| }
|
|
|
| void DocumentThreadableLoader::loadFallbackRequestForServiceWorker()
|
| @@ -630,6 +696,7 @@ void DocumentThreadableLoader::loadFallbackRequestForServiceWorker()
|
| clearResource();
|
| OwnPtr<ResourceRequest> fallbackRequest(m_fallbackRequestForServiceWorker.release());
|
| dispatchInitialRequest(*fallbackRequest);
|
| + // |this| may be dead here in async mode.
|
| }
|
|
|
| void DocumentThreadableLoader::loadActualRequest()
|
| @@ -653,9 +720,23 @@ void DocumentThreadableLoader::handlePreflightFailure(const String& url, const S
|
| // Prevent handleSuccessfulFinish() from bypassing access check.
|
| m_actualRequest = nullptr;
|
|
|
| - // FIXME: Should prevent timeout from being overridden after preflight failure, without
|
| - // resetting m_requestStartedSeconds to 0.0
|
| - m_client->didFailAccessControlCheck(error);
|
| + ThreadableLoaderClient* client = m_client;
|
| + clear();
|
| + client->didFailAccessControlCheck(error);
|
| + // |this| may be dead here in async mode.
|
| +}
|
| +
|
| +void DocumentThreadableLoader::handleError(const ResourceError& error)
|
| +{
|
| + // Copy the ResourceError instance to make it sure that the passed
|
| + // ResourceError is alive during didFail() even when the Resource is
|
| + // destructed during didFail().
|
| + ResourceError copiedError = error;
|
| +
|
| + ThreadableLoaderClient* client = m_client;
|
| + clear();
|
| + client->didFail(copiedError);
|
| + // |this| may be dead here.
|
| }
|
|
|
| void DocumentThreadableLoader::loadRequest(const ResourceRequest& request, ResourceLoaderOptions resourceLoaderOptions)
|
| @@ -723,10 +804,23 @@ void DocumentThreadableLoader::loadRequest(const ResourceRequest& request, Resou
|
|
|
| handleResponse(identifier, response, nullptr);
|
|
|
| + // handleResponse() may detect an error. In such a case (check |m_client|
|
| + // as it gets reset by clear() call), skip the rest.
|
| + //
|
| + // |this| is alive here since loadResourceSynchronously() keeps it alive
|
| + // until the end of the function.
|
| + if (!m_client)
|
| + return;
|
| +
|
| SharedBuffer* data = resource->resourceBuffer();
|
| if (data)
|
| handleReceivedData(data->data(), data->size());
|
|
|
| + // The client may cancel this loader in handleReceivedData(). In such a
|
| + // case, skip the rest.
|
| + if (!m_client)
|
| + return;
|
| +
|
| handleSuccessfulFinish(identifier, 0.0);
|
| }
|
|
|
|
|