2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "ScriptExecutionContext.h"
30 #include "ActiveDOMObject.h"
36 #include "DatabaseTask.h"
37 #include "DatabaseThread.h"
38 #include "ErrorEvent.h"
39 #include "EventListener.h"
40 #include "EventTarget.h"
41 #include "FileThread.h"
42 #include "MediaStream.h"
43 #include "MediaStreamRegistry.h"
44 #include "MessagePort.h"
45 #include "ScriptCallStack.h"
46 #include "SecurityOrigin.h"
48 #include "ThreadableBlobRegistry.h"
49 #include "WorkerContext.h"
50 #include "WorkerThread.h"
51 #include <wtf/MainThread.h>
52 #include <wtf/PassRefPtr.h>
53 #include <wtf/Vector.h>
56 #include "JSDOMWindow.h"
61 class ProcessMessagesSoonTask : public ScriptExecutionContext::Task {
63 static PassOwnPtr<ProcessMessagesSoonTask> create()
65 return adoptPtr(new ProcessMessagesSoonTask);
68 virtual void performTask(ScriptExecutionContext* context)
70 context->dispatchMessagePortEvents();
74 class ScriptExecutionContext::PendingException {
75 WTF_MAKE_NONCOPYABLE(PendingException);
77 PendingException(const String& errorMessage, int lineNumber, const String& sourceURL, PassRefPtr<ScriptCallStack> callStack)
78 : m_errorMessage(errorMessage)
79 , m_lineNumber(lineNumber)
80 , m_sourceURL(sourceURL)
81 , m_callStack(callStack)
84 String m_errorMessage;
87 RefPtr<ScriptCallStack> m_callStack;
90 ScriptExecutionContext::ScriptExecutionContext()
91 : m_iteratingActiveDOMObjects(false)
92 , m_inDestructor(false)
93 , m_inDispatchErrorEvent(false)
95 , m_hasOpenDatabases(false)
100 ScriptExecutionContext::~ScriptExecutionContext()
102 m_inDestructor = true;
103 for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != m_activeDOMObjects.end(); iter = m_activeDOMObjects.begin()) {
104 ActiveDOMObject* object = iter->first;
105 m_activeDOMObjects.remove(iter);
106 ASSERT(object->scriptExecutionContext() == this);
107 object->contextDestroyed();
110 HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end();
111 for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) {
112 ASSERT((*iter)->scriptExecutionContext() == this);
113 (*iter)->contextDestroyed();
116 if (m_databaseThread) {
117 ASSERT(m_databaseThread->terminationRequested());
118 m_databaseThread = 0;
121 #if ENABLE(BLOB) || ENABLE(FILE_SYSTEM)
123 m_fileThread->stop();
129 HashSet<String>::iterator publicBlobURLsEnd = m_publicBlobURLs.end();
130 for (HashSet<String>::iterator iter = m_publicBlobURLs.begin(); iter != publicBlobURLsEnd; ++iter)
131 ThreadableBlobRegistry::unregisterBlobURL(KURL(ParsedURLString, *iter));
133 HashSet<DOMURL*>::iterator domUrlsEnd = m_domUrls.end();
134 for (HashSet<DOMURL*>::iterator iter = m_domUrls.begin(); iter != domUrlsEnd; ++iter) {
135 ASSERT((*iter)->scriptExecutionContext() == this);
136 (*iter)->contextDestroyed();
140 #if ENABLE(MEDIA_STREAM)
141 HashSet<String>::iterator publicStreamURLsEnd = m_publicStreamURLs.end();
142 for (HashSet<String>::iterator iter = m_publicStreamURLs.begin(); iter != publicStreamURLsEnd; ++iter)
143 MediaStreamRegistry::registry().unregisterMediaStreamURL(KURL(ParsedURLString, *iter));
149 DatabaseThread* ScriptExecutionContext::databaseThread()
151 if (!m_databaseThread && !m_hasOpenDatabases) {
152 // Create the database thread on first request - but not if at least one database was already opened,
153 // because in that case we already had a database thread and terminated it and should not create another.
154 m_databaseThread = DatabaseThread::create();
155 if (!m_databaseThread->start())
156 m_databaseThread = 0;
159 return m_databaseThread.get();
162 void ScriptExecutionContext::stopDatabases(DatabaseTaskSynchronizer* cleanupSync)
164 ASSERT(isContextThread());
165 if (m_databaseThread)
166 m_databaseThread->requestTermination(cleanupSync);
167 else if (cleanupSync)
168 cleanupSync->taskCompleted();
173 void ScriptExecutionContext::processMessagePortMessagesSoon()
175 postTask(ProcessMessagesSoonTask::create());
178 void ScriptExecutionContext::dispatchMessagePortEvents()
180 RefPtr<ScriptExecutionContext> protect(this);
182 // Make a frozen copy.
183 Vector<MessagePort*> ports;
184 copyToVector(m_messagePorts, ports);
186 unsigned portCount = ports.size();
187 for (unsigned i = 0; i < portCount; ++i) {
188 MessagePort* port = ports[i];
189 // The port may be destroyed, and another one created at the same address, but this is safe, as the worst that can happen
190 // as a result is that dispatchMessages() will be called needlessly.
191 if (m_messagePorts.contains(port) && port->started())
192 port->dispatchMessages();
196 void ScriptExecutionContext::createdMessagePort(MessagePort* port)
200 ASSERT((isDocument() && isMainThread())
201 || (isWorkerContext() && currentThread() == static_cast<WorkerContext*>(this)->thread()->threadID()));
204 m_messagePorts.add(port);
207 void ScriptExecutionContext::destroyedMessagePort(MessagePort* port)
211 ASSERT((isDocument() && isMainThread())
212 || (isWorkerContext() && currentThread() == static_cast<WorkerContext*>(this)->thread()->threadID()));
215 m_messagePorts.remove(port);
219 void ScriptExecutionContext::createdDomUrl(DOMURL* url)
225 void ScriptExecutionContext::destroyedDomUrl(DOMURL* url)
228 m_domUrls.remove(url);
232 bool ScriptExecutionContext::canSuspendActiveDOMObjects()
234 // No protection against m_activeDOMObjects changing during iteration: canSuspend() shouldn't execute arbitrary JS.
235 m_iteratingActiveDOMObjects = true;
236 HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end();
237 for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
238 ASSERT(iter->first->scriptExecutionContext() == this);
239 if (!iter->first->canSuspend()) {
240 m_iteratingActiveDOMObjects = false;
244 m_iteratingActiveDOMObjects = false;
248 void ScriptExecutionContext::suspendActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why)
250 // No protection against m_activeDOMObjects changing during iteration: suspend() shouldn't execute arbitrary JS.
251 m_iteratingActiveDOMObjects = true;
252 HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end();
253 for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
254 ASSERT(iter->first->scriptExecutionContext() == this);
255 iter->first->suspend(why);
257 m_iteratingActiveDOMObjects = false;
260 void ScriptExecutionContext::resumeActiveDOMObjects()
262 // No protection against m_activeDOMObjects changing during iteration: resume() shouldn't execute arbitrary JS.
263 m_iteratingActiveDOMObjects = true;
264 HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end();
265 for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
266 ASSERT(iter->first->scriptExecutionContext() == this);
267 iter->first->resume();
269 m_iteratingActiveDOMObjects = false;
272 void ScriptExecutionContext::stopActiveDOMObjects()
274 // No protection against m_activeDOMObjects changing during iteration: stop() shouldn't execute arbitrary JS.
275 m_iteratingActiveDOMObjects = true;
276 HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end();
277 for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
278 ASSERT(iter->first->scriptExecutionContext() == this);
281 m_iteratingActiveDOMObjects = false;
283 // Also close MessagePorts. If they were ActiveDOMObjects (they could be) then they could be stopped instead.
287 void ScriptExecutionContext::createdActiveDOMObject(ActiveDOMObject* object, void* upcastPointer)
290 ASSERT(upcastPointer);
291 ASSERT(!m_inDestructor);
292 if (m_iteratingActiveDOMObjects)
294 m_activeDOMObjects.add(object, upcastPointer);
297 void ScriptExecutionContext::destroyedActiveDOMObject(ActiveDOMObject* object)
300 if (m_iteratingActiveDOMObjects)
302 m_activeDOMObjects.remove(object);
305 void ScriptExecutionContext::closeMessagePorts() {
306 HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end();
307 for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) {
308 ASSERT((*iter)->scriptExecutionContext() == this);
313 void ScriptExecutionContext::setSecurityOrigin(PassRefPtr<SecurityOrigin> securityOrigin)
315 m_securityOrigin = securityOrigin;
318 bool ScriptExecutionContext::sanitizeScriptError(String& errorMessage, int& lineNumber, String& sourceURL)
320 KURL targetURL = completeURL(sourceURL);
321 if (securityOrigin()->canRequest(targetURL))
323 errorMessage = "Script error.";
324 sourceURL = String();
329 void ScriptExecutionContext::reportException(const String& errorMessage, int lineNumber, const String& sourceURL, PassRefPtr<ScriptCallStack> callStack)
331 if (m_inDispatchErrorEvent) {
332 if (!m_pendingExceptions)
333 m_pendingExceptions = adoptPtr(new Vector<OwnPtr<PendingException> >());
334 m_pendingExceptions->append(adoptPtr(new PendingException(errorMessage, lineNumber, sourceURL, callStack)));
338 // First report the original exception and only then all the nested ones.
339 if (!dispatchErrorEvent(errorMessage, lineNumber, sourceURL))
340 logExceptionToConsole(errorMessage, lineNumber, sourceURL, callStack);
342 if (!m_pendingExceptions)
345 for (size_t i = 0; i < m_pendingExceptions->size(); i++) {
346 PendingException* e = m_pendingExceptions->at(i).get();
347 logExceptionToConsole(e->m_errorMessage, e->m_lineNumber, e->m_sourceURL, e->m_callStack);
349 m_pendingExceptions.clear();
352 bool ScriptExecutionContext::dispatchErrorEvent(const String& errorMessage, int lineNumber, const String& sourceURL)
354 EventTarget* target = errorEventTarget();
358 String message = errorMessage;
359 int line = lineNumber;
360 String sourceName = sourceURL;
361 sanitizeScriptError(message, line, sourceName);
363 ASSERT(!m_inDispatchErrorEvent);
364 m_inDispatchErrorEvent = true;
365 RefPtr<ErrorEvent> errorEvent = ErrorEvent::create(message, sourceName, line);
366 target->dispatchEvent(errorEvent);
367 m_inDispatchErrorEvent = false;
368 return errorEvent->defaultPrevented();
371 void ScriptExecutionContext::addTimeout(int timeoutId, DOMTimer* timer)
373 ASSERT(!m_timeouts.contains(timeoutId));
374 m_timeouts.set(timeoutId, timer);
377 void ScriptExecutionContext::removeTimeout(int timeoutId)
379 m_timeouts.remove(timeoutId);
382 DOMTimer* ScriptExecutionContext::findTimeout(int timeoutId)
384 return m_timeouts.get(timeoutId);
389 #if ENABLE(MEDIA_STREAM)
390 KURL ScriptExecutionContext::createPublicBlobURL(MediaStream* stream)
395 KURL publicURL = BlobURL::createPublicURL(securityOrigin());
397 // Since WebWorkers cannot obtain Stream objects, we should be on the main thread.
398 ASSERT(isMainThread());
399 MediaStreamRegistry::registry().registerMediaStreamURL(publicURL, stream);
400 m_publicStreamURLs.add(publicURL.string());
403 #endif // ENABLE(MEDIA_STREAM)
405 KURL ScriptExecutionContext::createPublicBlobURL(Blob* blob)
409 KURL publicURL = BlobURL::createPublicURL(securityOrigin());
410 if (publicURL.isEmpty())
412 ThreadableBlobRegistry::registerBlobURL(publicURL, blob->url());
413 m_publicBlobURLs.add(publicURL.string());
417 void ScriptExecutionContext::revokePublicBlobURL(const KURL& url)
419 if (m_publicBlobURLs.contains(url.string())) {
420 ThreadableBlobRegistry::unregisterBlobURL(url);
421 m_publicBlobURLs.remove(url.string());
423 #if ENABLE(MEDIA_STREAM)
424 if (m_publicStreamURLs.contains(url.string())) {
425 // FIXME: make sure of this assertion below. Raise a spec question if required.
426 // Since WebWorkers cannot obtain Stream objects, we should be on the main thread.
427 ASSERT(isMainThread());
428 MediaStreamRegistry::registry().unregisterMediaStreamURL(url);
429 m_publicStreamURLs.remove(url.string());
431 #endif // ENABLE(MEDIA_STREAM)
433 #endif // ENABLE(BLOB)
435 #if ENABLE(BLOB) || ENABLE(FILE_SYSTEM)
436 FileThread* ScriptExecutionContext::fileThread()
439 m_fileThread = FileThread::create();
440 if (!m_fileThread->start())
443 return m_fileThread.get();
447 void ScriptExecutionContext::adjustMinimumTimerInterval(double oldMinimumTimerInterval)
449 if (minimumTimerInterval() != oldMinimumTimerInterval) {
450 for (TimeoutMap::iterator iter = m_timeouts.begin(); iter != m_timeouts.end(); ++iter) {
451 DOMTimer* timer = iter->second;
452 timer->adjustMinimumTimerInterval(oldMinimumTimerInterval);
457 double ScriptExecutionContext::minimumTimerInterval() const
459 // The default implementation returns the DOMTimer's default
460 // minimum timer interval. FIXME: to make it work with dedicated
461 // workers, we will have to override it in the appropriate
462 // subclass, and provide a way to enumerate a Document's dedicated
463 // workers so we can update them all.
464 return Settings::defaultMinDOMTimerInterval();
467 ScriptExecutionContext::Task::~Task()
472 JSC::JSGlobalData* ScriptExecutionContext::globalData()
475 return JSDOMWindow::commonJSGlobalData();
478 if (isWorkerContext())
479 return static_cast<WorkerContext*>(this)->script()->globalData();
482 ASSERT_NOT_REACHED();
487 } // namespace WebCore