Add checked casts for ScriptExecutionContext.
[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 COMPUTER, 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 COMPUTER, 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 "ActiveDOMObject.h"
32 #include "ContentSecurityPolicy.h"
33 #include "DOMTimer.h"
34 #include "DOMURL.h"
35 #include "DOMWindow.h"
36 #include "ErrorEvent.h"
37 #include "Event.h"
38 #include "EventException.h"
39 #include "ExceptionCode.h"
40 #include "InspectorConsoleInstrumentation.h"
41 #include "MessagePort.h"
42 #include "NotImplemented.h"
43 #include "ScheduledAction.h"
44 #include "ScriptSourceCode.h"
45 #include "SecurityOrigin.h"
46 #include "URL.h"
47 #include "WorkerInspectorController.h"
48 #include "WorkerLocation.h"
49 #include "WorkerNavigator.h"
50 #include "WorkerObjectProxy.h"
51 #include "WorkerScriptLoader.h"
52 #include "WorkerThread.h"
53 #include "WorkerThreadableLoader.h"
54 #include "XMLHttpRequestException.h"
55 #include <bindings/ScriptValue.h>
56 #include <inspector/ScriptCallStack.h>
57 #include <wtf/RefPtr.h>
58
59 #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
60 #include "NotificationCenter.h"
61 #endif
62
63 using namespace Inspector;
64
65 namespace WebCore {
66
67 class CloseWorkerGlobalScopeTask : public ScriptExecutionContext::Task {
68 public:
69     static PassOwnPtr<CloseWorkerGlobalScopeTask> create()
70     {
71         return adoptPtr(new CloseWorkerGlobalScopeTask);
72     }
73
74     virtual void performTask(ScriptExecutionContext *context)
75     {
76         // Notify parent that this context is closed. Parent is responsible for calling WorkerThread::stop().
77         toWorkerGlobalScope(context)->thread().workerReportingProxy().workerGlobalScopeClosed();
78     }
79
80     virtual bool isCleanupTask() const { return true; }
81 };
82
83 WorkerGlobalScope::WorkerGlobalScope(const URL& url, const String& userAgent, std::unique_ptr<GroupSettings> settings, WorkerThread& thread, PassRefPtr<SecurityOrigin> topOrigin)
84     : m_url(url)
85     , m_userAgent(userAgent)
86     , m_groupSettings(std::move(settings))
87     , m_script(adoptPtr(new WorkerScriptController(this)))
88     , m_thread(thread)
89 #if ENABLE(INSPECTOR)
90     , m_workerInspectorController(std::make_unique<WorkerInspectorController>(*this))
91 #endif
92     , m_closing(false)
93     , m_eventQueue(*this)
94     , m_topOrigin(topOrigin)
95 {
96     setSecurityOrigin(SecurityOrigin::create(url));
97 }
98
99 WorkerGlobalScope::~WorkerGlobalScope()
100 {
101     ASSERT(currentThread() == thread().threadID());
102
103     // Make sure we have no observers.
104     notifyObserversOfStop();
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 void WorkerGlobalScope::applyContentSecurityPolicyFromString(const String& policy, ContentSecurityPolicy::HeaderType contentSecurityPolicyType)
111 {
112     setContentSecurityPolicy(ContentSecurityPolicy::create(this));
113     contentSecurityPolicy()->didReceiveHeader(policy, contentSecurityPolicyType);
114 }
115
116 URL WorkerGlobalScope::completeURL(const String& url) const
117 {
118     // Always return a null URL when passed a null string.
119     // FIXME: Should we change the URL constructor to have this behavior?
120     if (url.isNull())
121         return URL();
122     // Always use UTF-8 in Workers.
123     return URL(m_url, url);
124 }
125
126 String WorkerGlobalScope::userAgent(const URL&) const
127 {
128     return m_userAgent;
129 }
130
131 void WorkerGlobalScope::disableEval(const String& errorMessage)
132 {
133     m_script->disableEval(errorMessage);
134 }
135
136 WorkerLocation* WorkerGlobalScope::location() const
137 {
138     if (!m_location)
139         m_location = WorkerLocation::create(m_url);
140     return m_location.get();
141 }
142
143 void WorkerGlobalScope::close()
144 {
145     if (m_closing)
146         return;
147
148     // Let current script run to completion but prevent future script evaluations.
149     // After m_closing is set, all the tasks in the queue continue to be fetched but only
150     // tasks with isCleanupTask()==true will be executed.
151     m_closing = true;
152     postTask(CloseWorkerGlobalScopeTask::create());
153 }
154
155 WorkerNavigator* WorkerGlobalScope::navigator() const
156 {
157     if (!m_navigator)
158         m_navigator = WorkerNavigator::create(m_userAgent);
159     return m_navigator.get();
160 }
161
162 bool WorkerGlobalScope::hasPendingActivity() const
163 {
164     ActiveDOMObjectsSet::const_iterator activeObjectsEnd = activeDOMObjects().end();
165     for (ActiveDOMObjectsSet::const_iterator iter = activeDOMObjects().begin(); iter != activeObjectsEnd; ++iter) {
166         if ((*iter)->hasPendingActivity())
167             return true;
168     }
169
170     HashSet<MessagePort*>::const_iterator messagePortsEnd = messagePorts().end();
171     for (HashSet<MessagePort*>::const_iterator iter = messagePorts().begin(); iter != messagePortsEnd; ++iter) {
172         if ((*iter)->hasPendingActivity())
173             return true;
174     }
175
176     return false;
177 }
178
179 void WorkerGlobalScope::postTask(PassOwnPtr<Task> task)
180 {
181     thread().runLoop().postTask(task);
182 }
183
184 int WorkerGlobalScope::setTimeout(PassOwnPtr<ScheduledAction> action, int timeout)
185 {
186     return DOMTimer::install(scriptExecutionContext(), action, timeout, true);
187 }
188
189 void WorkerGlobalScope::clearTimeout(int timeoutId)
190 {
191     DOMTimer::removeById(scriptExecutionContext(), timeoutId);
192 }
193
194 int WorkerGlobalScope::setInterval(PassOwnPtr<ScheduledAction> action, int timeout)
195 {
196     return DOMTimer::install(scriptExecutionContext(), action, timeout, false);
197 }
198
199 void WorkerGlobalScope::clearInterval(int timeoutId)
200 {
201     DOMTimer::removeById(scriptExecutionContext(), timeoutId);
202 }
203
204 void WorkerGlobalScope::importScripts(const Vector<String>& urls, ExceptionCode& ec)
205 {
206     ASSERT(contentSecurityPolicy());
207     ec = 0;
208     Vector<String>::const_iterator urlsEnd = urls.end();
209     Vector<URL> completedURLs;
210     for (Vector<String>::const_iterator it = urls.begin(); it != urlsEnd; ++it) {
211         const URL& url = scriptExecutionContext()->completeURL(*it);
212         if (!url.isValid()) {
213             ec = SYNTAX_ERR;
214             return;
215         }
216         completedURLs.append(url);
217     }
218     Vector<URL>::const_iterator end = completedURLs.end();
219
220     for (Vector<URL>::const_iterator it = completedURLs.begin(); it != end; ++it) {
221         RefPtr<WorkerScriptLoader> scriptLoader(WorkerScriptLoader::create());
222         scriptLoader->loadSynchronously(scriptExecutionContext(), *it, AllowCrossOriginRequests);
223
224         // If the fetching attempt failed, throw a NETWORK_ERR exception and abort all these steps.
225         if (scriptLoader->failed()) {
226             ec = XMLHttpRequestException::NETWORK_ERR;
227             return;
228         }
229
230         InspectorInstrumentation::scriptImported(scriptExecutionContext(), scriptLoader->identifier(), scriptLoader->script());
231
232         Deprecated::ScriptValue exception;
233         m_script->evaluate(ScriptSourceCode(scriptLoader->script(), scriptLoader->responseURL()), &exception);
234         if (!exception.hasNoValue()) {
235             m_script->setException(exception);
236             return;
237         }
238     }
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, PassRefPtr<ScriptCallStack>)
247 {
248     thread().workerReportingProxy().postExceptionToWorkerObject(errorMessage, lineNumber, columnNumber, sourceURL);
249 }
250
251 void WorkerGlobalScope::addConsoleMessage(MessageSource source, MessageLevel level, const String& message, unsigned long requestIdentifier)
252 {
253     if (!isContextThread()) {
254         postTask(AddConsoleMessageTask::create(source, level, message));
255         return;
256     }
257
258     thread().workerReportingProxy().postConsoleMessageToWorkerObject(source, level, message, 0, 0, String());
259     addMessageToWorkerConsole(source, level, message, String(), 0, 0, 0, 0, requestIdentifier);
260 }
261
262 void WorkerGlobalScope::addMessage(MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, PassRefPtr<ScriptCallStack> callStack, JSC::ExecState* state, unsigned long requestIdentifier)
263 {
264     if (!isContextThread()) {
265         postTask(AddConsoleMessageTask::create(source, level, message));
266         return;
267     }
268
269     thread().workerReportingProxy().postConsoleMessageToWorkerObject(source, level, message, lineNumber, columnNumber, sourceURL);
270     addMessageToWorkerConsole(source, level, message, sourceURL, lineNumber, columnNumber, callStack, state, requestIdentifier);
271 }
272
273 void WorkerGlobalScope::addMessageToWorkerConsole(MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, PassRefPtr<ScriptCallStack> callStack, JSC::ExecState* state, unsigned long requestIdentifier)
274 {
275     ASSERT(isContextThread());
276     if (callStack)
277         InspectorInstrumentation::addMessageToConsole(this, source, MessageType::Log, level, message, callStack, requestIdentifier);
278     else
279         InspectorInstrumentation::addMessageToConsole(this, source, MessageType::Log, level, message, sourceURL, lineNumber, columnNumber, state, requestIdentifier);
280 }
281
282 bool WorkerGlobalScope::isContextThread() const
283 {
284     return currentThread() == thread().threadID();
285 }
286
287 bool WorkerGlobalScope::isJSExecutionForbidden() const
288 {
289     return m_script->isExecutionForbidden();
290 }
291
292 WorkerGlobalScope::Observer::Observer(WorkerGlobalScope* context)
293     : m_context(context)
294 {
295     ASSERT(m_context && m_context->isContextThread());
296     m_context->registerObserver(this);
297 }
298
299 WorkerGlobalScope::Observer::~Observer()
300 {
301     if (!m_context)
302         return;
303     ASSERT(m_context->isContextThread());
304     m_context->unregisterObserver(this);
305 }
306
307 void WorkerGlobalScope::Observer::stopObserving()
308 {
309     if (!m_context)
310         return;
311     ASSERT(m_context->isContextThread());
312     m_context->unregisterObserver(this);
313     m_context = 0;
314 }
315
316 void WorkerGlobalScope::registerObserver(Observer* observer)
317 {
318     ASSERT(observer);
319     m_workerObservers.add(observer);
320 }
321
322 void WorkerGlobalScope::unregisterObserver(Observer* observer)
323 {
324     ASSERT(observer);
325     m_workerObservers.remove(observer);
326 }
327
328 void WorkerGlobalScope::notifyObserversOfStop()
329 {
330     HashSet<Observer*>::iterator iter = m_workerObservers.begin();
331     while (iter != m_workerObservers.end()) {
332         WorkerGlobalScope::Observer* observer = *iter;
333         observer->stopObserving();
334         observer->notifyStop();
335         iter = m_workerObservers.begin();
336     }
337 }
338
339 WorkerEventQueue& WorkerGlobalScope::eventQueue() const
340 {
341     return m_eventQueue;
342 }
343
344 #if ENABLE(SUBTLE_CRYPTO)
345 bool WorkerGlobalScope::wrapCryptoKey(const Vector<uint8_t>&, Vector<uint8_t>&)
346 {
347     return false;
348 }
349
350 bool WorkerGlobalScope::unwrapCryptoKey(const Vector<uint8_t>&, Vector<uint8_t>&)
351 {
352     return false;
353 }
354 #endif // ENABLE(SUBTLE_CRYPTO)
355
356 } // namespace WebCore