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 "core/workers/WorkerGlobalScope.h"
30
31 #include "bindings/v8/ExceptionState.h"
32 #include "bindings/v8/ScheduledAction.h"
33 #include "bindings/v8/ScriptSourceCode.h"
34 #include "bindings/v8/ScriptValue.h"
35 #include "core/dom/ActiveDOMObject.h"
36 #include "core/dom/AddConsoleMessageTask.h"
37 #include "core/dom/ContextLifecycleNotifier.h"
38 #include "core/dom/DOMURL.h"
39 #include "core/dom/ExceptionCode.h"
40 #include "core/dom/MessagePort.h"
41 #include "core/events/ErrorEvent.h"
42 #include "core/events/Event.h"
43 #include "core/inspector/InspectorConsoleInstrumentation.h"
44 #include "core/inspector/ScriptCallStack.h"
45 #include "core/inspector/WorkerInspectorController.h"
46 #include "core/loader/WorkerThreadableLoader.h"
47 #include "core/frame/LocalDOMWindow.h"
48 #include "core/workers/WorkerNavigator.h"
49 #include "core/workers/WorkerClients.h"
50 #include "core/workers/WorkerConsole.h"
51 #include "core/workers/WorkerLocation.h"
52 #include "core/workers/WorkerNavigator.h"
53 #include "core/workers/WorkerReportingProxy.h"
54 #include "core/workers/WorkerScriptLoader.h"
55 #include "core/workers/WorkerThread.h"
56 #include "platform/network/ContentSecurityPolicyParsers.h"
57 #include "platform/weborigin/KURL.h"
58 #include "platform/weborigin/SecurityOrigin.h"
59
60 namespace WebCore {
61
62 class CloseWorkerGlobalScopeTask : public ExecutionContextTask {
63 public:
create()64 static PassOwnPtr<CloseWorkerGlobalScopeTask> create()
65 {
66 return adoptPtr(new CloseWorkerGlobalScopeTask);
67 }
68
performTask(ExecutionContext * context)69 virtual void performTask(ExecutionContext *context)
70 {
71 WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context);
72 // Notify parent that this context is closed. Parent is responsible for calling WorkerThread::stop().
73 workerGlobalScope->thread()->workerReportingProxy().workerGlobalScopeClosed();
74 }
75
isCleanupTask() const76 virtual bool isCleanupTask() const { return true; }
77 };
78
WorkerGlobalScope(const KURL & url,const String & userAgent,WorkerThread * thread,double timeOrigin,PassOwnPtrWillBeRawPtr<WorkerClients> workerClients)79 WorkerGlobalScope::WorkerGlobalScope(const KURL& url, const String& userAgent, WorkerThread* thread, double timeOrigin, PassOwnPtrWillBeRawPtr<WorkerClients> workerClients)
80 : m_url(url)
81 , m_userAgent(userAgent)
82 , m_script(adoptPtr(new WorkerScriptController(*this)))
83 , m_thread(thread)
84 , m_workerInspectorController(adoptPtr(new WorkerInspectorController(this)))
85 , m_closing(false)
86 , m_eventQueue(WorkerEventQueue::create(this))
87 , m_workerClients(workerClients)
88 , m_timeOrigin(timeOrigin)
89 , m_terminationObserver(0)
90 {
91 ScriptWrappable::init(this);
92 setClient(this);
93 setSecurityOrigin(SecurityOrigin::create(url));
94 m_workerClients->reattachThread();
95 }
96
~WorkerGlobalScope()97 WorkerGlobalScope::~WorkerGlobalScope()
98 {
99 }
100
applyContentSecurityPolicyFromString(const String & policy,ContentSecurityPolicyHeaderType contentSecurityPolicyType)101 void WorkerGlobalScope::applyContentSecurityPolicyFromString(const String& policy, ContentSecurityPolicyHeaderType contentSecurityPolicyType)
102 {
103 setContentSecurityPolicy(ContentSecurityPolicy::create(this));
104 contentSecurityPolicy()->didReceiveHeader(policy, contentSecurityPolicyType, ContentSecurityPolicyHeaderSourceHTTP);
105 }
106
executionContext() const107 ExecutionContext* WorkerGlobalScope::executionContext() const
108 {
109 return const_cast<WorkerGlobalScope*>(this);
110 }
111
virtualURL() const112 const KURL& WorkerGlobalScope::virtualURL() const
113 {
114 return m_url;
115 }
116
virtualCompleteURL(const String & url) const117 KURL WorkerGlobalScope::virtualCompleteURL(const String& url) const
118 {
119 return completeURL(url);
120 }
121
completeURL(const String & url) const122 KURL WorkerGlobalScope::completeURL(const String& url) const
123 {
124 // Always return a null URL when passed a null string.
125 // FIXME: Should we change the KURL constructor to have this behavior?
126 if (url.isNull())
127 return KURL();
128 // Always use UTF-8 in Workers.
129 return KURL(m_url, url);
130 }
131
userAgent(const KURL &) const132 String WorkerGlobalScope::userAgent(const KURL&) const
133 {
134 return m_userAgent;
135 }
136
disableEval(const String & errorMessage)137 void WorkerGlobalScope::disableEval(const String& errorMessage)
138 {
139 m_script->disableEval(errorMessage);
140 }
141
timerAlignmentInterval() const142 double WorkerGlobalScope::timerAlignmentInterval() const
143 {
144 return DOMTimer::visiblePageAlignmentInterval();
145 }
146
location() const147 WorkerLocation* WorkerGlobalScope::location() const
148 {
149 if (!m_location)
150 m_location = WorkerLocation::create(m_url);
151 return m_location.get();
152 }
153
close()154 void WorkerGlobalScope::close()
155 {
156 if (m_closing)
157 return;
158
159 // Let current script run to completion but prevent future script evaluations.
160 // After m_closing is set, all the tasks in the queue continue to be fetched but only
161 // tasks with isCleanupTask()==true will be executed.
162 m_closing = true;
163 postTask(CloseWorkerGlobalScopeTask::create());
164 }
165
console()166 WorkerConsole* WorkerGlobalScope::console()
167 {
168 if (!m_console)
169 m_console = WorkerConsole::create(this);
170 return m_console.get();
171 }
172
navigator() const173 WorkerNavigator* WorkerGlobalScope::navigator() const
174 {
175 if (!m_navigator)
176 m_navigator = WorkerNavigator::create(m_userAgent);
177 return m_navigator.get();
178 }
179
postTask(PassOwnPtr<ExecutionContextTask> task)180 void WorkerGlobalScope::postTask(PassOwnPtr<ExecutionContextTask> task)
181 {
182 thread()->runLoop().postTask(task);
183 }
184
clearInspector()185 void WorkerGlobalScope::clearInspector()
186 {
187 m_workerInspectorController.clear();
188 }
189
registerTerminationObserver(TerminationObserver * observer)190 void WorkerGlobalScope::registerTerminationObserver(TerminationObserver* observer)
191 {
192 ASSERT(!m_terminationObserver);
193 ASSERT(observer);
194 m_terminationObserver = observer;
195 }
196
unregisterTerminationObserver(TerminationObserver * observer)197 void WorkerGlobalScope::unregisterTerminationObserver(TerminationObserver* observer)
198 {
199 ASSERT(observer);
200 ASSERT(m_terminationObserver == observer);
201 m_terminationObserver = 0;
202 }
203
wasRequestedToTerminate()204 void WorkerGlobalScope::wasRequestedToTerminate()
205 {
206 if (m_terminationObserver)
207 m_terminationObserver->wasRequestedToTerminate();
208 }
209
dispose()210 void WorkerGlobalScope::dispose()
211 {
212 ASSERT(thread()->isCurrentThread());
213
214 m_eventQueue->close();
215 clearScript();
216 clearInspector();
217 setClient(0);
218
219 // We do not clear the thread field of the
220 // WorkerGlobalScope. Other objects keep the worker global scope
221 // alive because they need its thread field to check that work is
222 // being carried out on the right thread. We therefore cannot clear
223 // the thread field before all references to the worker global
224 // scope are gone.
225 }
226
importScripts(const Vector<String> & urls,ExceptionState & exceptionState)227 void WorkerGlobalScope::importScripts(const Vector<String>& urls, ExceptionState& exceptionState)
228 {
229 ASSERT(contentSecurityPolicy());
230 ASSERT(executionContext());
231
232 ExecutionContext& executionContext = *this->executionContext();
233
234 Vector<String>::const_iterator urlsEnd = urls.end();
235 Vector<KURL> completedURLs;
236 for (Vector<String>::const_iterator it = urls.begin(); it != urlsEnd; ++it) {
237 const KURL& url = executionContext.completeURL(*it);
238 if (!url.isValid()) {
239 exceptionState.throwDOMException(SyntaxError, "The URL '" + *it + "' is invalid.");
240 return;
241 }
242 if (!contentSecurityPolicy()->allowScriptFromSource(url)) {
243 exceptionState.throwDOMException(NetworkError, "The script at '" + url.elidedString() + "' failed to load.");
244 return;
245 }
246 completedURLs.append(url);
247 }
248 Vector<KURL>::const_iterator end = completedURLs.end();
249
250 for (Vector<KURL>::const_iterator it = completedURLs.begin(); it != end; ++it) {
251 RefPtr<WorkerScriptLoader> scriptLoader(WorkerScriptLoader::create());
252 scriptLoader->setTargetType(ResourceRequest::TargetIsScript);
253 scriptLoader->loadSynchronously(executionContext, *it, AllowCrossOriginRequests);
254
255 // If the fetching attempt failed, throw a NetworkError exception and abort all these steps.
256 if (scriptLoader->failed()) {
257 exceptionState.throwDOMException(NetworkError, "The script at '" + it->elidedString() + "' failed to load.");
258 return;
259 }
260
261 InspectorInstrumentation::scriptImported(&executionContext, scriptLoader->identifier(), scriptLoader->script());
262
263 RefPtrWillBeRawPtr<ErrorEvent> errorEvent = nullptr;
264 m_script->evaluate(ScriptSourceCode(scriptLoader->script(), scriptLoader->responseURL()), &errorEvent);
265 if (errorEvent) {
266 m_script->rethrowExceptionFromImportedScript(errorEvent.release());
267 return;
268 }
269 }
270 }
271
errorEventTarget()272 EventTarget* WorkerGlobalScope::errorEventTarget()
273 {
274 return this;
275 }
276
logExceptionToConsole(const String & errorMessage,const String & sourceURL,int lineNumber,int columnNumber,PassRefPtrWillBeRawPtr<ScriptCallStack>)277 void WorkerGlobalScope::logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, PassRefPtrWillBeRawPtr<ScriptCallStack>)
278 {
279 thread()->workerReportingProxy().reportException(errorMessage, lineNumber, columnNumber, sourceURL);
280 }
281
reportBlockedScriptExecutionToInspector(const String & directiveText)282 void WorkerGlobalScope::reportBlockedScriptExecutionToInspector(const String& directiveText)
283 {
284 InspectorInstrumentation::scriptExecutionBlockedByCSP(this, directiveText);
285 }
286
addMessage(MessageSource source,MessageLevel level,const String & message,const String & sourceURL,unsigned lineNumber,ScriptState * scriptState)287 void WorkerGlobalScope::addMessage(MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber, ScriptState* scriptState)
288 {
289 if (!isContextThread()) {
290 postTask(AddConsoleMessageTask::create(source, level, message));
291 return;
292 }
293 thread()->workerReportingProxy().reportConsoleMessage(source, level, message, lineNumber, sourceURL);
294 addMessageToWorkerConsole(source, level, message, sourceURL, lineNumber, nullptr, scriptState);
295 }
296
addMessageToWorkerConsole(MessageSource source,MessageLevel level,const String & message,const String & sourceURL,unsigned lineNumber,PassRefPtrWillBeRawPtr<ScriptCallStack> callStack,ScriptState * scriptState)297 void WorkerGlobalScope::addMessageToWorkerConsole(MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber, PassRefPtrWillBeRawPtr<ScriptCallStack> callStack, ScriptState* scriptState)
298 {
299 ASSERT(isContextThread());
300 if (callStack)
301 InspectorInstrumentation::addMessageToConsole(this, source, LogMessageType, level, message, callStack);
302 else
303 InspectorInstrumentation::addMessageToConsole(this, source, LogMessageType, level, message, sourceURL, lineNumber, 0, scriptState);
304 }
305
isContextThread() const306 bool WorkerGlobalScope::isContextThread() const
307 {
308 return thread()->isCurrentThread();
309 }
310
isJSExecutionForbidden() const311 bool WorkerGlobalScope::isJSExecutionForbidden() const
312 {
313 return m_script->isExecutionForbidden();
314 }
315
idleNotification()316 bool WorkerGlobalScope::idleNotification()
317 {
318 return script()->idleNotification();
319 }
320
eventQueue() const321 WorkerEventQueue* WorkerGlobalScope::eventQueue() const
322 {
323 return m_eventQueue.get();
324 }
325
countFeature(UseCounter::Feature) const326 void WorkerGlobalScope::countFeature(UseCounter::Feature) const
327 {
328 // FIXME: How should we count features for shared/service workers?
329 }
330
countDeprecation(UseCounter::Feature) const331 void WorkerGlobalScope::countDeprecation(UseCounter::Feature) const
332 {
333 // FIXME: How should we count features for shared/service workers?
334 }
335
trace(Visitor * visitor)336 void WorkerGlobalScope::trace(Visitor* visitor)
337 {
338 visitor->trace(m_console);
339 visitor->trace(m_location);
340 visitor->trace(m_navigator);
341 visitor->trace(m_eventQueue);
342 visitor->trace(m_workerClients);
343 WillBeHeapSupplementable<WorkerGlobalScope>::trace(visitor);
344 ExecutionContext::trace(visitor);
345 EventTargetWithInlineData::trace(visitor);
346 }
347
348 } // namespace WebCore
349