Move some more assorted classes from ExceptionCode to Exception
[WebKit.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 "InspectorConsoleInstrumentation.h"
36 #include "ScheduledAction.h"
37 #include "ScriptSourceCode.h"
38 #include "SecurityOrigin.h"
39 #include "SecurityOriginPolicy.h"
40 #include "SocketProvider.h"
41 #include "WorkerLoaderProxy.h"
42 #include "WorkerLocation.h"
43 #include "WorkerNavigator.h"
44 #include "WorkerReportingProxy.h"
45 #include "WorkerScriptLoader.h"
46 #include "WorkerThread.h"
47 #include <inspector/ConsoleMessage.h>
48
49 using namespace Inspector;
50
51 namespace WebCore {
52
53 WorkerGlobalScope::WorkerGlobalScope(const URL& url, const String& userAgent, WorkerThread& thread, bool shouldBypassMainWorldContentSecurityPolicy, RefPtr<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider)
54     : m_url(url)
55     , m_userAgent(userAgent)
56     , m_script(std::make_unique<WorkerScriptController>(this))
57     , m_thread(thread)
58     , m_shouldBypassMainWorldContentSecurityPolicy(shouldBypassMainWorldContentSecurityPolicy)
59     , m_eventQueue(*this)
60     , m_topOrigin(topOrigin)
61 #if ENABLE(INDEXED_DATABASE)
62     , m_connectionProxy(connectionProxy)
63 #endif
64 #if ENABLE(WEB_SOCKETS)
65     , m_socketProvider(socketProvider)
66 #endif
67 {
68 #if !ENABLE(INDEXED_DATABASE)
69     UNUSED_PARAM(connectionProxy);
70 #endif
71 #if !ENABLE(WEB_SOCKETS)
72     UNUSED_PARAM(socketProvider);
73 #endif
74
75     auto origin = SecurityOrigin::create(url);
76     if (m_topOrigin->hasUniversalAccess())
77         origin->grantUniversalAccess();
78
79     setSecurityOriginPolicy(SecurityOriginPolicy::create(WTFMove(origin)));
80     setContentSecurityPolicy(std::make_unique<ContentSecurityPolicy>(*this));
81 }
82
83 WorkerGlobalScope::~WorkerGlobalScope()
84 {
85     ASSERT(currentThread() == thread().threadID());
86
87     // Notify proxy that we are going away. This can free the WorkerThread object, so do not access it after this.
88     thread().workerReportingProxy().workerGlobalScopeDestroyed();
89 }
90
91 void WorkerGlobalScope::applyContentSecurityPolicyResponseHeaders(const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders)
92 {
93     contentSecurityPolicy()->didReceiveHeaders(contentSecurityPolicyResponseHeaders);
94 }
95
96 URL WorkerGlobalScope::completeURL(const String& url) const
97 {
98     // Always return a null URL when passed a null string.
99     // FIXME: Should we change the URL constructor to have this behavior?
100     if (url.isNull())
101         return URL();
102     // Always use UTF-8 in Workers.
103     return URL(m_url, url);
104 }
105
106 String WorkerGlobalScope::userAgent(const URL&) const
107 {
108     return m_userAgent;
109 }
110
111 void WorkerGlobalScope::disableEval(const String& errorMessage)
112 {
113     m_script->disableEval(errorMessage);
114 }
115
116 #if ENABLE(WEB_SOCKETS)
117
118 SocketProvider* WorkerGlobalScope::socketProvider()
119 {
120     return m_socketProvider.get();
121 }
122
123 #endif
124
125 #if ENABLE(INDEXED_DATABASE)
126
127 IDBClient::IDBConnectionProxy* WorkerGlobalScope::idbConnectionProxy()
128 {
129 #if ENABLE(INDEXED_DATABASE_IN_WORKERS)
130     return m_connectionProxy.get();
131 #else
132     return nullptr;
133 #endif
134 }
135
136 void WorkerGlobalScope::stopIndexedDatabase()
137 {
138 #if ENABLE(INDEXED_DATABASE_IN_WORKERS)
139     ASSERT(m_connectionProxy);
140     m_connectionProxy->forgetActivityForCurrentThread();
141 #endif
142 }
143
144 #endif // ENABLE(INDEXED_DATABASE)
145
146 WorkerLocation& WorkerGlobalScope::location() const
147 {
148     if (!m_location)
149         m_location = WorkerLocation::create(m_url);
150     return *m_location;
151 }
152
153 void WorkerGlobalScope::close()
154 {
155     if (m_closing)
156         return;
157
158     // Let current script run to completion but prevent future script evaluations.
159     // After m_closing is set, all the tasks in the queue continue to be fetched but only
160     // tasks with isCleanupTask()==true will be executed.
161     m_closing = true;
162     postTask({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext& context) {
163         ASSERT_WITH_SECURITY_IMPLICATION(is<WorkerGlobalScope>(context));
164         WorkerGlobalScope& workerGlobalScope = downcast<WorkerGlobalScope>(context);
165         // Notify parent that this context is closed. Parent is responsible for calling WorkerThread::stop().
166         workerGlobalScope.thread().workerReportingProxy().workerGlobalScopeClosed();
167     } });
168 }
169
170 WorkerNavigator& WorkerGlobalScope::navigator() const
171 {
172     if (!m_navigator)
173         m_navigator = WorkerNavigator::create(m_userAgent);
174     return *m_navigator;
175 }
176
177 void WorkerGlobalScope::postTask(Task&& task)
178 {
179     thread().runLoop().postTask(WTFMove(task));
180 }
181
182 int WorkerGlobalScope::setTimeout(std::unique_ptr<ScheduledAction> action, int timeout)
183 {
184     return DOMTimer::install(*this, WTFMove(action), std::chrono::milliseconds(timeout), true);
185 }
186
187 void WorkerGlobalScope::clearTimeout(int timeoutId)
188 {
189     DOMTimer::removeById(*this, timeoutId);
190 }
191
192 int WorkerGlobalScope::setInterval(std::unique_ptr<ScheduledAction> action, int timeout)
193 {
194     return DOMTimer::install(*this, WTFMove(action), std::chrono::milliseconds(timeout), false);
195 }
196
197 void WorkerGlobalScope::clearInterval(int timeoutId)
198 {
199     DOMTimer::removeById(*this, timeoutId);
200 }
201
202 ExceptionOr<void> WorkerGlobalScope::importScripts(const Vector<String>& urls)
203 {
204     ASSERT(contentSecurityPolicy());
205
206     Vector<URL> completedURLs;
207     completedURLs.reserveInitialCapacity(urls.size());
208     for (auto& entry : urls) {
209         URL url = completeURL(entry);
210         if (!url.isValid())
211             return Exception { SYNTAX_ERR };
212         completedURLs.uncheckedAppend(WTFMove(url));
213     }
214
215     for (auto& url : completedURLs) {
216         // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
217         bool shouldBypassMainWorldContentSecurityPolicy = this->shouldBypassMainWorldContentSecurityPolicy();
218         if (!shouldBypassMainWorldContentSecurityPolicy && !contentSecurityPolicy()->allowScriptFromSource(url))
219             return Exception { NETWORK_ERR };
220
221         auto scriptLoader = WorkerScriptLoader::create();
222         scriptLoader->loadSynchronously(this, url, FetchOptions::Mode::NoCors, shouldBypassMainWorldContentSecurityPolicy ? ContentSecurityPolicyEnforcement::DoNotEnforce : ContentSecurityPolicyEnforcement::EnforceScriptSrcDirective);
223
224         // If the fetching attempt failed, throw a NETWORK_ERR exception and abort all these steps.
225         if (scriptLoader->failed())
226             return Exception { NETWORK_ERR };
227
228         InspectorInstrumentation::scriptImported(this, scriptLoader->identifier(), scriptLoader->script());
229
230         NakedPtr<JSC::Exception> exception;
231         m_script->evaluate(ScriptSourceCode(scriptLoader->script(), scriptLoader->responseURL()), exception);
232         if (exception) {
233             m_script->setException(exception);
234             return { };
235         }
236     }
237
238     return { };
239 }
240
241 EventTarget* WorkerGlobalScope::errorEventTarget()
242 {
243     return this;
244 }
245
246 void WorkerGlobalScope::logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, RefPtr<ScriptCallStack>&&)
247 {
248     thread().workerReportingProxy().postExceptionToWorkerObject(errorMessage, lineNumber, columnNumber, sourceURL);
249 }
250
251 void WorkerGlobalScope::addConsoleMessage(std::unique_ptr<Inspector::ConsoleMessage>&& message)
252 {
253     if (!isContextThread()) {
254         postTask(AddConsoleMessageTask(message->source(), message->level(), message->message()));
255         return;
256     }
257
258     thread().workerReportingProxy().postConsoleMessageToWorkerObject(message->source(), message->level(), message->message(), message->line(), message->column(), message->url());
259     InspectorInstrumentation::addMessageToConsole(this, WTFMove(message));
260 }
261
262 void WorkerGlobalScope::addConsoleMessage(MessageSource source, MessageLevel level, const String& message, unsigned long requestIdentifier)
263 {
264     addMessage(source, level, message, { }, 0, 0, nullptr, nullptr, requestIdentifier);
265 }
266
267 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)
268 {
269     if (!isContextThread()) {
270         postTask(AddConsoleMessageTask(source, level, messageText));
271         return;
272     }
273
274     thread().workerReportingProxy().postConsoleMessageToWorkerObject(source, level, messageText, lineNumber, columnNumber, sourceURL);
275
276     std::unique_ptr<Inspector::ConsoleMessage> message;
277     if (callStack)
278         message = std::make_unique<Inspector::ConsoleMessage>(source, MessageType::Log, level, messageText, WTFMove(callStack), requestIdentifier);
279     else
280         message = std::make_unique<Inspector::ConsoleMessage>(source, MessageType::Log, level, messageText, sourceURL, lineNumber, columnNumber, state, requestIdentifier);
281     InspectorInstrumentation::addMessageToConsole(this, WTFMove(message));
282 }
283
284 bool WorkerGlobalScope::isContextThread() const
285 {
286     return currentThread() == thread().threadID();
287 }
288
289 bool WorkerGlobalScope::isJSExecutionForbidden() const
290 {
291     return m_script->isExecutionForbidden();
292 }
293
294 WorkerEventQueue& WorkerGlobalScope::eventQueue() const
295 {
296     return m_eventQueue;
297 }
298
299 #if ENABLE(SUBTLE_CRYPTO)
300
301 bool WorkerGlobalScope::wrapCryptoKey(const Vector<uint8_t>& key, Vector<uint8_t>& wrappedKey)
302 {
303     bool result = false;
304     bool done = false;
305     m_thread.workerLoaderProxy().postTaskToLoader([&result, &key, &wrappedKey, &done, workerGlobalScope = this](ScriptExecutionContext& context) {
306         result = context.wrapCryptoKey(key, wrappedKey);
307         done = true;
308         workerGlobalScope->postTask([](ScriptExecutionContext& context) {
309             ASSERT_UNUSED(context, context.isWorkerGlobalScope());
310         });
311     });
312
313     auto waitResult = MessageQueueMessageReceived;
314     while (!done && waitResult != MessageQueueTerminated)
315         waitResult = m_thread.runLoop().runInMode(this, WorkerRunLoop::defaultMode());
316
317     return result;
318 }
319
320 bool WorkerGlobalScope::unwrapCryptoKey(const Vector<uint8_t>& wrappedKey, Vector<uint8_t>& key)
321 {
322     bool result = false, done = false;
323     m_thread.workerLoaderProxy().postTaskToLoader([&result, &wrappedKey, &key, &done, workerGlobalScope = this](ScriptExecutionContext& context) {
324         result = context.unwrapCryptoKey(wrappedKey, key);
325         done = true;
326         workerGlobalScope->postTask([](ScriptExecutionContext& context) {
327             ASSERT_UNUSED(context, context.isWorkerGlobalScope());
328         });
329     });
330
331     auto waitResult = MessageQueueMessageReceived;
332     while (!done && waitResult != MessageQueueTerminated)
333         waitResult = m_thread.runLoop().runInMode(this, WorkerRunLoop::defaultMode());
334
335     return result;
336 }
337
338 #endif // ENABLE(SUBTLE_CRYPTO)
339
340 Crypto& WorkerGlobalScope::crypto()
341 {
342     if (!m_crypto)
343         m_crypto = Crypto::create(*this);
344     return *m_crypto;
345 }
346
347 } // namespace WebCore