8e9fb9783d98016c735846f8a0ddb25920a0285f
[WebKit-https.git] / WebCore / workers / WorkerContext.cpp
1 /*
2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3  * Copyright (C) 2009 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 "DOMTimer.h"
36 #include "DOMWindow.h"
37 #include "Event.h"
38 #include "EventException.h"
39 #include "MessageEvent.h"
40 #include "NotImplemented.h"
41 #include "ScriptSourceCode.h"
42 #include "ScriptValue.h"
43 #include "SecurityOrigin.h"
44 #include "WorkerLocation.h"
45 #include "WorkerNavigator.h"
46 #include "WorkerObjectProxy.h"
47 #include "WorkerScriptLoader.h"
48 #include "WorkerThread.h"
49 #include "WorkerThreadableLoader.h"
50 #include "XMLHttpRequestException.h"
51 #include <wtf/RefPtr.h>
52
53 namespace WebCore {
54
55 WorkerContext::WorkerContext(const KURL& url, const String& userAgent, WorkerThread* thread)
56     : m_url(url)
57     , m_userAgent(userAgent)
58     , m_script(new WorkerScriptController(this))
59     , m_thread(thread)
60     , m_closing(false)
61 {
62     setSecurityOrigin(SecurityOrigin::create(url));
63 }
64
65 WorkerContext::~WorkerContext()
66 {
67     ASSERT(currentThread() == m_thread->threadID());
68
69     m_thread->workerObjectProxy().workerContextDestroyed();
70 }
71
72 ScriptExecutionContext* WorkerContext::scriptExecutionContext() const
73 {
74     return const_cast<WorkerContext*>(this);
75 }
76
77 const KURL& WorkerContext::virtualURL() const
78 {
79     return m_url;
80 }
81
82 KURL WorkerContext::virtualCompleteURL(const String& url) const
83 {
84     return completeURL(url);
85 }
86
87 KURL WorkerContext::completeURL(const String& url) const
88 {
89     // Always return a null URL when passed a null string.
90     // FIXME: Should we change the KURL constructor to have this behavior?
91     if (url.isNull())
92         return KURL();
93     // Always use UTF-8 in Workers.
94     return KURL(m_url, url);
95 }
96
97 String WorkerContext::userAgent(const KURL&) const
98 {
99     return m_userAgent;
100 }
101
102 WorkerLocation* WorkerContext::location() const
103 {
104     if (!m_location)
105         m_location = WorkerLocation::create(m_url);
106     return m_location.get();
107 }
108
109 void WorkerContext::close()
110 {
111     if (m_closing)
112         return;
113
114     m_closing = true;
115     m_thread->stop();
116 }
117
118 WorkerNavigator* WorkerContext::navigator() const
119 {
120     if (!m_navigator)
121         m_navigator = WorkerNavigator::create(m_userAgent);
122     return m_navigator.get();
123 }
124
125 bool WorkerContext::hasPendingActivity() const
126 {
127     ActiveDOMObjectsMap& activeObjects = activeDOMObjects();
128     ActiveDOMObjectsMap::const_iterator activeObjectsEnd = activeObjects.end();
129     for (ActiveDOMObjectsMap::const_iterator iter = activeObjects.begin(); iter != activeObjectsEnd; ++iter) {
130         if (iter->first->hasPendingActivity())
131             return true;
132     }
133
134     // Keep the worker active as long as there is a MessagePort with pending activity or that is remotely entangled.
135     HashSet<MessagePort*>::const_iterator messagePortsEnd = messagePorts().end();
136     for (HashSet<MessagePort*>::const_iterator iter = messagePorts().begin(); iter != messagePortsEnd; ++iter) {
137         if ((*iter)->hasPendingActivity() || ((*iter)->isEntangled() && !(*iter)->locallyEntangledPort()))
138             return true;
139     }
140
141     return false;
142 }
143
144 void WorkerContext::reportException(const String& errorMessage, int lineNumber, const String& sourceURL)
145 {
146     m_thread->workerObjectProxy().postExceptionToWorkerObject(errorMessage, lineNumber, sourceURL);
147 }
148
149 void WorkerContext::addMessage(MessageDestination destination, MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL)
150 {
151     m_thread->workerObjectProxy().postConsoleMessageToWorkerObject(destination, source, type, level, message, lineNumber, sourceURL);
152 }
153
154 void WorkerContext::resourceRetrievedByXMLHttpRequest(unsigned long, const ScriptString&)
155 {
156     // FIXME: The implementation is pending the fixes in https://bugs.webkit.org/show_bug.cgi?id=23175
157     notImplemented();
158 }
159
160 void WorkerContext::scriptImported(unsigned long, const String&)
161 {
162     // FIXME: The implementation is pending the fixes in https://bugs.webkit.org/show_bug.cgi?id=23175
163     notImplemented();
164 }
165
166 void WorkerContext::postMessage(const String& message, ExceptionCode& ec)
167 {
168     postMessage(message, 0, ec);
169 }
170
171 void WorkerContext::postMessage(const String& message, MessagePort* port, ExceptionCode& ec)
172 {
173     if (m_closing)
174         return;
175     // Disentangle the port in preparation for sending it to the remote context.
176     OwnPtr<MessagePortChannel> channel = port ? port->disentangle(ec) : 0;
177     if (ec)
178         return;
179     m_thread->workerObjectProxy().postMessageToWorkerObject(message, channel.release());
180 }
181
182 void WorkerContext::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool)
183 {
184     EventListenersMap::iterator iter = m_eventListeners.find(eventType);
185     if (iter == m_eventListeners.end()) {
186         ListenerVector listeners;
187         listeners.append(eventListener);
188         m_eventListeners.add(eventType, listeners);
189     } else {
190         ListenerVector& listeners = iter->second;
191         for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) {
192             if (*listenerIter == eventListener)
193                 return;
194         }
195         
196         listeners.append(eventListener);
197         m_eventListeners.add(eventType, listeners);
198     }    
199 }
200
201 void WorkerContext::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool)
202 {
203     EventListenersMap::iterator iter = m_eventListeners.find(eventType);
204     if (iter == m_eventListeners.end())
205         return;
206     
207     ListenerVector& listeners = iter->second;
208     for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) {
209         if (*listenerIter == eventListener) {
210             listeners.remove(listenerIter - listeners.begin());
211             return;
212         }
213     }
214 }
215
216 bool WorkerContext::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec)
217 {
218     if (!event || event->type().isEmpty()) {
219         ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR;
220         return true;
221     }
222     
223     ListenerVector listenersCopy = m_eventListeners.get(event->type());
224     for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) {
225         event->setTarget(this);
226         event->setCurrentTarget(this);
227         listenerIter->get()->handleEvent(event.get(), false);
228     }
229     
230     return !event->defaultPrevented();
231 }
232
233 void WorkerContext::postTask(PassRefPtr<Task> task)
234 {
235     thread()->runLoop().postTask(task);
236 }
237
238 int WorkerContext::setTimeout(ScheduledAction* action, int timeout)
239 {
240     return DOMTimer::install(scriptExecutionContext(), action, timeout, true);
241 }
242
243 void WorkerContext::clearTimeout(int timeoutId)
244 {
245     DOMTimer::removeById(scriptExecutionContext(), timeoutId);
246 }
247
248 int WorkerContext::setInterval(ScheduledAction* action, int timeout)
249 {
250     return DOMTimer::install(scriptExecutionContext(), action, timeout, false);
251 }
252
253 void WorkerContext::clearInterval(int timeoutId)
254 {
255     DOMTimer::removeById(scriptExecutionContext(), timeoutId);
256 }
257
258 void WorkerContext::dispatchMessage(const String& message, PassRefPtr<MessagePort> port)
259 {
260     // Since close() stops the thread event loop, this should not ever get called while closing.
261     ASSERT(!m_closing);
262     RefPtr<Event> evt = MessageEvent::create(message, "", "", 0, port);
263
264     if (m_onmessageListener.get()) {
265         evt->setTarget(this);
266         evt->setCurrentTarget(this);
267         m_onmessageListener->handleEvent(evt.get(), false);
268     }
269
270     ExceptionCode ec = 0;
271     dispatchEvent(evt.release(), ec);
272     ASSERT(!ec);
273 }
274
275 void WorkerContext::importScripts(const Vector<String>& urls, const String& callerURL, int callerLine, ExceptionCode& ec)
276 {
277     ec = 0;
278     Vector<String>::const_iterator urlsEnd = urls.end();
279     Vector<KURL> completedURLs;
280     for (Vector<String>::const_iterator it = urls.begin(); it != urlsEnd; ++it) {
281         const KURL& url = scriptExecutionContext()->completeURL(*it);
282         if (!url.isValid()) {
283             ec = SYNTAX_ERR;
284             return;
285         }
286         completedURLs.append(url);
287     }
288     String securityOrigin = scriptExecutionContext()->securityOrigin()->toString();
289     Vector<KURL>::const_iterator end = completedURLs.end();
290
291     for (Vector<KURL>::const_iterator it = completedURLs.begin(); it != end; ++it) {
292         WorkerScriptLoader scriptLoader;
293         scriptLoader.loadSynchronously(scriptExecutionContext(), *it, AllowCrossOriginRedirect);
294
295         // If the fetching attempt failed, throw a NETWORK_ERR exception and abort all these steps.
296         if (scriptLoader.failed()) {
297             ec = XMLHttpRequestException::NETWORK_ERR;
298             return;
299         }
300
301         scriptExecutionContext()->scriptImported(scriptLoader.identifier(), scriptLoader.script());
302         scriptExecutionContext()->addMessage(InspectorControllerDestination, JSMessageSource, LogMessageType, LogMessageLevel, "Worker script imported: \"" + *it + "\".", callerLine, callerURL);
303
304         ScriptValue exception;
305         m_script->evaluate(ScriptSourceCode(scriptLoader.script(), *it), &exception);
306         if (!exception.hasNoValue()) {
307             m_script->setException(exception);
308             return;
309         }
310     }
311 }
312
313 } // namespace WebCore
314
315 #endif // ENABLE(WORKERS)