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 "MessagePort.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
WorkerContext(const KURL & url,const String & userAgent,WorkerThread * thread)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
~WorkerContext()65 WorkerContext::~WorkerContext()
66 {
67 }
68
scriptExecutionContext() const69 ScriptExecutionContext* WorkerContext::scriptExecutionContext() const
70 {
71 return const_cast<WorkerContext*>(this);
72 }
73
virtualURL() const74 const KURL& WorkerContext::virtualURL() const
75 {
76 return m_url;
77 }
78
virtualCompleteURL(const String & url) const79 KURL WorkerContext::virtualCompleteURL(const String& url) const
80 {
81 return completeURL(url);
82 }
83
completeURL(const String & url) const84 KURL WorkerContext::completeURL(const String& url) const
85 {
86 // Always return a null URL when passed a null string.
87 // FIXME: Should we change the KURL constructor to have this behavior?
88 if (url.isNull())
89 return KURL();
90 // Always use UTF-8 in Workers.
91 return KURL(m_url, url);
92 }
93
userAgent(const KURL &) const94 String WorkerContext::userAgent(const KURL&) const
95 {
96 return m_userAgent;
97 }
98
location() const99 WorkerLocation* WorkerContext::location() const
100 {
101 if (!m_location)
102 m_location = WorkerLocation::create(m_url);
103 return m_location.get();
104 }
105
close()106 void WorkerContext::close()
107 {
108 if (m_closing)
109 return;
110
111 m_closing = true;
112 m_thread->stop();
113 }
114
navigator() const115 WorkerNavigator* WorkerContext::navigator() const
116 {
117 if (!m_navigator)
118 m_navigator = WorkerNavigator::create(m_userAgent);
119 return m_navigator.get();
120 }
121
hasPendingActivity() const122 bool WorkerContext::hasPendingActivity() const
123 {
124 ActiveDOMObjectsMap& activeObjects = activeDOMObjects();
125 ActiveDOMObjectsMap::const_iterator activeObjectsEnd = activeObjects.end();
126 for (ActiveDOMObjectsMap::const_iterator iter = activeObjects.begin(); iter != activeObjectsEnd; ++iter) {
127 if (iter->first->hasPendingActivity())
128 return true;
129 }
130
131 // Keep the worker active as long as there is a MessagePort with pending activity or that is remotely entangled.
132 HashSet<MessagePort*>::const_iterator messagePortsEnd = messagePorts().end();
133 for (HashSet<MessagePort*>::const_iterator iter = messagePorts().begin(); iter != messagePortsEnd; ++iter) {
134 if ((*iter)->hasPendingActivity() || ((*iter)->isEntangled() && !(*iter)->locallyEntangledPort()))
135 return true;
136 }
137
138 return false;
139 }
140
resourceRetrievedByXMLHttpRequest(unsigned long,const ScriptString &)141 void WorkerContext::resourceRetrievedByXMLHttpRequest(unsigned long, const ScriptString&)
142 {
143 // FIXME: The implementation is pending the fixes in https://bugs.webkit.org/show_bug.cgi?id=23175
144 notImplemented();
145 }
146
scriptImported(unsigned long,const String &)147 void WorkerContext::scriptImported(unsigned long, const String&)
148 {
149 // FIXME: The implementation is pending the fixes in https://bugs.webkit.org/show_bug.cgi?id=23175
150 notImplemented();
151 }
152
addEventListener(const AtomicString & eventType,PassRefPtr<EventListener> eventListener,bool)153 void WorkerContext::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool)
154 {
155 EventListenersMap::iterator iter = m_eventListeners.find(eventType);
156 if (iter == m_eventListeners.end()) {
157 ListenerVector listeners;
158 listeners.append(eventListener);
159 m_eventListeners.add(eventType, listeners);
160 } else {
161 ListenerVector& listeners = iter->second;
162 for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) {
163 if (*listenerIter == eventListener)
164 return;
165 }
166
167 listeners.append(eventListener);
168 m_eventListeners.add(eventType, listeners);
169 }
170 }
171
removeEventListener(const AtomicString & eventType,EventListener * eventListener,bool)172 void WorkerContext::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool)
173 {
174 EventListenersMap::iterator iter = m_eventListeners.find(eventType);
175 if (iter == m_eventListeners.end())
176 return;
177
178 ListenerVector& listeners = iter->second;
179 for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) {
180 if (*listenerIter == eventListener) {
181 listeners.remove(listenerIter - listeners.begin());
182 return;
183 }
184 }
185 }
186
dispatchEvent(PassRefPtr<Event> event,ExceptionCode & ec)187 bool WorkerContext::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec)
188 {
189 if (!event || event->type().isEmpty()) {
190 ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR;
191 return true;
192 }
193
194 ListenerVector listenersCopy = m_eventListeners.get(event->type());
195 for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) {
196 event->setTarget(this);
197 event->setCurrentTarget(this);
198 listenerIter->get()->handleEvent(event.get(), false);
199 }
200
201 return !event->defaultPrevented();
202 }
203
postTask(PassRefPtr<Task> task)204 void WorkerContext::postTask(PassRefPtr<Task> task)
205 {
206 thread()->runLoop().postTask(task);
207 }
208
setTimeout(ScheduledAction * action,int timeout)209 int WorkerContext::setTimeout(ScheduledAction* action, int timeout)
210 {
211 return DOMTimer::install(scriptExecutionContext(), action, timeout, true);
212 }
213
clearTimeout(int timeoutId)214 void WorkerContext::clearTimeout(int timeoutId)
215 {
216 DOMTimer::removeById(scriptExecutionContext(), timeoutId);
217 }
218
setInterval(ScheduledAction * action,int timeout)219 int WorkerContext::setInterval(ScheduledAction* action, int timeout)
220 {
221 return DOMTimer::install(scriptExecutionContext(), action, timeout, false);
222 }
223
clearInterval(int timeoutId)224 void WorkerContext::clearInterval(int timeoutId)
225 {
226 DOMTimer::removeById(scriptExecutionContext(), timeoutId);
227 }
228
importScripts(const Vector<String> & urls,const String & callerURL,int callerLine,ExceptionCode & ec)229 void WorkerContext::importScripts(const Vector<String>& urls, const String& callerURL, int callerLine, 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 String securityOrigin = scriptExecutionContext()->securityOrigin()->toString();
243 Vector<KURL>::const_iterator end = completedURLs.end();
244
245 for (Vector<KURL>::const_iterator it = completedURLs.begin(); it != end; ++it) {
246 WorkerScriptLoader scriptLoader;
247 scriptLoader.loadSynchronously(scriptExecutionContext(), *it, AllowCrossOriginRedirect);
248
249 // If the fetching attempt failed, throw a NETWORK_ERR exception and abort all these steps.
250 if (scriptLoader.failed()) {
251 ec = XMLHttpRequestException::NETWORK_ERR;
252 return;
253 }
254
255 scriptExecutionContext()->scriptImported(scriptLoader.identifier(), scriptLoader.script());
256 scriptExecutionContext()->addMessage(InspectorControllerDestination, JSMessageSource, LogMessageType, LogMessageLevel, "Worker script imported: \"" + *it + "\".", callerLine, callerURL);
257
258 ScriptValue exception;
259 m_script->evaluate(ScriptSourceCode(scriptLoader.script(), *it), &exception);
260 if (!exception.hasNoValue()) {
261 m_script->setException(exception);
262 return;
263 }
264 }
265 }
266
reportException(const String & errorMessage,int lineNumber,const String & sourceURL)267 void WorkerContext::reportException(const String& errorMessage, int lineNumber, const String& sourceURL)
268 {
269 bool errorHandled = false;
270 if (onerror())
271 errorHandled = onerror()->reportError(errorMessage, sourceURL, lineNumber);
272
273 if (!errorHandled)
274 forwardException(errorMessage, lineNumber, sourceURL);
275 }
276
277 } // namespace WebCore
278
279 #endif // ENABLE(WORKERS)
280