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