summaryrefslogtreecommitdiff
path: root/Source/WebCore/workers/WorkerThread.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/workers/WorkerThread.cpp')
-rw-r--r--Source/WebCore/workers/WorkerThread.cpp258
1 files changed, 131 insertions, 127 deletions
diff --git a/Source/WebCore/workers/WorkerThread.cpp b/Source/WebCore/workers/WorkerThread.cpp
index fca606ec2..8b1e9234b 100644
--- a/Source/WebCore/workers/WorkerThread.cpp
+++ b/Source/WebCore/workers/WorkerThread.cpp
@@ -10,10 +10,10 @@
* 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
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE 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
@@ -25,42 +25,35 @@
*/
#include "config.h"
-
#include "WorkerThread.h"
-#include "DedicatedWorkerGlobalScope.h"
-#include "InspectorInstrumentation.h"
+#include "ContentSecurityPolicyResponseHeaders.h"
+#include "IDBConnectionProxy.h"
#include "ScriptSourceCode.h"
#include "SecurityOrigin.h"
+#include "SocketProvider.h"
#include "ThreadGlobalData.h"
#include "URL.h"
+#include "WorkerGlobalScope.h"
+#include "WorkerInspectorController.h"
#include <utility>
+#include <wtf/Lock.h>
#include <wtf/NeverDestroyed.h>
#include <wtf/Noncopyable.h>
#include <wtf/text/WTFString.h>
-#if ENABLE(SQL_DATABASE)
-#include "DatabaseManager.h"
-#include "DatabaseTask.h"
-#endif
-
#if PLATFORM(IOS)
+#include "FloatingPointEnvironment.h"
#include "WebCoreThread.h"
#endif
-namespace WebCore {
-
-static std::mutex& threadSetMutex()
-{
- static std::once_flag onceFlag;
- static std::mutex* mutex;
+#if PLATFORM(GTK)
+#include <wtf/glib/GRefPtr.h>
+#endif
- std::call_once(onceFlag, []{
- mutex = std::make_unique<std::mutex>().release();
- });
+namespace WebCore {
- return *mutex;
-}
+static StaticLock threadSetMutex;
static HashSet<WorkerThread*>& workerThreads()
{
@@ -71,7 +64,7 @@ static HashSet<WorkerThread*>& workerThreads()
unsigned WorkerThread::workerThreadCount()
{
- std::lock_guard<std::mutex> lock(threadSetMutex());
+ std::lock_guard<StaticLock> lock(threadSetMutex);
return workerThreads().size();
}
@@ -79,58 +72,60 @@ unsigned WorkerThread::workerThreadCount()
struct WorkerThreadStartupData {
WTF_MAKE_NONCOPYABLE(WorkerThreadStartupData); WTF_MAKE_FAST_ALLOCATED;
public:
- static PassOwnPtr<WorkerThreadStartupData> create(const URL& scriptURL, const String& userAgent, const GroupSettings* settings, const String& sourceCode, WorkerThreadStartMode startMode, const String& contentSecurityPolicy, ContentSecurityPolicy::HeaderType contentSecurityPolicyType, const SecurityOrigin* topOrigin)
- {
- return adoptPtr(new WorkerThreadStartupData(scriptURL, userAgent, settings, sourceCode, startMode, contentSecurityPolicy, contentSecurityPolicyType, topOrigin));
- }
+ WorkerThreadStartupData(const URL& scriptURL, const String& identifier, const String& userAgent, const String& sourceCode, WorkerThreadStartMode, const ContentSecurityPolicyResponseHeaders&, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin& topOrigin, MonotonicTime timeOrigin);
URL m_scriptURL;
+ String m_identifier;
String m_userAgent;
- std::unique_ptr<GroupSettings> m_groupSettings;
String m_sourceCode;
WorkerThreadStartMode m_startMode;
- String m_contentSecurityPolicy;
- ContentSecurityPolicy::HeaderType m_contentSecurityPolicyType;
- RefPtr<SecurityOrigin> m_topOrigin;
-private:
- WorkerThreadStartupData(const URL& scriptURL, const String& userAgent, const GroupSettings*, const String& sourceCode, WorkerThreadStartMode, const String& contentSecurityPolicy, ContentSecurityPolicy::HeaderType contentSecurityPolicyType, const SecurityOrigin* topOrigin);
+ ContentSecurityPolicyResponseHeaders m_contentSecurityPolicyResponseHeaders;
+ bool m_shouldBypassMainWorldContentSecurityPolicy;
+ Ref<SecurityOrigin> m_topOrigin;
+ MonotonicTime m_timeOrigin;
};
-WorkerThreadStartupData::WorkerThreadStartupData(const URL& scriptURL, const String& userAgent, const GroupSettings* settings, const String& sourceCode, WorkerThreadStartMode startMode, const String& contentSecurityPolicy, ContentSecurityPolicy::HeaderType contentSecurityPolicyType, const SecurityOrigin* topOrigin)
- : m_scriptURL(scriptURL.copy())
+WorkerThreadStartupData::WorkerThreadStartupData(const URL& scriptURL, const String& identifier, const String& userAgent, const String& sourceCode, WorkerThreadStartMode startMode, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin& topOrigin, MonotonicTime timeOrigin)
+ : m_scriptURL(scriptURL.isolatedCopy())
+ , m_identifier(identifier.isolatedCopy())
, m_userAgent(userAgent.isolatedCopy())
, m_sourceCode(sourceCode.isolatedCopy())
, m_startMode(startMode)
- , m_contentSecurityPolicy(contentSecurityPolicy.isolatedCopy())
- , m_contentSecurityPolicyType(contentSecurityPolicyType)
- , m_topOrigin(topOrigin ? topOrigin->isolatedCopy() : 0)
+ , m_contentSecurityPolicyResponseHeaders(contentSecurityPolicyResponseHeaders.isolatedCopy())
+ , m_shouldBypassMainWorldContentSecurityPolicy(shouldBypassMainWorldContentSecurityPolicy)
+ , m_topOrigin(topOrigin.isolatedCopy())
+ , m_timeOrigin(timeOrigin)
{
- if (!settings)
- return;
-
- m_groupSettings = std::make_unique<GroupSettings>();
- m_groupSettings->setLocalStorageQuotaBytes(settings->localStorageQuotaBytes());
- m_groupSettings->setIndexedDBQuotaBytes(settings->indexedDBQuotaBytes());
- m_groupSettings->setIndexedDBDatabasePath(settings->indexedDBDatabasePath().isolatedCopy());
}
-WorkerThread::WorkerThread(const URL& scriptURL, const String& userAgent, const GroupSettings* settings, const String& sourceCode, WorkerLoaderProxy& workerLoaderProxy, WorkerReportingProxy& workerReportingProxy, WorkerThreadStartMode startMode, const String& contentSecurityPolicy, ContentSecurityPolicy::HeaderType contentSecurityPolicyType, const SecurityOrigin* topOrigin)
+WorkerThread::WorkerThread(const URL& scriptURL, const String& identifier, const String& userAgent, const String& sourceCode, WorkerLoaderProxy& workerLoaderProxy, WorkerReportingProxy& workerReportingProxy, WorkerThreadStartMode startMode, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin& topOrigin, MonotonicTime timeOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider, JSC::RuntimeFlags runtimeFlags)
: m_threadID(0)
, m_workerLoaderProxy(workerLoaderProxy)
, m_workerReportingProxy(workerReportingProxy)
- , m_startupData(WorkerThreadStartupData::create(scriptURL, userAgent, settings, sourceCode, startMode, contentSecurityPolicy, contentSecurityPolicyType, topOrigin))
-#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
- , m_notificationClient(0)
+ , m_runtimeFlags(runtimeFlags)
+ , m_startupData(std::make_unique<WorkerThreadStartupData>(scriptURL, identifier, userAgent, sourceCode, startMode, contentSecurityPolicyResponseHeaders, shouldBypassMainWorldContentSecurityPolicy, topOrigin, timeOrigin))
+#if ENABLE(INDEXED_DATABASE)
+ , m_idbConnectionProxy(connectionProxy)
+#endif
+#if ENABLE(WEB_SOCKETS)
+ , m_socketProvider(socketProvider)
#endif
{
- std::lock_guard<std::mutex> lock(threadSetMutex());
+#if !ENABLE(INDEXED_DATABASE)
+ UNUSED_PARAM(connectionProxy);
+#endif
+#if !ENABLE(WEB_SOCKETS)
+ UNUSED_PARAM(socketProvider);
+#endif
+
+ std::lock_guard<StaticLock> lock(threadSetMutex);
workerThreads().add(this);
}
WorkerThread::~WorkerThread()
{
- std::lock_guard<std::mutex> lock(threadSetMutex());
+ std::lock_guard<StaticLock> lock(threadSetMutex);
ASSERT(workerThreads().contains(this));
workerThreads().remove(this);
@@ -139,7 +134,7 @@ WorkerThread::~WorkerThread()
bool WorkerThread::start()
{
// Mutex protection is necessary to ensure that m_threadID is initialized when the thread starts.
- MutexLocker lock(m_threadCreationMutex);
+ LockHolder lock(m_threadCreationMutex);
if (m_threadID)
return true;
@@ -158,12 +153,17 @@ void WorkerThread::workerThread()
{
// Propagate the mainThread's fenv to workers.
#if PLATFORM(IOS)
- fesetenv(&mainThreadFEnv);
+ FloatingPointEnvironment::singleton().propagateMainThreadEnvironment();
+#endif
+
+#if PLATFORM(GTK)
+ GRefPtr<GMainContext> mainContext = adoptGRef(g_main_context_new());
+ g_main_context_push_thread_default(mainContext.get());
#endif
{
- MutexLocker lock(m_threadCreationMutex);
- m_workerGlobalScope = createWorkerGlobalScope(m_startupData->m_scriptURL, m_startupData->m_userAgent, std::move(m_startupData->m_groupSettings), m_startupData->m_contentSecurityPolicy, m_startupData->m_contentSecurityPolicyType, m_startupData->m_topOrigin.release());
+ LockHolder lock(m_threadCreationMutex);
+ m_workerGlobalScope = createWorkerGlobalScope(m_startupData->m_scriptURL, m_startupData->m_identifier, m_startupData->m_userAgent, m_startupData->m_contentSecurityPolicyResponseHeaders, m_startupData->m_shouldBypassMainWorldContentSecurityPolicy, WTFMove(m_startupData->m_topOrigin), m_startupData->m_timeOrigin);
if (m_runLoop.terminated()) {
// The worker was terminated before the thread had a chance to run. Since the context didn't exist yet,
@@ -172,25 +172,34 @@ void WorkerThread::workerThread()
}
}
+ if (m_startupData->m_startMode == WorkerThreadStartMode::WaitForInspector) {
+ startRunningDebuggerTasks();
+
+ // If the worker was somehow terminated while processing debugger commands.
+ if (m_runLoop.terminated())
+ m_workerGlobalScope->script()->forbidExecution();
+ }
+
WorkerScriptController* script = m_workerGlobalScope->script();
-#if ENABLE(INSPECTOR)
- InspectorInstrumentation::willEvaluateWorkerScript(workerGlobalScope(), m_startupData->m_startMode);
-#endif
script->evaluate(ScriptSourceCode(m_startupData->m_sourceCode, m_startupData->m_scriptURL));
// Free the startup data to cause its member variable deref's happen on the worker's thread (since
// all ref/derefs of these objects are happening on the thread at this point). Note that
// WorkerThread::~WorkerThread happens on a different thread where it was created.
- m_startupData.clear();
+ m_startupData = nullptr;
runEventLoop();
+#if PLATFORM(GTK)
+ g_main_context_pop_thread_default(mainContext.get());
+#endif
+
ThreadIdentifier threadID = m_threadID;
ASSERT(m_workerGlobalScope->hasOneRef());
// The below assignment will destroy the context, which will in turn notify messaging proxy.
// We cannot let any objects survive past thread exit, because no other thread will run GC or otherwise destroy them.
- m_workerGlobalScope = 0;
+ m_workerGlobalScope = nullptr;
// Clean up WebCore::ThreadGlobalData before WTF::WTFThreadData goes away!
threadGlobalData().destroy();
@@ -199,98 +208,93 @@ void WorkerThread::workerThread()
detachThread(threadID);
}
+void WorkerThread::startRunningDebuggerTasks()
+{
+ ASSERT(!m_pausedForDebugger);
+ m_pausedForDebugger = true;
+
+ MessageQueueWaitResult result;
+ do {
+ result = m_runLoop.runInMode(m_workerGlobalScope.get(), WorkerRunLoop::debuggerMode());
+ } while (result != MessageQueueTerminated && m_pausedForDebugger);
+}
+
+void WorkerThread::stopRunningDebuggerTasks()
+{
+ m_pausedForDebugger = false;
+}
+
void WorkerThread::runEventLoop()
{
// Does not return until terminated.
m_runLoop.run(m_workerGlobalScope.get());
}
-class WorkerThreadShutdownFinishTask : public ScriptExecutionContext::Task {
-public:
- static PassOwnPtr<WorkerThreadShutdownFinishTask> create()
- {
- return adoptPtr(new WorkerThreadShutdownFinishTask());
- }
-
- virtual void performTask(ScriptExecutionContext *context)
- {
- ASSERT_WITH_SECURITY_IMPLICATION(context->isWorkerGlobalScope());
- WorkerGlobalScope* workerGlobalScope = static_cast<WorkerGlobalScope*>(context);
- // It's not safe to call clearScript until all the cleanup tasks posted by functions initiated by WorkerThreadShutdownStartTask have completed.
- workerGlobalScope->clearScript();
- }
-
- virtual bool isCleanupTask() const { return true; }
-};
+void WorkerThread::stop()
+{
+ // Mutex protection is necessary because stop() can be called before the context is fully created.
+ LockHolder lock(m_threadCreationMutex);
-class WorkerThreadShutdownStartTask : public ScriptExecutionContext::Task {
-public:
- static PassOwnPtr<WorkerThreadShutdownStartTask> create()
- {
- return adoptPtr(new WorkerThreadShutdownStartTask());
- }
+ // Ensure that tasks are being handled by thread event loop. If script execution weren't forbidden, a while(1) loop in JS could keep the thread alive forever.
+ if (m_workerGlobalScope) {
+ m_workerGlobalScope->script()->scheduleExecutionTermination();
- virtual void performTask(ScriptExecutionContext *context)
- {
- ASSERT_WITH_SECURITY_IMPLICATION(context->isWorkerGlobalScope());
- WorkerGlobalScope* workerGlobalScope = static_cast<WorkerGlobalScope*>(context);
+ m_runLoop.postTaskAndTerminate({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext& context ) {
+ WorkerGlobalScope& workerGlobalScope = downcast<WorkerGlobalScope>(context);
-#if ENABLE(SQL_DATABASE)
- // FIXME: Should we stop the databases as part of stopActiveDOMObjects() below?
- DatabaseTaskSynchronizer cleanupSync;
- DatabaseManager::manager().stopDatabases(workerGlobalScope, &cleanupSync);
+#if ENABLE(INDEXED_DATABASE)
+ workerGlobalScope.stopIndexedDatabase();
#endif
- workerGlobalScope->stopActiveDOMObjects();
+ workerGlobalScope.stopActiveDOMObjects();
- workerGlobalScope->notifyObserversOfStop();
+ workerGlobalScope.inspectorController().workerTerminating();
- // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects,
- // which become dangling once Heap is destroyed.
- workerGlobalScope->removeAllEventListeners();
+ // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects,
+ // which become dangling once Heap is destroyed.
+ workerGlobalScope.removeAllEventListeners();
-#if ENABLE(SQL_DATABASE)
- // We wait for the database thread to clean up all its stuff so that we
- // can do more stringent leak checks as we exit.
- cleanupSync.waitForTaskCompletion();
-#endif
+ // Stick a shutdown command at the end of the queue, so that we deal
+ // with all the cleanup tasks the databases post first.
+ workerGlobalScope.postTask({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext& context) {
+ WorkerGlobalScope& workerGlobalScope = downcast<WorkerGlobalScope>(context);
+ // It's not safe to call clearScript until all the cleanup tasks posted by functions initiated by WorkerThreadShutdownStartTask have completed.
+ workerGlobalScope.clearScript();
+ } });
- // Stick a shutdown command at the end of the queue, so that we deal
- // with all the cleanup tasks the databases post first.
- workerGlobalScope->postTask(WorkerThreadShutdownFinishTask::create());
+ } });
+ return;
}
+ m_runLoop.terminate();
+}
- virtual bool isCleanupTask() const { return true; }
-};
-
-void WorkerThread::stop()
+void WorkerThread::releaseFastMallocFreeMemoryInAllThreads()
{
- // Mutex protection is necessary because stop() can be called before the context is fully created.
- MutexLocker lock(m_threadCreationMutex);
+ std::lock_guard<StaticLock> lock(threadSetMutex);
- // Ensure that tasks are being handled by thread event loop. If script execution weren't forbidden, a while(1) loop in JS could keep the thread alive forever.
- if (m_workerGlobalScope) {
- m_workerGlobalScope->script()->scheduleExecutionTermination();
-
-#if ENABLE(SQL_DATABASE)
- DatabaseManager::manager().interruptAllDatabasesForContext(m_workerGlobalScope.get());
-#endif
- m_runLoop.postTaskAndTerminate(WorkerThreadShutdownStartTask::create());
- return;
+ for (auto* workerThread : workerThreads()) {
+ workerThread->runLoop().postTask([] (ScriptExecutionContext&) {
+ WTF::releaseFastMallocFreeMemory();
+ });
}
- m_runLoop.terminate();
}
-class ReleaseFastMallocFreeMemoryTask : public ScriptExecutionContext::Task {
- virtual void performTask(ScriptExecutionContext*) override { WTF::releaseFastMallocFreeMemory(); }
-};
-
-void WorkerThread::releaseFastMallocFreeMemoryInAllThreads()
+IDBClient::IDBConnectionProxy* WorkerThread::idbConnectionProxy()
{
- std::lock_guard<std::mutex> lock(threadSetMutex());
+#if ENABLE(INDEXED_DATABASE)
+ return m_idbConnectionProxy.get();
+#else
+ return nullptr;
+#endif
+}
- for (auto* workerThread : workerThreads())
- workerThread->runLoop().postTask(adoptPtr(new ReleaseFastMallocFreeMemoryTask));
+SocketProvider* WorkerThread::socketProvider()
+{
+#if ENABLE(WEB_SOCKETS)
+ return m_socketProvider.get();
+#else
+ return nullptr;
+#endif
}
} // namespace WebCore