blob: 2e0e51392731d5b7e74512584212381710decda6 [file] [log] [blame]
/*
* Copyright (C) 2009 Apple Inc. All Rights Reserved.
* Copyright (C) 2009, 2011 Google Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "core/workers/WorkerScriptLoader.h"
#include <memory>
#include "core/dom/ExecutionContext.h"
#include "core/html/parser/TextResourceDecoder.h"
#include "core/inspector/ConsoleMessage.h"
#include "core/loader/WorkerThreadableLoader.h"
#include "core/loader/resource/ScriptResource.h"
#include "core/origin_trials/OriginTrialContext.h"
#include "core/workers/WorkerGlobalScope.h"
#include "platform/HTTPNames.h"
#include "platform/loader/fetch/ResourceLoaderOptions.h"
#include "platform/loader/fetch/ResourceResponse.h"
#include "platform/loader/fetch/TextResourceDecoderOptions.h"
#include "platform/network/ContentSecurityPolicyResponseHeaders.h"
#include "platform/network/NetworkUtils.h"
#include "platform/weborigin/SecurityOrigin.h"
#include "platform/wtf/PtrUtil.h"
#include "platform/wtf/RefPtr.h"
#include "public/platform/WebAddressSpace.h"
namespace blink {
WorkerScriptLoader::WorkerScriptLoader()
: response_callback_(nullptr),
finished_callback_(nullptr),
request_context_(WebURLRequest::kRequestContextWorker),
response_address_space_(kWebAddressSpacePublic) {}
WorkerScriptLoader::~WorkerScriptLoader() {
// If |m_threadableLoader| is still working, we have to cancel it here.
// Otherwise WorkerScriptLoader::didFail() of the deleted |this| will be
// called from DocumentThreadableLoader::notifyFinished() when the frame
// will be destroyed.
if (need_to_cancel_)
Cancel();
}
void WorkerScriptLoader::LoadSynchronously(
ExecutionContext& execution_context,
const KURL& url,
WebAddressSpace creation_address_space) {
url_ = url;
execution_context_ = &execution_context;
ResourceRequest request(CreateResourceRequest(creation_address_space));
request.SetFetchCredentialsMode(WebURLRequest::kFetchCredentialsModeInclude);
SECURITY_DCHECK(execution_context.IsWorkerGlobalScope());
ThreadableLoaderOptions options;
options.fetch_request_mode = WebURLRequest::kFetchRequestModeNoCORS;
// FIXME: Should we add EnforceScriptSrcDirective here?
options.content_security_policy_enforcement =
kDoNotEnforceContentSecurityPolicy;
ResourceLoaderOptions resource_loader_options;
WorkerThreadableLoader::LoadResourceSynchronously(
ToWorkerGlobalScope(execution_context), request, *this, options,
resource_loader_options);
}
void WorkerScriptLoader::LoadAsynchronously(
ExecutionContext& execution_context,
const KURL& url,
WebURLRequest::FetchRequestMode fetch_request_mode,
WebURLRequest::FetchCredentialsMode fetch_credentials_mode,
WebAddressSpace creation_address_space,
std::unique_ptr<WTF::Closure> response_callback,
std::unique_ptr<WTF::Closure> finished_callback) {
DCHECK(response_callback || finished_callback);
response_callback_ = std::move(response_callback);
finished_callback_ = std::move(finished_callback);
url_ = url;
execution_context_ = &execution_context;
ResourceRequest request(CreateResourceRequest(creation_address_space));
request.SetFetchCredentialsMode(fetch_credentials_mode);
ThreadableLoaderOptions options;
options.fetch_request_mode = fetch_request_mode;
ResourceLoaderOptions resource_loader_options;
// During create, callbacks may happen which could remove the last reference
// to this object, while some of the callchain assumes that the client and
// loader wouldn't be deleted within callbacks.
// (E.g. see crbug.com/524694 for why we can't easily remove this protect)
RefPtr<WorkerScriptLoader> protect(this);
need_to_cancel_ = true;
threadable_loader_ = ThreadableLoader::Create(
execution_context, this, options, resource_loader_options);
threadable_loader_->Start(request);
if (failed_)
NotifyFinished();
}
const KURL& WorkerScriptLoader::ResponseURL() const {
DCHECK(!Failed());
return response_url_;
}
ResourceRequest WorkerScriptLoader::CreateResourceRequest(
WebAddressSpace creation_address_space) {
ResourceRequest request(url_);
request.SetHTTPMethod(HTTPNames::GET);
request.SetRequestContext(request_context_);
request.SetExternalRequestStateFromRequestorAddressSpace(
creation_address_space);
return request;
}
void WorkerScriptLoader::DidReceiveResponse(
unsigned long identifier,
const ResourceResponse& response,
std::unique_ptr<WebDataConsumerHandle> handle) {
DCHECK(!handle);
if (response.HttpStatusCode() / 100 != 2 && response.HttpStatusCode()) {
NotifyError();
return;
}
if (!ScriptResource::MimeTypeAllowedByNosniff(response)) {
execution_context_->AddConsoleMessage(ConsoleMessage::Create(
kSecurityMessageSource, kErrorMessageLevel,
"Refused to execute script from '" + url_.ElidedString() +
"' because its MIME type ('" + response.HttpContentType() +
"') is not executable, and strict MIME type checking is enabled."));
NotifyError();
return;
}
identifier_ = identifier;
response_url_ = response.Url();
response_encoding_ = response.TextEncodingName();
app_cache_id_ = response.AppCacheID();
referrer_policy_ = response.HttpHeaderField(HTTPNames::Referrer_Policy);
ProcessContentSecurityPolicy(response);
origin_trial_tokens_ = OriginTrialContext::ParseHeaderValue(
response.HttpHeaderField(HTTPNames::Origin_Trial));
if (NetworkUtils::IsReservedIPAddress(response.RemoteIPAddress())) {
response_address_space_ =
SecurityOrigin::Create(response_url_)->IsLocalhost()
? kWebAddressSpaceLocal
: kWebAddressSpacePrivate;
}
if (response_callback_)
(*response_callback_)();
}
void WorkerScriptLoader::DidReceiveData(const char* data, unsigned len) {
if (failed_)
return;
if (!decoder_) {
if (!response_encoding_.IsEmpty()) {
decoder_ = TextResourceDecoder::Create(TextResourceDecoderOptions(
TextResourceDecoderOptions::kPlainTextContent,
WTF::TextEncoding(response_encoding_)));
} else {
decoder_ = TextResourceDecoder::Create(TextResourceDecoderOptions(
TextResourceDecoderOptions::kPlainTextContent, UTF8Encoding()));
}
}
if (!len)
return;
source_text_.Append(decoder_->Decode(data, len));
}
void WorkerScriptLoader::DidReceiveCachedMetadata(const char* data, int size) {
cached_metadata_ = WTF::MakeUnique<Vector<char>>(size);
memcpy(cached_metadata_->data(), data, size);
}
void WorkerScriptLoader::DidFinishLoading(unsigned long identifier, double) {
need_to_cancel_ = false;
if (!failed_ && decoder_)
source_text_.Append(decoder_->Flush());
NotifyFinished();
}
void WorkerScriptLoader::DidFail(const ResourceError& error) {
need_to_cancel_ = false;
canceled_ = error.IsCancellation();
NotifyError();
}
void WorkerScriptLoader::DidFailRedirectCheck() {
// When didFailRedirectCheck() is called, the ResourceLoader for the script
// is not canceled yet. So we don't reset |m_needToCancel| here.
NotifyError();
}
void WorkerScriptLoader::Cancel() {
need_to_cancel_ = false;
if (threadable_loader_)
threadable_loader_->Cancel();
}
String WorkerScriptLoader::SourceText() {
return source_text_.ToString();
}
void WorkerScriptLoader::NotifyError() {
failed_ = true;
// notifyError() could be called before ThreadableLoader::create() returns
// e.g. from didFail(), and in that case m_threadableLoader is not yet set
// (i.e. still null).
// Since the callback invocation in notifyFinished() potentially delete
// |this| object, the callback invocation should be postponed until the
// create() call returns. See loadAsynchronously() for the postponed call.
if (threadable_loader_)
NotifyFinished();
}
void WorkerScriptLoader::NotifyFinished() {
if (!finished_callback_)
return;
std::unique_ptr<WTF::Closure> callback = std::move(finished_callback_);
(*callback)();
}
void WorkerScriptLoader::ProcessContentSecurityPolicy(
const ResourceResponse& response) {
// Per http://www.w3.org/TR/CSP2/#processing-model-workers, if the Worker's
// URL is not a GUID, then it grabs its CSP from the response headers
// directly. Otherwise, the Worker inherits the policy from the parent
// document (which is implemented in WorkerMessagingProxy, and
// m_contentSecurityPolicy should be left as nullptr to inherit the policy).
if (!response.Url().ProtocolIs("blob") &&
!response.Url().ProtocolIs("file") &&
!response.Url().ProtocolIs("filesystem")) {
content_security_policy_ = ContentSecurityPolicy::Create();
content_security_policy_->SetOverrideURLForSelf(response.Url());
content_security_policy_->DidReceiveHeaders(
ContentSecurityPolicyResponseHeaders(response));
}
}
} // namespace blink