db81181727de917e8ea57b56d3f20c9782cc9a46
[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         ASSERT_WITH_SECURITY_IMPLICATION(context->isWorkerGlobalScope());
77         WorkerGlobalScope* workerGlobalScope = static_cast<WorkerGlobalScope*>(context);
78         // Notify parent that this context is closed. Parent is responsible for calling WorkerThread::stop().
79         workerGlobalScope->thread()->workerReportingProxy().workerGlobalScopeClosed();
80     }
81
82     virtual bool isCleanupTask() const { return true; }
83 };
84
85 WorkerGlobalScope::WorkerGlobalScope(const URL& url, const String& userAgent, std::unique_ptr<GroupSettings> settings, WorkerThread* thread, PassRefPtr<SecurityOrigin> topOrigin)
86     : m_url(url)
87     , m_userAgent(userAgent)
88     , m_groupSettings(std::move(settings))
89     , m_script(adoptPtr(new WorkerScriptController(this)))
90     , m_thread(thread)
91 #if ENABLE(INSPECTOR)
92     , m_workerInspectorController(std::make_unique<WorkerInspectorController>(*this))
93 #endif
94     , m_closing(false)
95     , m_eventQueue(*this)
96     , m_topOrigin(topOrigin)
97 {
98     setSecurityOrigin(SecurityOrigin::create(url));
99 }
100
101 WorkerGlobalScope::~WorkerGlobalScope()
102 {
103     ASSERT(currentThread() == thread()->threadID());
104
105     // Make sure we have no observers.
106     notifyObserversOfStop();
107
108     // Notify proxy that we are going away. This can free the WorkerThread object, so do not access it after this.
109     thread()->workerReportingProxy().workerGlobalScopeDestroyed();
110 }
111
112 void WorkerGlobalScope::applyContentSecurityPolicyFromString(const String& policy, ContentSecurityPolicy::HeaderType contentSecurityPolicyType)
113 {
114     setContentSecurityPolicy(ContentSecurityPolicy::create(this));
115     contentSecurityPolicy()->didReceiveHeader(policy, contentSecurityPolicyType);
116 }
117
118 URL WorkerGlobalScope::completeURL(const String& url) const
119 {
120     // Always return a null URL when passed a null string.
121     // FIXME: Should we change the URL constructor to have this behavior?
122     if (url.isNull())
123         return URL();
124     // Always use UTF-8 in Workers.
125     return URL(m_url, url);
126 }
127
128 String WorkerGlobalScope::userAgent(const URL&) const
129 {
130     return m_userAgent;
131 }
132
133 void WorkerGlobalScope::disableEval(const String& errorMessage)
134 {
135     m_script->disableEval(errorMessage);
136 }
137
138 WorkerLocation* WorkerGlobalScope::location() const
139 {
140     if (!m_location)
141         m_location = WorkerLocation::create(m_url);
142     return m_location.get();
143 }
144
145 void WorkerGlobalScope::close()
146 {
147     if (m_closing)
148         return;
149
150     // Let current script run to completion but prevent future script evaluations.
151     // After m_closing is set, all the tasks in the queue continue to be fetched but only
152     // tasks with isCleanupTask()==true will be executed.
153     m_closing = true;
154     postTask(CloseWorkerGlobalScopeTask::create());
155 }
156
157 WorkerNavigator* WorkerGlobalScope::navigator() const
158 {
159     if (!m_navigator)
160         m_navigator = WorkerNavigator::create(m_userAgent);
161     return m_navigator.get();
162 }
163
164 bool WorkerGlobalScope::hasPendingActivity() const
165 {
166     ActiveDOMObjectsSet::const_iterator activeObjectsEnd = activeDOMObjects().end();
167     for (ActiveDOMObjectsSet::const_iterator iter = activeDOMObjects().begin(); iter != activeObjectsEnd; ++iter) {
168         if ((*iter)->hasPendingActivity())
169             return true;
170     }
171
172     HashSet<MessagePort*>::const_iterator messagePortsEnd = messagePorts().end();
173     for (HashSet<MessagePort*>::const_iterator iter = messagePorts().begin(); iter != messagePortsEnd; ++iter) {
174         if ((*iter)->hasPendingActivity())
175             return true;
176     }
177
178     return false;
179 }
180
181 void WorkerGlobalScope::postTask(PassOwnPtr<Task> task)
182 {
183     thread()->runLoop().postTask(task);
184 }
185
186 int WorkerGlobalScope::setTimeout(PassOwnPtr<ScheduledAction> action, int timeout)
187 {
188     return DOMTimer::install(scriptExecutionContext(), action, timeout, true);
189 }
190
191 void WorkerGlobalScope::clearTimeout(int timeoutId)
192 {
193     DOMTimer::removeById(scriptExecutionContext(), timeoutId);
194 }
195
196 int WorkerGlobalScope::setInterval(PassOwnPtr<ScheduledAction> action, int timeout)
197 {
198     return DOMTimer::install(scriptExecutionContext(), action, timeout, false);
199 }
200
201 void WorkerGlobalScope::clearInterval(int timeoutId)
202 {
203     DOMTimer::removeById(scriptExecutionContext(), timeoutId);
204 }
205
206 void WorkerGlobalScope::importScripts(const Vector<String>& urls, ExceptionCode& ec)
207 {
208     ASSERT(contentSecurityPolicy());
209     ec = 0;
210     Vector<String>::const_iterator urlsEnd = urls.end();
211     Vector<URL> completedURLs;
212     for (Vector<String>::const_iterator it = urls.begin(); it != urlsEnd; ++it) {
213         const URL& url = scriptExecutionContext()->completeURL(*it);
214         if (!url.isValid()) {
215             ec = SYNTAX_ERR;
216             return;
217         }
218         completedURLs.append(url);
219     }
220     Vector<URL>::const_iterator end = completedURLs.end();
221
222     for (Vector<URL>::const_iterator it = completedURLs.begin(); it != end; ++it) {
223         RefPtr<WorkerScriptLoader> scriptLoader(WorkerScriptLoader::create());
224         scriptLoader->loadSynchronously(scriptExecutionContext(), *it, AllowCrossOriginRequests);
225
226         // If the fetching attempt failed, throw a NETWORK_ERR exception and abort all these steps.
227         if (scriptLoader->failed()) {
228             ec = XMLHttpRequestException::NETWORK_ERR;
229             return;
230         }
231
232         InspectorInstrumentation::scriptImported(scriptExecutionContext(), scriptLoader->identifier(), scriptLoader->script());
233
234         Deprecated::ScriptValue exception;
235         m_script->evaluate(ScriptSourceCode(scriptLoader->script(), scriptLoader->responseURL()), &exception);
236         if (!exception.hasNoValue()) {
237             m_script->setException(exception);
238             return;
239         }
240     }
241 }
242
243 EventTarget* WorkerGlobalScope::errorEventTarget()
244 {
245     return this;
246 }
247
248 void WorkerGlobalScope::logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, PassRefPtr<ScriptCallStack>)
249 {
250     thread()->workerReportingProxy().postExceptionToWorkerObject(errorMessage, lineNumber, columnNumber, sourceURL);
251 }
252
253 void WorkerGlobalScope::addConsoleMessage(MessageSource source, MessageLevel level, const String& message, unsigned long requestIdentifier)
254 {
255     if (!isContextThread()) {
256         postTask(AddConsoleMessageTask::create(source, level, message));
257         return;
258     }
259
260     thread()->workerReportingProxy().postConsoleMessageToWorkerObject(source, level, message, 0, 0, String());
261     addMessageToWorkerConsole(source, level, message, String(), 0, 0, 0, 0, requestIdentifier);
262 }
263
264 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)
265 {
266     if (!isContextThread()) {
267         postTask(AddConsoleMessageTask::create(source, level, message));
268         return;
269     }
270
271     thread()->workerReportingProxy().postConsoleMessageToWorkerObject(source, level, message, lineNumber, columnNumber, sourceURL);
272     addMessageToWorkerConsole(source, level, message, sourceURL, lineNumber, columnNumber, callStack, state, requestIdentifier);
273 }
274
275 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)
276 {
277     ASSERT(isContextThread());
278     if (callStack)
279         InspectorInstrumentation::addMessageToConsole(this, source, MessageType::Log, level, message, callStack, requestIdentifier);
280     else
281         InspectorInstrumentation::addMessageToConsole(this, source, MessageType::Log, level, message, sourceURL, lineNumber, columnNumber, state, requestIdentifier);
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 WorkerGlobalScope::Observer::Observer(WorkerGlobalScope* context)
295     : m_context(context)
296 {
297     ASSERT(m_context && m_context->isContextThread());
298     m_context->registerObserver(this);
299 }
300
301 WorkerGlobalScope::Observer::~Observer()
302 {
303     if (!m_context)
304         return;
305     ASSERT(m_context->isContextThread());
306     m_context->unregisterObserver(this);
307 }
308
309 void WorkerGlobalScope::Observer::stopObserving()
310 {
311     if (!m_context)
312         return;
313     ASSERT(m_context->isContextThread());
314     m_context->unregisterObserver(this);
315     m_context = 0;
316 }
317
318 void WorkerGlobalScope::registerObserver(Observer* observer)
319 {
320     ASSERT(observer);
321     m_workerObservers.add(observer);
322 }
323
324 void WorkerGlobalScope::unregisterObserver(Observer* observer)
325 {
326     ASSERT(observer);
327     m_workerObservers.remove(observer);
328 }
329
330 void WorkerGlobalScope::notifyObserversOfStop()
331 {
332     HashSet<Observer*>::iterator iter = m_workerObservers.begin();
333     while (iter != m_workerObservers.end()) {
334         WorkerGlobalScope::Observer* observer = *iter;
335         observer->stopObserving();
336         observer->notifyStop();
337         iter = m_workerObservers.begin();
338     }
339 }
340
341 WorkerEventQueue& WorkerGlobalScope::eventQueue() const
342 {
343     return m_eventQueue;
344 }
345
346 #if ENABLE(SUBTLE_CRYPTO)
347 bool WorkerGlobalScope::wrapCryptoKey(const Vector<uint8_t>&, Vector<uint8_t>&)
348 {
349     return false;
350 }
351
352 bool WorkerGlobalScope::unwrapCryptoKey(const Vector<uint8_t>&, Vector<uint8_t>&)
353 {
354     return false;
355 }
356 #endif // ENABLE(SUBTLE_CRYPTO)
357
358 } // namespace WebCore