blob: 5a6411d7e8dbba701486257356966ce1a6b2dca3 [file] [log] [blame]
/*
* Copyright (C) 2008 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/WorkerGlobalScope.h"
#include "bindings/core/v8/ExceptionState.h"
#include "bindings/core/v8/ScriptSourceCode.h"
#include "bindings/core/v8/SourceLocation.h"
#include "bindings/core/v8/V8AbstractEventListener.h"
#include "bindings/core/v8/WorkerOrWorkletScriptController.h"
#include "core/dom/ContextLifecycleNotifier.h"
#include "core/dom/ExceptionCode.h"
#include "core/dom/SuspendableObject.h"
#include "core/events/ErrorEvent.h"
#include "core/events/Event.h"
#include "core/frame/DOMTimerCoordinator.h"
#include "core/inspector/ConsoleMessage.h"
#include "core/inspector/ConsoleMessageStorage.h"
#include "core/inspector/WorkerInspectorController.h"
#include "core/inspector/WorkerThreadDebugger.h"
#include "core/loader/WorkerThreadableLoader.h"
#include "core/probe/CoreProbes.h"
#include "core/workers/WorkerLocation.h"
#include "core/workers/WorkerNavigator.h"
#include "core/workers/WorkerReportingProxy.h"
#include "core/workers/WorkerScriptLoader.h"
#include "core/workers/WorkerThread.h"
#include "platform/CrossThreadFunctional.h"
#include "platform/InstanceCounters.h"
#include "platform/loader/fetch/MemoryCache.h"
#include "platform/network/ContentSecurityPolicyParsers.h"
#include "platform/scheduler/child/web_scheduler.h"
#include "platform/weborigin/KURL.h"
#include "platform/weborigin/SecurityOrigin.h"
#include "platform/wtf/Assertions.h"
#include "platform/wtf/RefPtr.h"
#include "public/platform/Platform.h"
#include "public/platform/WebURLRequest.h"
namespace blink {
namespace {
void RemoveURLFromMemoryCacheInternal(const KURL& url) {
GetMemoryCache()->RemoveURLFromCache(url);
}
} // namespace
WorkerGlobalScope::~WorkerGlobalScope() {
DCHECK(!ScriptController());
InstanceCounters::DecrementCounter(
InstanceCounters::kWorkerGlobalScopeCounter);
}
KURL WorkerGlobalScope::CompleteURL(const String& url) const {
// Always return a null URL when passed a null string.
// FIXME: Should we change the KURL constructor to have this behavior?
if (url.IsNull())
return KURL();
// Always use UTF-8 in Workers.
return KURL(url_, url);
}
void WorkerGlobalScope::Dispose() {
DCHECK(GetThread()->IsCurrentThread());
// Event listeners would keep DOMWrapperWorld objects alive for too long.
// Also, they have references to JS objects, which become dangling once Heap
// is destroyed.
closing_ = true;
HeapHashSet<Member<V8AbstractEventListener>> listeners;
listeners.swap(event_listeners_);
while (!listeners.IsEmpty()) {
for (const auto& listener : listeners)
listener->ClearListenerObject();
listeners.clear();
// Pick up any additions made while iterating.
listeners.swap(event_listeners_);
}
RemoveAllEventListeners();
event_queue_->Close();
WorkerOrWorkletGlobalScope::Dispose();
}
void WorkerGlobalScope::ReportFeature(WebFeature feature) {
DCHECK(IsContextThread());
DCHECK(thread_);
thread_->GetWorkerReportingProxy().CountFeature(feature);
}
void WorkerGlobalScope::ReportDeprecation(WebFeature feature) {
DCHECK(IsContextThread());
DCHECK(thread_);
thread_->GetWorkerReportingProxy().CountDeprecation(feature);
}
void WorkerGlobalScope::ExceptionUnhandled(int exception_id) {
ErrorEvent* event = pending_error_events_.Take(exception_id);
DCHECK(event);
if (WorkerThreadDebugger* debugger =
WorkerThreadDebugger::From(GetThread()->GetIsolate()))
debugger->ExceptionThrown(thread_, event);
}
void WorkerGlobalScope::RegisterEventListener(
V8AbstractEventListener* event_listener) {
// TODO(sof): remove once crbug.com/677654 has been diagnosed.
CHECK(&ThreadState::FromObject(this)->Heap() ==
&ThreadState::FromObject(event_listener)->Heap());
bool new_entry = event_listeners_.insert(event_listener).is_new_entry;
CHECK(new_entry);
}
void WorkerGlobalScope::DeregisterEventListener(
V8AbstractEventListener* event_listener) {
auto it = event_listeners_.find(event_listener);
CHECK(it != event_listeners_.end() || closing_);
event_listeners_.erase(it);
}
WorkerLocation* WorkerGlobalScope::location() const {
if (!location_)
location_ = WorkerLocation::Create(url_);
return location_.Get();
}
WorkerNavigator* WorkerGlobalScope::navigator() const {
if (!navigator_)
navigator_ = WorkerNavigator::Create(user_agent_);
return navigator_.Get();
}
void WorkerGlobalScope::close() {
// Let current script run to completion, but tell the worker micro task
// runner to tear down the thread after this task.
closing_ = true;
}
String WorkerGlobalScope::origin() const {
return GetSecurityOrigin()->ToString();
}
void WorkerGlobalScope::importScripts(const Vector<String>& urls,
ExceptionState& exception_state) {
DCHECK(GetContentSecurityPolicy());
DCHECK(GetExecutionContext());
ExecutionContext& execution_context = *this->GetExecutionContext();
Vector<KURL> completed_urls;
for (const String& url_string : urls) {
const KURL& url = execution_context.CompleteURL(url_string);
if (!url.IsValid()) {
exception_state.ThrowDOMException(
kSyntaxError, "The URL '" + url_string + "' is invalid.");
return;
}
if (!GetContentSecurityPolicy()->AllowScriptFromSource(
url, AtomicString(), IntegrityMetadataSet(), kNotParserInserted)) {
exception_state.ThrowDOMException(
kNetworkError,
"The script at '" + url.ElidedString() + "' failed to load.");
return;
}
completed_urls.push_back(url);
}
for (const KURL& complete_url : completed_urls) {
RefPtr<WorkerScriptLoader> script_loader(WorkerScriptLoader::Create());
script_loader->LoadSynchronously(
execution_context, complete_url, WebURLRequest::kRequestContextScript,
execution_context.GetSecurityContext().AddressSpace());
// If the fetching attempt failed, throw a NetworkError exception and
// abort all these steps.
if (script_loader->Failed()) {
exception_state.ThrowDOMException(
kNetworkError, "The script at '" + complete_url.ElidedString() +
"' failed to load.");
return;
}
probe::scriptImported(&execution_context, script_loader->Identifier(),
script_loader->SourceText());
ErrorEvent* error_event = nullptr;
std::unique_ptr<Vector<char>> cached_meta_data(
script_loader->ReleaseCachedMetadata());
CachedMetadataHandler* handler(CreateWorkerScriptCachedMetadataHandler(
complete_url, cached_meta_data.get()));
GetThread()->GetWorkerReportingProxy().WillEvaluateImportedScript(
script_loader->SourceText().length(),
script_loader->CachedMetadata()
? script_loader->CachedMetadata()->size()
: 0);
ScriptController()->Evaluate(ScriptSourceCode(script_loader->SourceText(),
script_loader->ResponseURL()),
&error_event, handler, v8_cache_options_);
if (error_event) {
ScriptController()->RethrowExceptionFromImportedScript(error_event,
exception_state);
return;
}
}
}
v8::Local<v8::Object> WorkerGlobalScope::Wrap(
v8::Isolate*,
v8::Local<v8::Object> creation_context) {
LOG(FATAL) << "WorkerGlobalScope must never be wrapped with wrap method. "
"The global object of ECMAScript environment is used as the "
"wrapper.";
return v8::Local<v8::Object>();
}
v8::Local<v8::Object> WorkerGlobalScope::AssociateWithWrapper(
v8::Isolate*,
const WrapperTypeInfo*,
v8::Local<v8::Object> wrapper) {
LOG(FATAL) << "WorkerGlobalScope must never be wrapped with wrap method. "
"The global object of ECMAScript environment is used as the "
"wrapper.";
return v8::Local<v8::Object>();
}
bool WorkerGlobalScope::HasPendingActivity() const {
return timers_.HasInstalledTimeout();
}
bool WorkerGlobalScope::IsContextThread() const {
return GetThread()->IsCurrentThread();
}
void WorkerGlobalScope::AddConsoleMessage(ConsoleMessage* console_message) {
DCHECK(IsContextThread());
GetThread()->GetWorkerReportingProxy().ReportConsoleMessage(
console_message->Source(), console_message->Level(),
console_message->Message(), console_message->Location());
GetThread()->GetConsoleMessageStorage()->AddConsoleMessage(this,
console_message);
}
WorkerEventQueue* WorkerGlobalScope::GetEventQueue() const {
return event_queue_.Get();
}
CoreProbeSink* WorkerGlobalScope::GetProbeSink() {
if (IsClosing())
return nullptr;
if (WorkerInspectorController* controller =
GetThread()->GetWorkerInspectorController())
return controller->GetProbeSink();
return nullptr;
}
bool WorkerGlobalScope::IsSecureContext(String& error_message) const {
// Until there are APIs that are available in workers and that
// require a privileged context test that checks ancestors, just do
// a simple check here. Once we have a need for a real
// |isSecureContext| check here, we can check the responsible
// document for a privileged context at worker creation time, pass
// it in via WorkerThreadStartupData, and check it here.
if (GetSecurityOrigin()->IsPotentiallyTrustworthy())
return true;
error_message = GetSecurityOrigin()->IsPotentiallyTrustworthyErrorMessage();
return false;
}
ExecutionContext* WorkerGlobalScope::GetExecutionContext() const {
return const_cast<WorkerGlobalScope*>(this);
}
WorkerGlobalScope::WorkerGlobalScope(
const KURL& url,
const String& user_agent,
WorkerThread* thread,
double time_origin,
std::unique_ptr<SecurityOrigin::PrivilegeData>
starter_origin_privilage_data,
WorkerClients* worker_clients)
: WorkerOrWorkletGlobalScope(thread->GetIsolate(), worker_clients),
url_(url),
user_agent_(user_agent),
v8_cache_options_(kV8CacheOptionsDefault),
thread_(thread),
event_queue_(WorkerEventQueue::Create(this)),
timers_(TaskRunnerHelper::Get(TaskType::kTimer, this)),
time_origin_(time_origin) {
InstanceCounters::IncrementCounter(
InstanceCounters::kWorkerGlobalScopeCounter);
SetSecurityOrigin(SecurityOrigin::Create(url));
if (starter_origin_privilage_data)
GetSecurityOrigin()->TransferPrivilegesFrom(
std::move(starter_origin_privilage_data));
}
void WorkerGlobalScope::ApplyContentSecurityPolicyFromVector(
const Vector<CSPHeaderAndType>& headers) {
if (!GetContentSecurityPolicy()) {
ContentSecurityPolicy* csp = ContentSecurityPolicy::Create();
SetContentSecurityPolicy(csp);
}
for (const auto& policy_and_type : headers)
GetContentSecurityPolicy()->DidReceiveHeader(
policy_and_type.first, policy_and_type.second,
kContentSecurityPolicyHeaderSourceHTTP);
GetContentSecurityPolicy()->BindToExecutionContext(GetExecutionContext());
}
void WorkerGlobalScope::SetWorkerSettings(
std::unique_ptr<WorkerSettings> worker_settings) {
worker_settings_ = std::move(worker_settings);
}
void WorkerGlobalScope::ExceptionThrown(ErrorEvent* event) {
int next_id = ++last_pending_error_event_id_;
pending_error_events_.Set(next_id, event);
GetThread()->GetWorkerReportingProxy().ReportException(
event->MessageForConsole(), event->Location()->Clone(), next_id);
}
void WorkerGlobalScope::RemoveURLFromMemoryCache(const KURL& url) {
thread_->GetParentFrameTaskRunners()
->Get(TaskType::kNetworking)
->PostTask(BLINK_FROM_HERE,
CrossThreadBind(&RemoveURLFromMemoryCacheInternal, url));
}
KURL WorkerGlobalScope::VirtualCompleteURL(const String& url) const {
return CompleteURL(url);
}
DEFINE_TRACE(WorkerGlobalScope) {
visitor->Trace(location_);
visitor->Trace(navigator_);
visitor->Trace(event_queue_);
visitor->Trace(timers_);
visitor->Trace(event_listeners_);
visitor->Trace(pending_error_events_);
EventTargetWithInlineData::Trace(visitor);
SecurityContext::Trace(visitor);
WorkerOrWorkletGlobalScope::Trace(visitor);
Supplementable<WorkerGlobalScope>::Trace(visitor);
}
} // namespace blink