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