ASSERTION FAILED: m_scriptExecutionContext->isContextThread() seen with LayoutTest...
[WebKit-https.git] / Source / WebCore / workers / WorkerGlobalScope.cpp
1 /*
2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3  * Copyright (C) 2009, 2011 Google Inc. All Rights Reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27
28 #include "config.h"
29 #include "WorkerGlobalScope.h"
30
31 #include "ContentSecurityPolicy.h"
32 #include "Crypto.h"
33 #include "ExceptionCode.h"
34 #include "IDBConnectionProxy.h"
35 #include "InspectorInstrumentation.h"
36 #include "Performance.h"
37 #include "ScheduledAction.h"
38 #include "ScriptSourceCode.h"
39 #include "SecurityOrigin.h"
40 #include "SecurityOriginPolicy.h"
41 #include "SocketProvider.h"
42 #include "WorkerInspectorController.h"
43 #include "WorkerLoaderProxy.h"
44 #include "WorkerLocation.h"
45 #include "WorkerNavigator.h"
46 #include "WorkerReportingProxy.h"
47 #include "WorkerScriptLoader.h"
48 #include "WorkerThread.h"
49 #include <inspector/ScriptArguments.h>
50 #include <inspector/ScriptCallStack.h>
51
52 using namespace Inspector;
53
54 namespace WebCore {
55
56 WorkerGlobalScope::WorkerGlobalScope(const URL& url, const String& identifier, const String& userAgent, WorkerThread& thread, bool shouldBypassMainWorldContentSecurityPolicy, Ref<SecurityOrigin>&& topOrigin, MonotonicTime timeOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider)
57     : m_url(url)
58     , m_identifier(identifier)
59     , m_userAgent(userAgent)
60     , m_thread(thread)
61     , m_script(std::make_unique<WorkerScriptController>(this))
62     , m_inspectorController(std::make_unique<WorkerInspectorController>(*this))
63     , m_shouldBypassMainWorldContentSecurityPolicy(shouldBypassMainWorldContentSecurityPolicy)
64     , m_eventQueue(*this)
65     , m_topOrigin(WTFMove(topOrigin))
66 #if ENABLE(INDEXED_DATABASE)
67     , m_connectionProxy(connectionProxy)
68 #endif
69 #if ENABLE(WEB_SOCKETS)
70     , m_socketProvider(socketProvider)
71 #endif
72 #if ENABLE(WEB_TIMING)
73     , m_performance(Performance::create(*this, timeOrigin))
74 #endif
75 {
76 #if !ENABLE(INDEXED_DATABASE)
77     UNUSED_PARAM(connectionProxy);
78 #endif
79 #if !ENABLE(WEB_SOCKETS)
80     UNUSED_PARAM(socketProvider);
81 #endif
82 #if !ENABLE(WEB_TIMING)
83     UNUSED_PARAM(timeOrigin);
84 #endif
85
86     auto origin = SecurityOrigin::create(url);
87     if (m_topOrigin->hasUniversalAccess())
88         origin->grantUniversalAccess();
89     if (m_topOrigin->needsStorageAccessFromFileURLsQuirk())
90         origin->grantStorageAccessFromFileURLsQuirk();
91
92     setSecurityOriginPolicy(SecurityOriginPolicy::create(WTFMove(origin)));
93     setContentSecurityPolicy(std::make_unique<ContentSecurityPolicy>(*this));
94 }
95
96 WorkerGlobalScope::~WorkerGlobalScope()
97 {
98     ASSERT(currentThread() == thread().threadID());
99
100 #if ENABLE(WEB_TIMING)
101     m_performance = nullptr;
102 #endif
103
104     m_crypto = nullptr;
105
106     // Notify proxy that we are going away. This can free the WorkerThread object, so do not access it after this.
107     thread().workerReportingProxy().workerGlobalScopeDestroyed();
108 }
109
110 String WorkerGlobalScope::origin() const
111 {
112     auto* securityOrigin = this->securityOrigin();
113     return securityOrigin ? securityOrigin->toString() : emptyString();
114 }
115
116 void WorkerGlobalScope::removeAllEventListeners()
117 {
118     EventTarget::removeAllEventListeners();
119
120 #if ENABLE(WEB_TIMING)
121     m_performance->removeAllEventListeners();
122 #endif
123 }
124
125 void WorkerGlobalScope::applyContentSecurityPolicyResponseHeaders(const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders)
126 {
127     contentSecurityPolicy()->didReceiveHeaders(contentSecurityPolicyResponseHeaders);
128 }
129
130 URL WorkerGlobalScope::completeURL(const String& url) const
131 {
132     // Always return a null URL when passed a null string.
133     // FIXME: Should we change the URL constructor to have this behavior?
134     if (url.isNull())
135         return URL();
136     // Always use UTF-8 in Workers.
137     return URL(m_url, url);
138 }
139
140 String WorkerGlobalScope::userAgent(const URL&) const
141 {
142     return m_userAgent;
143 }
144
145 void WorkerGlobalScope::disableEval(const String& errorMessage)
146 {
147     m_script->disableEval(errorMessage);
148 }
149
150 #if ENABLE(WEB_SOCKETS)
151
152 SocketProvider* WorkerGlobalScope::socketProvider()
153 {
154     return m_socketProvider.get();
155 }
156
157 #endif
158
159 #if ENABLE(INDEXED_DATABASE)
160
161 IDBClient::IDBConnectionProxy* WorkerGlobalScope::idbConnectionProxy()
162 {
163 #if ENABLE(INDEXED_DATABASE_IN_WORKERS)
164     return m_connectionProxy.get();
165 #else
166     return nullptr;
167 #endif
168 }
169
170 void WorkerGlobalScope::stopIndexedDatabase()
171 {
172 #if ENABLE(INDEXED_DATABASE_IN_WORKERS)
173     ASSERT(m_connectionProxy);
174     m_connectionProxy->forgetActivityForCurrentThread();
175 #endif
176 }
177
178 #endif // ENABLE(INDEXED_DATABASE)
179
180 WorkerLocation& WorkerGlobalScope::location() const
181 {
182     if (!m_location)
183         m_location = WorkerLocation::create(m_url);
184     return *m_location;
185 }
186
187 void WorkerGlobalScope::close()
188 {
189     if (m_closing)
190         return;
191
192     // Let current script run to completion but prevent future script evaluations.
193     // After m_closing is set, all the tasks in the queue continue to be fetched but only
194     // tasks with isCleanupTask()==true will be executed.
195     m_closing = true;
196     postTask({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext& context) {
197         ASSERT_WITH_SECURITY_IMPLICATION(is<WorkerGlobalScope>(context));
198         WorkerGlobalScope& workerGlobalScope = downcast<WorkerGlobalScope>(context);
199         // Notify parent that this context is closed. Parent is responsible for calling WorkerThread::stop().
200         workerGlobalScope.thread().workerReportingProxy().workerGlobalScopeClosed();
201     } });
202 }
203
204 WorkerNavigator& WorkerGlobalScope::navigator() const
205 {
206     if (!m_navigator)
207         m_navigator = WorkerNavigator::create(m_userAgent);
208     return *m_navigator;
209 }
210
211 void WorkerGlobalScope::postTask(Task&& task)
212 {
213     thread().runLoop().postTask(WTFMove(task));
214 }
215
216 int WorkerGlobalScope::setTimeout(std::unique_ptr<ScheduledAction> action, int timeout)
217 {
218     return DOMTimer::install(*this, WTFMove(action), Seconds::fromMilliseconds(timeout), true);
219 }
220
221 void WorkerGlobalScope::clearTimeout(int timeoutId)
222 {
223     DOMTimer::removeById(*this, timeoutId);
224 }
225
226 int WorkerGlobalScope::setInterval(std::unique_ptr<ScheduledAction> action, int timeout)
227 {
228     return DOMTimer::install(*this, WTFMove(action), Seconds::fromMilliseconds(timeout), false);
229 }
230
231 void WorkerGlobalScope::clearInterval(int timeoutId)
232 {
233     DOMTimer::removeById(*this, timeoutId);
234 }
235
236 ExceptionOr<void> WorkerGlobalScope::importScripts(const Vector<String>& urls)
237 {
238     ASSERT(contentSecurityPolicy());
239
240     Vector<URL> completedURLs;
241     completedURLs.reserveInitialCapacity(urls.size());
242     for (auto& entry : urls) {
243         URL url = completeURL(entry);
244         if (!url.isValid())
245             return Exception { SYNTAX_ERR };
246         completedURLs.uncheckedAppend(WTFMove(url));
247     }
248
249     for (auto& url : completedURLs) {
250         // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
251         bool shouldBypassMainWorldContentSecurityPolicy = this->shouldBypassMainWorldContentSecurityPolicy();
252         if (!shouldBypassMainWorldContentSecurityPolicy && !contentSecurityPolicy()->allowScriptFromSource(url))
253             return Exception { NETWORK_ERR };
254
255         auto scriptLoader = WorkerScriptLoader::create();
256         scriptLoader->loadSynchronously(this, url, FetchOptions::Mode::NoCors, shouldBypassMainWorldContentSecurityPolicy ? ContentSecurityPolicyEnforcement::DoNotEnforce : ContentSecurityPolicyEnforcement::EnforceScriptSrcDirective, resourceRequestIdentifier());
257
258         // If the fetching attempt failed, throw a NETWORK_ERR exception and abort all these steps.
259         if (scriptLoader->failed())
260             return Exception { NETWORK_ERR };
261
262         InspectorInstrumentation::scriptImported(*this, scriptLoader->identifier(), scriptLoader->script());
263
264         NakedPtr<JSC::Exception> exception;
265         m_script->evaluate(ScriptSourceCode(scriptLoader->script(), scriptLoader->responseURL()), exception);
266         if (exception) {
267             m_script->setException(exception);
268             return { };
269         }
270     }
271
272     return { };
273 }
274
275 EventTarget* WorkerGlobalScope::errorEventTarget()
276 {
277     return this;
278 }
279
280 void WorkerGlobalScope::logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, RefPtr<ScriptCallStack>&&)
281 {
282     thread().workerReportingProxy().postExceptionToWorkerObject(errorMessage, lineNumber, columnNumber, sourceURL);
283 }
284
285 void WorkerGlobalScope::addConsoleMessage(std::unique_ptr<Inspector::ConsoleMessage>&& message)
286 {
287     if (!isContextThread()) {
288         postTask(AddConsoleMessageTask(message->source(), message->level(), message->message()));
289         return;
290     }
291
292     InspectorInstrumentation::addMessageToConsole(*this, WTFMove(message));
293 }
294
295 void WorkerGlobalScope::addConsoleMessage(MessageSource source, MessageLevel level, const String& message, unsigned long requestIdentifier)
296 {
297     addMessage(source, level, message, { }, 0, 0, nullptr, nullptr, requestIdentifier);
298 }
299
300 void WorkerGlobalScope::addMessage(MessageSource source, MessageLevel level, const String& messageText, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, RefPtr<ScriptCallStack>&& callStack, JSC::ExecState* state, unsigned long requestIdentifier)
301 {
302     if (!isContextThread()) {
303         postTask(AddConsoleMessageTask(source, level, messageText));
304         return;
305     }
306
307     std::unique_ptr<Inspector::ConsoleMessage> message;
308     if (callStack)
309         message = std::make_unique<Inspector::ConsoleMessage>(source, MessageType::Log, level, messageText, callStack.releaseNonNull(), requestIdentifier);
310     else
311         message = std::make_unique<Inspector::ConsoleMessage>(source, MessageType::Log, level, messageText, sourceURL, lineNumber, columnNumber, state, requestIdentifier);
312     InspectorInstrumentation::addMessageToConsole(*this, WTFMove(message));
313 }
314
315 bool WorkerGlobalScope::isContextThread() const
316 {
317     return currentThread() == thread().threadID();
318 }
319
320 bool WorkerGlobalScope::isJSExecutionForbidden() const
321 {
322     return m_script->isExecutionForbidden();
323 }
324
325 WorkerEventQueue& WorkerGlobalScope::eventQueue() const
326 {
327     return m_eventQueue;
328 }
329
330 #if ENABLE(SUBTLE_CRYPTO)
331
332 bool WorkerGlobalScope::wrapCryptoKey(const Vector<uint8_t>& key, Vector<uint8_t>& wrappedKey)
333 {
334     bool result = false;
335     bool done = false;
336     m_thread.workerLoaderProxy().postTaskToLoader([&result, &key, &wrappedKey, &done, workerGlobalScope = this](ScriptExecutionContext& context) {
337         result = context.wrapCryptoKey(key, wrappedKey);
338         done = true;
339         workerGlobalScope->postTask([](ScriptExecutionContext& context) {
340             ASSERT_UNUSED(context, context.isWorkerGlobalScope());
341         });
342     });
343
344     auto waitResult = MessageQueueMessageReceived;
345     while (!done && waitResult != MessageQueueTerminated)
346         waitResult = m_thread.runLoop().runInMode(this, WorkerRunLoop::defaultMode());
347
348     return result;
349 }
350
351 bool WorkerGlobalScope::unwrapCryptoKey(const Vector<uint8_t>& wrappedKey, Vector<uint8_t>& key)
352 {
353     bool result = false, done = false;
354     m_thread.workerLoaderProxy().postTaskToLoader([&result, &wrappedKey, &key, &done, workerGlobalScope = this](ScriptExecutionContext& context) {
355         result = context.unwrapCryptoKey(wrappedKey, key);
356         done = true;
357         workerGlobalScope->postTask([](ScriptExecutionContext& context) {
358             ASSERT_UNUSED(context, context.isWorkerGlobalScope());
359         });
360     });
361
362     auto waitResult = MessageQueueMessageReceived;
363     while (!done && waitResult != MessageQueueTerminated)
364         waitResult = m_thread.runLoop().runInMode(this, WorkerRunLoop::defaultMode());
365
366     return result;
367 }
368
369 #endif // ENABLE(SUBTLE_CRYPTO)
370
371 Crypto& WorkerGlobalScope::crypto()
372 {
373     if (!m_crypto)
374         m_crypto = Crypto::create(*this);
375     return *m_crypto;
376 }
377
378 #if ENABLE(WEB_TIMING)
379
380 Performance& WorkerGlobalScope::performance() const
381 {
382     return *m_performance;
383 }
384
385 #endif
386
387 } // namespace WebCore