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