WebCore: Adding support for the optional creation callback that could be
[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 "Database.h"
36 #include "DOMTimer.h"
37 #include "DOMWindow.h"
38 #include "Event.h"
39 #include "EventException.h"
40 #include "MessagePort.h"
41 #include "NotImplemented.h"
42 #include "ScriptSourceCode.h"
43 #include "ScriptValue.h"
44 #include "SecurityOrigin.h"
45 #include "WorkerLocation.h"
46 #include "WorkerNavigator.h"
47 #include "WorkerObjectProxy.h"
48 #include "WorkerScriptLoader.h"
49 #include "WorkerThread.h"
50 #include "WorkerThreadableLoader.h"
51 #include "XMLHttpRequestException.h"
52 #include <wtf/RefPtr.h>
53 #include <wtf/UnusedParam.h>
54
55 #if ENABLE(NOTIFICATIONS)
56 #include "NotificationCenter.h"
57 #endif
58
59 namespace WebCore {
60
61 WorkerContext::WorkerContext(const KURL& url, const String& userAgent, WorkerThread* thread)
62     : m_url(url)
63     , m_userAgent(userAgent)
64     , m_script(new WorkerScriptController(this))
65     , m_thread(thread)
66     , m_closing(false)
67 {
68     setSecurityOrigin(SecurityOrigin::create(url));
69 }
70
71 WorkerContext::~WorkerContext()
72 {
73     ASSERT(currentThread() == thread()->threadID());
74 #if ENABLE(NOTIFICATIONS)
75     m_notifications.clear();
76 #endif
77     // Notify proxy that we are going away. This can free the WorkerThread object, so do not access it after this.
78     thread()->workerReportingProxy().workerContextDestroyed();
79 }
80
81 ScriptExecutionContext* WorkerContext::scriptExecutionContext() const
82 {
83     return const_cast<WorkerContext*>(this);
84 }
85
86 const KURL& WorkerContext::virtualURL() const
87 {
88     return m_url;
89 }
90
91 KURL WorkerContext::virtualCompleteURL(const String& url) const
92 {
93     return completeURL(url);
94 }
95
96 KURL WorkerContext::completeURL(const String& url) const
97 {
98     // Always return a null URL when passed a null string.
99     // FIXME: Should we change the KURL constructor to have this behavior?
100     if (url.isNull())
101         return KURL();
102     // Always use UTF-8 in Workers.
103     return KURL(m_url, url);
104 }
105
106 String WorkerContext::userAgent(const KURL&) const
107 {
108     return m_userAgent;
109 }
110
111 WorkerLocation* WorkerContext::location() const
112 {
113     if (!m_location)
114         m_location = WorkerLocation::create(m_url);
115     return m_location.get();
116 }
117
118 void WorkerContext::close()
119 {
120     if (m_closing)
121         return;
122
123     m_closing = true;
124     // Notify parent that this context is closed. Parent is responsible for calling WorkerThread::stop().
125     thread()->workerReportingProxy().workerContextClosed();
126 }
127
128 WorkerNavigator* WorkerContext::navigator() const
129 {
130     if (!m_navigator)
131         m_navigator = WorkerNavigator::create(m_userAgent);
132     return m_navigator.get();
133 }
134
135 bool WorkerContext::hasPendingActivity() const
136 {
137     ActiveDOMObjectsMap& activeObjects = activeDOMObjects();
138     ActiveDOMObjectsMap::const_iterator activeObjectsEnd = activeObjects.end();
139     for (ActiveDOMObjectsMap::const_iterator iter = activeObjects.begin(); iter != activeObjectsEnd; ++iter) {
140         if (iter->first->hasPendingActivity())
141             return true;
142     }
143
144     // Keep the worker active as long as there is a MessagePort with pending activity or that is remotely entangled.
145     HashSet<MessagePort*>::const_iterator messagePortsEnd = messagePorts().end();
146     for (HashSet<MessagePort*>::const_iterator iter = messagePorts().begin(); iter != messagePortsEnd; ++iter) {
147         if ((*iter)->hasPendingActivity() || ((*iter)->isEntangled() && !(*iter)->locallyEntangledPort()))
148             return true;
149     }
150
151     return false;
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::postTask(PassOwnPtr<Task> task)
167 {
168     thread()->runLoop().postTask(task);
169 }
170
171 int WorkerContext::setTimeout(ScheduledAction* action, int timeout)
172 {
173     return DOMTimer::install(scriptExecutionContext(), action, timeout, true);
174 }
175
176 void WorkerContext::clearTimeout(int timeoutId)
177 {
178     DOMTimer::removeById(scriptExecutionContext(), timeoutId);
179 }
180
181 int WorkerContext::setInterval(ScheduledAction* action, int timeout)
182 {
183     return DOMTimer::install(scriptExecutionContext(), action, timeout, false);
184 }
185
186 void WorkerContext::clearInterval(int timeoutId)
187 {
188     DOMTimer::removeById(scriptExecutionContext(), timeoutId);
189 }
190
191 void WorkerContext::importScripts(const Vector<String>& urls, const String& callerURL, int callerLine, ExceptionCode& ec)
192 {
193 #if !ENABLE(INSPECTOR)
194     UNUSED_PARAM(callerURL);
195     UNUSED_PARAM(callerLine);
196 #endif
197     ec = 0;
198     Vector<String>::const_iterator urlsEnd = urls.end();
199     Vector<KURL> completedURLs;
200     for (Vector<String>::const_iterator it = urls.begin(); it != urlsEnd; ++it) {
201         const KURL& url = scriptExecutionContext()->completeURL(*it);
202         if (!url.isValid()) {
203             ec = SYNTAX_ERR;
204             return;
205         }
206         completedURLs.append(url);
207     }
208     Vector<KURL>::const_iterator end = completedURLs.end();
209
210     for (Vector<KURL>::const_iterator it = completedURLs.begin(); it != end; ++it) {
211         WorkerScriptLoader scriptLoader;
212         scriptLoader.loadSynchronously(scriptExecutionContext(), *it, AllowCrossOriginRequests);
213
214         // If the fetching attempt failed, throw a NETWORK_ERR exception and abort all these steps.
215         if (scriptLoader.failed()) {
216             ec = XMLHttpRequestException::NETWORK_ERR;
217             return;
218         }
219
220         scriptExecutionContext()->scriptImported(scriptLoader.identifier(), scriptLoader.script());
221 #if ENABLE(INSPECTOR)
222         scriptExecutionContext()->addMessage(InspectorControllerDestination, JSMessageSource, LogMessageType, LogMessageLevel, "Worker script imported: \"" + *it + "\".", callerLine, callerURL);
223 #endif
224
225         ScriptValue exception;
226         m_script->evaluate(ScriptSourceCode(scriptLoader.script(), *it), &exception);
227         if (!exception.hasNoValue()) {
228             m_script->setException(exception);
229             return;
230         }
231     }
232 }
233
234 void WorkerContext::reportException(const String& errorMessage, int lineNumber, const String& sourceURL)
235 {
236     bool errorHandled = false;
237     if (onerror())
238         errorHandled = onerror()->reportError(this, errorMessage, sourceURL, lineNumber);
239
240     if (!errorHandled)
241         thread()->workerReportingProxy().postExceptionToWorkerObject(errorMessage, lineNumber, sourceURL);
242 }
243
244 void WorkerContext::addMessage(MessageDestination destination, MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL)
245 {
246     thread()->workerReportingProxy().postConsoleMessageToWorkerObject(destination, source, type, level, message, lineNumber, sourceURL);
247 }
248
249 #if ENABLE(NOTIFICATIONS)
250 NotificationCenter* WorkerContext::webkitNotifications() const
251 {
252     if (!m_notifications)
253         m_notifications = NotificationCenter::create(scriptExecutionContext(), m_thread->getNotificationPresenter());
254     return m_notifications.get();
255 }
256 #endif
257
258 #if ENABLE(DATABASE)
259 PassRefPtr<Database> WorkerContext::openDatabase(const String& name, const String& version, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode& ec)
260 {
261     if (!securityOrigin()->canAccessDatabase()) {
262         ec = SECURITY_ERR;
263         return 0;
264     }
265
266     ASSERT(Database::isAvailable());
267     if (!Database::isAvailable())
268         return 0;
269
270     return Database::openDatabase(this, name, version, displayName, estimatedSize, creationCallback, ec);
271 }
272 #endif
273
274 bool WorkerContext::isContextThread() const
275 {
276     return currentThread() == thread()->threadID();
277 }
278
279 EventTargetData* WorkerContext::eventTargetData()
280 {
281     return &m_eventTargetData;
282 }
283
284 EventTargetData* WorkerContext::ensureEventTargetData()
285 {
286     return &m_eventTargetData;
287 }
288
289 } // namespace WebCore
290
291 #endif // ENABLE(WORKERS)