Teach ScriptExecutionContexts about their SessionID.
[WebKit-https.git] / Source / WebCore / workers / WorkerGlobalScope.cpp
1 /*
2  * Copyright (C) 2008-2017 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 "IDBConnectionProxy.h"
34 #include "InspectorInstrumentation.h"
35 #include "Performance.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, MonotonicTime timeOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider, SessionID sessionID)
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     , m_socketProvider(socketProvider)
69     , m_performance(Performance::create(*this, timeOrigin))
70     , m_sessionID(sessionID)
71 {
72 #if !ENABLE(INDEXED_DATABASE)
73     UNUSED_PARAM(connectionProxy);
74 #endif
75
76     auto origin = SecurityOrigin::create(url);
77     if (m_topOrigin->hasUniversalAccess())
78         origin->grantUniversalAccess();
79     if (m_topOrigin->needsStorageAccessFromFileURLsQuirk())
80         origin->grantStorageAccessFromFileURLsQuirk();
81
82     setSecurityOriginPolicy(SecurityOriginPolicy::create(WTFMove(origin)));
83     setContentSecurityPolicy(std::make_unique<ContentSecurityPolicy>(*this));
84 }
85
86 WorkerGlobalScope::~WorkerGlobalScope()
87 {
88     ASSERT(currentThread() == thread().threadID());
89
90     m_performance = nullptr;
91     m_crypto = nullptr;
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 String WorkerGlobalScope::origin() const
98 {
99     auto* securityOrigin = this->securityOrigin();
100     return securityOrigin ? securityOrigin->toString() : emptyString();
101 }
102
103 void WorkerGlobalScope::removeAllEventListeners()
104 {
105     EventTarget::removeAllEventListeners();
106     m_performance->removeAllEventListeners();
107 }
108
109 bool WorkerGlobalScope::isSecureContext() const
110 {
111     return securityOrigin() && securityOrigin()->isPotentionallyTrustworthy();
112 }
113
114 void WorkerGlobalScope::applyContentSecurityPolicyResponseHeaders(const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders)
115 {
116     contentSecurityPolicy()->didReceiveHeaders(contentSecurityPolicyResponseHeaders);
117 }
118
119 URL WorkerGlobalScope::completeURL(const String& url) const
120 {
121     // Always return a null URL when passed a null string.
122     // FIXME: Should we change the URL constructor to have this behavior?
123     if (url.isNull())
124         return URL();
125     // Always use UTF-8 in Workers.
126     return URL(m_url, url);
127 }
128
129 String WorkerGlobalScope::userAgent(const URL&) const
130 {
131     return m_userAgent;
132 }
133
134 void WorkerGlobalScope::disableEval(const String& errorMessage)
135 {
136     m_script->disableEval(errorMessage);
137 }
138
139 void WorkerGlobalScope::disableWebAssembly(const String& errorMessage)
140 {
141     m_script->disableWebAssembly(errorMessage);
142 }
143
144 SocketProvider* WorkerGlobalScope::socketProvider()
145 {
146     return m_socketProvider.get();
147 }
148
149 #if ENABLE(INDEXED_DATABASE)
150
151 IDBClient::IDBConnectionProxy* WorkerGlobalScope::idbConnectionProxy()
152 {
153 #if ENABLE(INDEXED_DATABASE_IN_WORKERS)
154     return m_connectionProxy.get();
155 #else
156     return nullptr;
157 #endif
158 }
159
160 void WorkerGlobalScope::stopIndexedDatabase()
161 {
162 #if ENABLE(INDEXED_DATABASE_IN_WORKERS)
163     ASSERT(m_connectionProxy);
164     m_connectionProxy->forgetActivityForCurrentThread();
165 #endif
166 }
167
168 #endif // ENABLE(INDEXED_DATABASE)
169
170 WorkerLocation& WorkerGlobalScope::location() const
171 {
172     if (!m_location)
173         m_location = WorkerLocation::create(m_url);
174     return *m_location;
175 }
176
177 void WorkerGlobalScope::close()
178 {
179     if (m_closing)
180         return;
181
182     // Let current script run to completion but prevent future script evaluations.
183     // After m_closing is set, all the tasks in the queue continue to be fetched but only
184     // tasks with isCleanupTask()==true will be executed.
185     m_closing = true;
186     postTask({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext& context) {
187         ASSERT_WITH_SECURITY_IMPLICATION(is<WorkerGlobalScope>(context));
188         WorkerGlobalScope& workerGlobalScope = downcast<WorkerGlobalScope>(context);
189         // Notify parent that this context is closed. Parent is responsible for calling WorkerThread::stop().
190         workerGlobalScope.thread().workerReportingProxy().workerGlobalScopeClosed();
191     } });
192 }
193
194 WorkerNavigator& WorkerGlobalScope::navigator()
195 {
196     if (!m_navigator)
197         m_navigator = WorkerNavigator::create(*this, m_userAgent);
198     return *m_navigator;
199 }
200
201 void WorkerGlobalScope::postTask(Task&& task)
202 {
203     thread().runLoop().postTask(WTFMove(task));
204 }
205
206 ExceptionOr<int> WorkerGlobalScope::setTimeout(JSC::ExecState& state, std::unique_ptr<ScheduledAction> action, int timeout, Vector<JSC::Strong<JSC::Unknown>>&& arguments)
207 {
208     // FIXME: Should this check really happen here? Or should it happen when code is about to eval?
209     if (action->type() == ScheduledAction::Type::Code) {
210         if (!contentSecurityPolicy()->allowEval(&state))
211             return 0;
212     }
213
214     action->addArguments(WTFMove(arguments));
215
216     return DOMTimer::install(*this, WTFMove(action), Seconds::fromMilliseconds(timeout), true);
217 }
218
219 void WorkerGlobalScope::clearTimeout(int timeoutId)
220 {
221     DOMTimer::removeById(*this, timeoutId);
222 }
223
224 ExceptionOr<int> WorkerGlobalScope::setInterval(JSC::ExecState& state, std::unique_ptr<ScheduledAction> action, int timeout, Vector<JSC::Strong<JSC::Unknown>>&& arguments)
225 {
226     // FIXME: Should this check really happen here? Or should it happen when code is about to eval?
227     if (action->type() == ScheduledAction::Type::Code) {
228         if (!contentSecurityPolicy()->allowEval(&state))
229             return 0;
230     }
231
232     action->addArguments(WTFMove(arguments));
233
234     return DOMTimer::install(*this, WTFMove(action), Seconds::fromMilliseconds(timeout), false);
235 }
236
237 void WorkerGlobalScope::clearInterval(int timeoutId)
238 {
239     DOMTimer::removeById(*this, timeoutId);
240 }
241
242 ExceptionOr<void> WorkerGlobalScope::importScripts(const Vector<String>& urls)
243 {
244     ASSERT(contentSecurityPolicy());
245
246     Vector<URL> completedURLs;
247     completedURLs.reserveInitialCapacity(urls.size());
248     for (auto& entry : urls) {
249         URL url = completeURL(entry);
250         if (!url.isValid())
251             return Exception { SyntaxError };
252         completedURLs.uncheckedAppend(WTFMove(url));
253     }
254
255     for (auto& url : completedURLs) {
256         // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
257         bool shouldBypassMainWorldContentSecurityPolicy = this->shouldBypassMainWorldContentSecurityPolicy();
258         if (!shouldBypassMainWorldContentSecurityPolicy && !contentSecurityPolicy()->allowScriptFromSource(url))
259             return Exception { NetworkError };
260
261         auto scriptLoader = WorkerScriptLoader::create();
262         scriptLoader->loadSynchronously(this, url, FetchOptions::Mode::NoCors, shouldBypassMainWorldContentSecurityPolicy ? ContentSecurityPolicyEnforcement::DoNotEnforce : ContentSecurityPolicyEnforcement::EnforceScriptSrcDirective, resourceRequestIdentifier());
263
264         // If the fetching attempt failed, throw a NetworkError exception and abort all these steps.
265         if (scriptLoader->failed())
266             return Exception { NetworkError };
267
268         InspectorInstrumentation::scriptImported(*this, scriptLoader->identifier(), scriptLoader->script());
269
270         NakedPtr<JSC::Exception> exception;
271         m_script->evaluate(ScriptSourceCode(scriptLoader->script(), scriptLoader->responseURL()), exception);
272         if (exception) {
273             m_script->setException(exception);
274             return { };
275         }
276     }
277
278     return { };
279 }
280
281 EventTarget* WorkerGlobalScope::errorEventTarget()
282 {
283     return this;
284 }
285
286 void WorkerGlobalScope::logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, RefPtr<ScriptCallStack>&&)
287 {
288     thread().workerReportingProxy().postExceptionToWorkerObject(errorMessage, lineNumber, columnNumber, sourceURL);
289 }
290
291 void WorkerGlobalScope::addConsoleMessage(std::unique_ptr<Inspector::ConsoleMessage>&& message)
292 {
293     if (!isContextThread()) {
294         postTask(AddConsoleMessageTask(message->source(), message->level(), message->message()));
295         return;
296     }
297
298     InspectorInstrumentation::addMessageToConsole(*this, WTFMove(message));
299 }
300
301 void WorkerGlobalScope::addConsoleMessage(MessageSource source, MessageLevel level, const String& message, unsigned long requestIdentifier)
302 {
303     addMessage(source, level, message, { }, 0, 0, nullptr, nullptr, requestIdentifier);
304 }
305
306 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)
307 {
308     if (!isContextThread()) {
309         postTask(AddConsoleMessageTask(source, level, messageText));
310         return;
311     }
312
313     std::unique_ptr<Inspector::ConsoleMessage> message;
314     if (callStack)
315         message = std::make_unique<Inspector::ConsoleMessage>(source, MessageType::Log, level, messageText, callStack.releaseNonNull(), requestIdentifier);
316     else
317         message = std::make_unique<Inspector::ConsoleMessage>(source, MessageType::Log, level, messageText, sourceURL, lineNumber, columnNumber, state, requestIdentifier);
318     InspectorInstrumentation::addMessageToConsole(*this, WTFMove(message));
319 }
320
321 bool WorkerGlobalScope::isContextThread() const
322 {
323     return currentThread() == thread().threadID();
324 }
325
326 bool WorkerGlobalScope::isJSExecutionForbidden() const
327 {
328     return m_script->isExecutionForbidden();
329 }
330
331 WorkerEventQueue& WorkerGlobalScope::eventQueue() const
332 {
333     return m_eventQueue;
334 }
335
336 #if ENABLE(SUBTLE_CRYPTO)
337
338 bool WorkerGlobalScope::wrapCryptoKey(const Vector<uint8_t>& key, Vector<uint8_t>& wrappedKey)
339 {
340     bool result = false;
341     bool done = false;
342     m_thread.workerLoaderProxy().postTaskToLoader([&result, &key, &wrappedKey, &done, workerGlobalScope = this](ScriptExecutionContext& context) {
343         result = context.wrapCryptoKey(key, wrappedKey);
344         done = true;
345         workerGlobalScope->postTask([](ScriptExecutionContext& context) {
346             ASSERT_UNUSED(context, context.isWorkerGlobalScope());
347         });
348     });
349
350     auto waitResult = MessageQueueMessageReceived;
351     while (!done && waitResult != MessageQueueTerminated)
352         waitResult = m_thread.runLoop().runInMode(this, WorkerRunLoop::defaultMode());
353
354     return result;
355 }
356
357 bool WorkerGlobalScope::unwrapCryptoKey(const Vector<uint8_t>& wrappedKey, Vector<uint8_t>& key)
358 {
359     bool result = false, done = false;
360     m_thread.workerLoaderProxy().postTaskToLoader([&result, &wrappedKey, &key, &done, workerGlobalScope = this](ScriptExecutionContext& context) {
361         result = context.unwrapCryptoKey(wrappedKey, key);
362         done = true;
363         workerGlobalScope->postTask([](ScriptExecutionContext& context) {
364             ASSERT_UNUSED(context, context.isWorkerGlobalScope());
365         });
366     });
367
368     auto waitResult = MessageQueueMessageReceived;
369     while (!done && waitResult != MessageQueueTerminated)
370         waitResult = m_thread.runLoop().runInMode(this, WorkerRunLoop::defaultMode());
371
372     return result;
373 }
374
375 #endif // ENABLE(SUBTLE_CRYPTO)
376
377 Crypto& WorkerGlobalScope::crypto()
378 {
379     if (!m_crypto)
380         m_crypto = Crypto::create(*this);
381     return *m_crypto;
382 }
383
384 Performance& WorkerGlobalScope::performance() const
385 {
386     return *m_performance;
387 }
388
389 } // namespace WebCore