57b2189a74a00f3c7986d89d3ea80551155808ea
[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     return false;
134 }
135
136 void WorkerContext::reportException(const String& errorMessage, int lineNumber, const String& sourceURL)
137 {
138     m_thread->workerObjectProxy().postExceptionToWorkerObject(errorMessage, lineNumber, sourceURL);
139 }
140
141 void WorkerContext::addMessage(MessageDestination destination, MessageSource source, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL)
142 {
143     m_thread->workerObjectProxy().postConsoleMessageToWorkerObject(destination, source, level, message, lineNumber, sourceURL);
144 }
145
146 void WorkerContext::resourceRetrievedByXMLHttpRequest(unsigned long, const ScriptString&)
147 {
148     // FIXME: The implementation is pending the fixes in https://bugs.webkit.org/show_bug.cgi?id=23175
149     notImplemented();
150 }
151
152 void WorkerContext::scriptImported(unsigned long, const String&)
153 {
154     // FIXME: The implementation is pending the fixes in https://bugs.webkit.org/show_bug.cgi?id=23175
155     notImplemented();
156 }
157
158 void WorkerContext::postMessage(const String& message)
159 {
160     if (m_closing)
161         return;
162
163     m_thread->workerObjectProxy().postMessageToWorkerObject(message);
164 }
165
166 void WorkerContext::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool)
167 {
168     EventListenersMap::iterator iter = m_eventListeners.find(eventType);
169     if (iter == m_eventListeners.end()) {
170         ListenerVector listeners;
171         listeners.append(eventListener);
172         m_eventListeners.add(eventType, listeners);
173     } else {
174         ListenerVector& listeners = iter->second;
175         for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) {
176             if (*listenerIter == eventListener)
177                 return;
178         }
179         
180         listeners.append(eventListener);
181         m_eventListeners.add(eventType, listeners);
182     }    
183 }
184
185 void WorkerContext::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool)
186 {
187     EventListenersMap::iterator iter = m_eventListeners.find(eventType);
188     if (iter == m_eventListeners.end())
189         return;
190     
191     ListenerVector& listeners = iter->second;
192     for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) {
193         if (*listenerIter == eventListener) {
194             listeners.remove(listenerIter - listeners.begin());
195             return;
196         }
197     }
198 }
199
200 bool WorkerContext::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec)
201 {
202     if (!event || event->type().isEmpty()) {
203         ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR;
204         return true;
205     }
206     
207     ListenerVector listenersCopy = m_eventListeners.get(event->type());
208     for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) {
209         event->setTarget(this);
210         event->setCurrentTarget(this);
211         listenerIter->get()->handleEvent(event.get(), false);
212     }
213     
214     return !event->defaultPrevented();
215 }
216
217 void WorkerContext::postTask(PassRefPtr<Task> task)
218 {
219     thread()->runLoop().postTask(task);
220 }
221
222 int WorkerContext::setTimeout(ScheduledAction* action, int timeout)
223 {
224     return DOMTimer::install(scriptExecutionContext(), action, timeout, true);
225 }
226
227 void WorkerContext::clearTimeout(int timeoutId)
228 {
229     DOMTimer::removeById(scriptExecutionContext(), timeoutId);
230 }
231
232 int WorkerContext::setInterval(ScheduledAction* action, int timeout)
233 {
234     return DOMTimer::install(scriptExecutionContext(), action, timeout, false);
235 }
236
237 void WorkerContext::clearInterval(int timeoutId)
238 {
239     DOMTimer::removeById(scriptExecutionContext(), timeoutId);
240 }
241
242 void WorkerContext::dispatchMessage(const String& message)
243 {
244     // Since close() stops the thread event loop, this should not ever get called while closing.
245     ASSERT(!m_closing);
246     RefPtr<Event> evt = MessageEvent::create(message, "", "", 0, 0);
247
248     if (m_onmessageListener.get()) {
249         evt->setTarget(this);
250         evt->setCurrentTarget(this);
251         m_onmessageListener->handleEvent(evt.get(), false);
252     }
253
254     ExceptionCode ec = 0;
255     dispatchEvent(evt.release(), ec);
256     ASSERT(!ec);
257 }
258
259 void WorkerContext::importScripts(const Vector<String>& urls, const String& callerURL, int callerLine, ExceptionCode& ec)
260 {
261     ec = 0;
262     Vector<String>::const_iterator urlsEnd = urls.end();
263     Vector<KURL> completedURLs;
264     for (Vector<String>::const_iterator it = urls.begin(); it != urlsEnd; ++it) {
265         const KURL& url = scriptExecutionContext()->completeURL(*it);
266         if (!url.isValid()) {
267             ec = SYNTAX_ERR;
268             return;
269         }
270         completedURLs.append(url);
271     }
272     String securityOrigin = scriptExecutionContext()->securityOrigin()->toString();
273     Vector<KURL>::const_iterator end = completedURLs.end();
274
275     for (Vector<KURL>::const_iterator it = completedURLs.begin(); it != end; ++it) {
276         WorkerScriptLoader scriptLoader;
277         scriptLoader.loadSynchronously(scriptExecutionContext(), *it, AllowDifferentRedirectOrigin);
278
279         // If the fetching attempt failed, throw a NETWORK_ERR exception and abort all these steps.
280         if (scriptLoader.failed()) {
281             ec = XMLHttpRequestException::NETWORK_ERR;
282             return;
283         }
284
285         scriptExecutionContext()->scriptImported(scriptLoader.identifier(), scriptLoader.script());
286         scriptExecutionContext()->addMessage(InspectorControllerDestination, JSMessageSource, LogMessageLevel, "Worker script imported: \"" + *it + "\".", callerLine, callerURL);
287
288         ScriptValue exception;
289         m_script->evaluate(ScriptSourceCode(scriptLoader.script(), *it), &exception);
290         if (!exception.hasNoValue()) {
291             m_script->setException(exception);
292             return;
293         }
294     }
295 }
296
297 } // namespace WebCore
298
299 #endif // ENABLE(WORKERS)