Unreviewed. Restabilize non-unified build.
[WebKit-https.git] / Source / WebCore / workers / Worker.cpp
index 18528ae..e38eee3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2010 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2008-2017 Apple Inc. All rights reserved.
  * Copyright (C) 2009 Google Inc. All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include "Worker.h"
 
 #include "ContentSecurityPolicy.h"
+#include "ErrorEvent.h"
 #include "Event.h"
 #include "EventNames.h"
 #include "InspectorInstrumentation.h"
-#include "NetworkStateNotifier.h"
+#include "LoaderStrategy.h"
+#include "PlatformStrategies.h"
 #include "ResourceResponse.h"
 #include "SecurityOrigin.h"
 #include "WorkerGlobalScopeProxy.h"
 #include "WorkerScriptLoader.h"
 #include "WorkerThread.h"
-#include <inspector/IdentifiersFactory.h>
+#include <JavaScriptCore/IdentifiersFactory.h>
+#include <JavaScriptCore/ScriptCallStack.h>
 #include <wtf/HashSet.h>
+#include <wtf/IsoMallocInlines.h>
 #include <wtf/MainThread.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/Scope.h>
 
 namespace WebCore {
 
-static HashSet<Worker*>* allWorkers;
+WTF_MAKE_ISO_ALLOCATED_IMPL(Worker);
 
-void networkStateChanged(bool isOnLine)
+static HashSet<Worker*>& allWorkers()
 {
-    for (auto& worker : *allWorkers)
+    static NeverDestroyed<HashSet<Worker*>> set;
+    return set;
+}
+
+void Worker::networkStateChanged(bool isOnLine)
+{
+    for (auto* worker : allWorkers())
         worker->notifyNetworkStateChange(isOnLine);
 }
 
-inline Worker::Worker(ScriptExecutionContext& context, JSC::RuntimeFlags runtimeFlags)
+inline Worker::Worker(ScriptExecutionContext& context, JSC::RuntimeFlags runtimeFlags, const Options& options)
     : ActiveDOMObject(&context)
+    , m_name(options.name)
     , m_identifier("worker:" + Inspector::IdentifiersFactory::createIdentifier())
-    , m_contextProxy(WorkerGlobalScopeProxy::create(this))
+    , m_contextProxy(WorkerGlobalScopeProxy::create(*this))
     , m_runtimeFlags(runtimeFlags)
+    , m_eventQueue(GenericEventQueue::create(*this))
 {
-    if (!allWorkers) {
-        allWorkers = new HashSet<Worker*>;
-        networkStateNotifier().addNetworkStateChangeListener(networkStateChanged);
+    static bool addedListener;
+    if (!addedListener) {
+        platformStrategies()->loaderStrategy()->addOnlineStateChangeListener(&networkStateChanged);
+        addedListener = true;
     }
 
-    HashSet<Worker*>::AddResult addResult = allWorkers->add(this);
+    auto addResult = allWorkers().add(this);
     ASSERT_UNUSED(addResult, addResult.isNewEntry);
 }
 
-ExceptionOr<Ref<Worker>> Worker::create(ScriptExecutionContext& context, const String& url, JSC::RuntimeFlags runtimeFlags)
+ExceptionOr<Ref<Worker>> Worker::create(ScriptExecutionContext& context, JSC::RuntimeFlags runtimeFlags, const String& url, const Options& options)
 {
     ASSERT(isMainThread());
 
     // We don't currently support nested workers, so workers can only be created from documents.
     ASSERT_WITH_SECURITY_IMPLICATION(context.isDocument());
 
-    auto worker = adoptRef(*new Worker(context, runtimeFlags));
+    auto worker = adoptRef(*new Worker(context, runtimeFlags, options));
 
     worker->suspendIfNeeded();
 
@@ -85,41 +100,59 @@ ExceptionOr<Ref<Worker>> Worker::create(ScriptExecutionContext& context, const S
     worker->m_shouldBypassMainWorldContentSecurityPolicy = shouldBypassMainWorldContentSecurityPolicy;
 
     // The worker context does not exist while loading, so we must ensure that the worker object is not collected, nor are its event listeners.
-    worker->setPendingActivity(worker.ptr());
+    worker->setPendingActivity(worker.get());
+
+    // https://html.spec.whatwg.org/multipage/workers.html#official-moment-of-creation
+    worker->m_workerCreationTime = MonotonicTime::now();
 
     worker->m_scriptLoader = WorkerScriptLoader::create();
     auto contentSecurityPolicyEnforcement = shouldBypassMainWorldContentSecurityPolicy ? ContentSecurityPolicyEnforcement::DoNotEnforce : ContentSecurityPolicyEnforcement::EnforceChildSrcDirective;
-    worker->m_scriptLoader->loadAsynchronously(&context, scriptURL.releaseReturnValue(), FetchOptions::Mode::SameOrigin, contentSecurityPolicyEnforcement, worker->m_identifier, worker.ptr());
-    return WTFMove(worker);
+
+    ResourceRequest request { scriptURL.releaseReturnValue() };
+    request.setInitiatorIdentifier(worker->m_identifier);
+
+    FetchOptions fetchOptions;
+    fetchOptions.mode = FetchOptions::Mode::SameOrigin;
+    fetchOptions.cache = FetchOptions::Cache::Default;
+    fetchOptions.redirect = FetchOptions::Redirect::Follow;
+    fetchOptions.destination = FetchOptions::Destination::Worker;
+    worker->m_scriptLoader->loadAsynchronously(context, WTFMove(request), WTFMove(fetchOptions), contentSecurityPolicyEnforcement, ServiceWorkersMode::All, worker);
+    return worker;
 }
 
 Worker::~Worker()
 {
     ASSERT(isMainThread());
     ASSERT(scriptExecutionContext()); // The context is protected by worker context proxy, so it cannot be destroyed while a Worker exists.
-    allWorkers->remove(this);
-    m_contextProxy->workerObjectDestroyed();
+    allWorkers().remove(this);
+    m_contextProxy.workerObjectDestroyed();
 }
 
-ExceptionOr<void> Worker::postMessage(RefPtr<SerializedScriptValue>&& message, Vector<RefPtr<MessagePort>>&& ports)
+ExceptionOr<void> Worker::postMessage(JSC::ExecState& state, JSC::JSValue messageValue, Vector<JSC::Strong<JSC::JSObject>>&& transfer)
 {
+    Vector<RefPtr<MessagePort>> ports;
+    auto message = SerializedScriptValue::create(state, messageValue, WTFMove(transfer), ports, SerializationContext::WorkerPostMessage);
+    if (message.hasException())
+        return message.releaseException();
+
     // Disentangle the port in preparation for sending it to the remote context.
     auto channels = MessagePort::disentanglePorts(WTFMove(ports));
     if (channels.hasException())
         return channels.releaseException();
-    m_contextProxy->postMessageToWorkerGlobalScope(WTFMove(message), channels.releaseReturnValue());
+
+    m_contextProxy.postMessageToWorkerGlobalScope({ message.releaseReturnValue(), channels.releaseReturnValue() });
     return { };
 }
 
 void Worker::terminate()
 {
-    m_contextProxy->terminateWorkerGlobalScope();
+    m_contextProxy.terminateWorkerGlobalScope();
+    m_eventQueue->cancelAllEvents();
 }
 
 bool Worker::canSuspendForDocumentSuspension() const
 {
-    // FIXME: It is not currently possible to suspend a worker, so pages with workers can not go into page cache.
-    return false;
+    return true;
 }
 
 const char* Worker::activeDOMObjectName() const
@@ -134,12 +167,12 @@ void Worker::stop()
 
 bool Worker::hasPendingActivity() const
 {
-    return m_contextProxy->hasPendingActivity() || ActiveDOMObject::hasPendingActivity();
+    return m_contextProxy.hasPendingActivity() || ActiveDOMObject::hasPendingActivity() || m_eventQueue->hasPendingEvents();
 }
 
 void Worker::notifyNetworkStateChange(bool isOnLine)
 {
-    m_contextProxy->notifyNetworkStateChange(isOnLine);
+    m_contextProxy.notifyNetworkStateChange(isOnLine);
 }
 
 void Worker::didReceiveResponse(unsigned long identifier, const ResourceResponse& response)
@@ -152,16 +185,40 @@ void Worker::didReceiveResponse(unsigned long identifier, const ResourceResponse
 
 void Worker::notifyFinished()
 {
-    if (m_scriptLoader->failed())
-        dispatchEvent(Event::create(eventNames().errorEvent, false, true));
-    else {
-        const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders = m_contentSecurityPolicyResponseHeaders ? m_contentSecurityPolicyResponseHeaders.value() : scriptExecutionContext()->contentSecurityPolicy()->responseHeaders();
-        m_contextProxy->startWorkerGlobalScope(m_scriptLoader->url(), scriptExecutionContext()->userAgent(m_scriptLoader->url()), m_scriptLoader->script(), contentSecurityPolicyResponseHeaders, m_shouldBypassMainWorldContentSecurityPolicy, m_runtimeFlags);
-        InspectorInstrumentation::scriptImported(*scriptExecutionContext(), m_scriptLoader->identifier(), m_scriptLoader->script());
+    auto clearLoader = makeScopeExit([this] {
+        m_scriptLoader = nullptr;
+        unsetPendingActivity(*this);
+    });
+
+    auto* context = scriptExecutionContext();
+    if (!context)
+        return;
+
+    if (m_scriptLoader->failed()) {
+        enqueueEvent(Event::create(eventNames().errorEvent, Event::CanBubble::No, Event::IsCancelable::Yes));
+        return;
     }
-    m_scriptLoader = nullptr;
 
-    unsetPendingActivity(this);
+    bool isOnline = platformStrategies()->loaderStrategy()->isOnLine();
+    const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders = m_contentSecurityPolicyResponseHeaders ? m_contentSecurityPolicyResponseHeaders.value() : context->contentSecurityPolicy()->responseHeaders();
+    m_contextProxy.startWorkerGlobalScope(m_scriptLoader->url(), m_name, context->userAgent(m_scriptLoader->url()), isOnline, m_scriptLoader->script(), contentSecurityPolicyResponseHeaders, m_shouldBypassMainWorldContentSecurityPolicy, m_workerCreationTime, m_runtimeFlags);
+    InspectorInstrumentation::scriptImported(*context, m_scriptLoader->identifier(), m_scriptLoader->script());
+}
+
+void Worker::enqueueEvent(Ref<Event>&& event)
+{
+    m_eventQueue->enqueueEvent(WTFMove(event));
+}
+
+void Worker::dispatchEvent(Event& event)
+{
+    RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!m_eventQueue->isSuspended());
+
+    AbstractWorker::dispatchEvent(event);
+    if (is<ErrorEvent>(event) && !event.defaultPrevented() && event.isTrusted() && scriptExecutionContext()) {
+        auto& errorEvent = downcast<ErrorEvent>(event);
+        scriptExecutionContext()->reportException(errorEvent.message(), errorEvent.lineno(), errorEvent.colno(), errorEvent.filename(), nullptr, nullptr);
+    }
 }
 
 } // namespace WebCore