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