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"
31 #include "ContentSecurityPolicy.h"
34 #include "DatabaseTask.h"
35 #include "DatabaseThread.h"
36 #include "ErrorEvent.h"
37 #include "EventListener.h"
38 #include "EventTarget.h"
39 #include "FileThread.h"
40 #include "MessagePort.h"
41 #include "ScriptCallStack.h"
42 #include "SecurityOrigin.h"
44 #include "WorkerContext.h"
45 #include "WorkerThread.h"
46 #include <wtf/MainThread.h>
47 #include <wtf/PassRefPtr.h>
48 #include <wtf/Vector.h>
51 // FIXME: This is a layering violation.
52 #include "JSDOMWindow.h"
57 class ProcessMessagesSoonTask : public ScriptExecutionContext::Task {
59 static PassOwnPtr<ProcessMessagesSoonTask> create()
61 return adoptPtr(new ProcessMessagesSoonTask);
64 virtual void performTask(ScriptExecutionContext* context)
66 context->dispatchMessagePortEvents();
70 class ScriptExecutionContext::PendingException {
71 WTF_MAKE_NONCOPYABLE(PendingException);
73 PendingException(const String& errorMessage, int lineNumber, const String& sourceURL, PassRefPtr<ScriptCallStack> callStack)
74 : m_errorMessage(errorMessage)
75 , m_lineNumber(lineNumber)
76 , m_sourceURL(sourceURL)
77 , m_callStack(callStack)
80 String m_errorMessage;
83 RefPtr<ScriptCallStack> m_callStack;
86 void ScriptExecutionContext::AddConsoleMessageTask::performTask(ScriptExecutionContext* context)
88 // FIXME: We should call addConsoleMessage instead, but that uses 1 as the fifth parameter instead of 0.
89 context->addMessage(m_source, m_type, m_level, m_message, 0, String(), 0);
92 ScriptExecutionContext::ScriptExecutionContext()
93 : m_iteratingActiveDOMObjects(false)
94 , m_inDestructor(false)
95 , m_inDispatchErrorEvent(false)
96 #if ENABLE(SQL_DATABASE)
97 , m_hasOpenDatabases(false)
102 ScriptExecutionContext::~ScriptExecutionContext()
104 m_inDestructor = true;
106 for (HashSet<ContextDestructionObserver*>::iterator iter = m_destructionObservers.begin(); iter != m_destructionObservers.end(); iter = m_destructionObservers.begin()) {
107 ContextDestructionObserver* observer = *iter;
108 m_destructionObservers.remove(observer);
109 ASSERT(observer->scriptExecutionContext() == this);
110 observer->contextDestroyed();
113 HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end();
114 for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) {
115 ASSERT((*iter)->scriptExecutionContext() == this);
116 (*iter)->contextDestroyed();
118 #if ENABLE(SQL_DATABASE)
119 if (m_databaseThread) {
120 ASSERT(m_databaseThread->terminationRequested());
121 m_databaseThread = 0;
124 #if ENABLE(BLOB) || ENABLE(FILE_SYSTEM)
126 m_fileThread->stop();
132 #if ENABLE(SQL_DATABASE)
134 DatabaseThread* ScriptExecutionContext::databaseThread()
136 if (!m_databaseThread && !m_hasOpenDatabases) {
137 // Create the database thread on first request - but not if at least one database was already opened,
138 // because in that case we already had a database thread and terminated it and should not create another.
139 m_databaseThread = DatabaseThread::create();
140 if (!m_databaseThread->start())
141 m_databaseThread = 0;
144 return m_databaseThread.get();
147 void ScriptExecutionContext::stopDatabases(DatabaseTaskSynchronizer* cleanupSync)
149 ASSERT(isContextThread());
150 if (m_databaseThread)
151 m_databaseThread->requestTermination(cleanupSync);
152 else if (cleanupSync)
153 cleanupSync->taskCompleted();
158 void ScriptExecutionContext::processMessagePortMessagesSoon()
160 postTask(ProcessMessagesSoonTask::create());
163 void ScriptExecutionContext::dispatchMessagePortEvents()
165 RefPtr<ScriptExecutionContext> protect(this);
167 // Make a frozen copy.
168 Vector<MessagePort*> ports;
169 copyToVector(m_messagePorts, ports);
171 unsigned portCount = ports.size();
172 for (unsigned i = 0; i < portCount; ++i) {
173 MessagePort* port = ports[i];
174 // The port may be destroyed, and another one created at the same address, but this is safe, as the worst that can happen
175 // as a result is that dispatchMessages() will be called needlessly.
176 if (m_messagePorts.contains(port) && port->started())
177 port->dispatchMessages();
181 void ScriptExecutionContext::createdMessagePort(MessagePort* port)
185 ASSERT((isDocument() && isMainThread())
186 || (isWorkerContext() && currentThread() == static_cast<WorkerContext*>(this)->thread()->threadID()));
189 m_messagePorts.add(port);
192 void ScriptExecutionContext::destroyedMessagePort(MessagePort* port)
196 ASSERT((isDocument() && isMainThread())
197 || (isWorkerContext() && currentThread() == static_cast<WorkerContext*>(this)->thread()->threadID()));
200 m_messagePorts.remove(port);
203 bool ScriptExecutionContext::canSuspendActiveDOMObjects()
205 // No protection against m_activeDOMObjects changing during iteration: canSuspend() shouldn't execute arbitrary JS.
206 m_iteratingActiveDOMObjects = true;
207 HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end();
208 for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
209 ASSERT(iter->first->scriptExecutionContext() == this);
210 if (!iter->first->canSuspend()) {
211 m_iteratingActiveDOMObjects = false;
215 m_iteratingActiveDOMObjects = false;
219 void ScriptExecutionContext::suspendActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why)
221 // No protection against m_activeDOMObjects changing during iteration: suspend() shouldn't execute arbitrary JS.
222 m_iteratingActiveDOMObjects = true;
223 HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end();
224 for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
225 ASSERT(iter->first->scriptExecutionContext() == this);
226 iter->first->suspend(why);
228 m_iteratingActiveDOMObjects = false;
231 void ScriptExecutionContext::resumeActiveDOMObjects()
233 // No protection against m_activeDOMObjects changing during iteration: resume() shouldn't execute arbitrary JS.
234 m_iteratingActiveDOMObjects = true;
235 HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end();
236 for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
237 ASSERT(iter->first->scriptExecutionContext() == this);
238 iter->first->resume();
240 m_iteratingActiveDOMObjects = false;
243 void ScriptExecutionContext::stopActiveDOMObjects()
245 // No protection against m_activeDOMObjects changing during iteration: stop() shouldn't execute arbitrary JS.
246 m_iteratingActiveDOMObjects = true;
247 HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end();
248 for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
249 ASSERT(iter->first->scriptExecutionContext() == this);
252 m_iteratingActiveDOMObjects = false;
254 // Also close MessagePorts. If they were ActiveDOMObjects (they could be) then they could be stopped instead.
258 void ScriptExecutionContext::didCreateActiveDOMObject(ActiveDOMObject* object, void* upcastPointer)
261 ASSERT(upcastPointer);
262 ASSERT(!m_inDestructor);
263 if (m_iteratingActiveDOMObjects)
265 m_activeDOMObjects.add(object, upcastPointer);
268 void ScriptExecutionContext::willDestroyActiveDOMObject(ActiveDOMObject* object)
271 if (m_iteratingActiveDOMObjects)
273 m_activeDOMObjects.remove(object);
276 void ScriptExecutionContext::didCreateDestructionObserver(ContextDestructionObserver* observer)
279 ASSERT(!m_inDestructor);
280 m_destructionObservers.add(observer);
283 void ScriptExecutionContext::willDestroyDestructionObserver(ContextDestructionObserver* observer)
286 m_destructionObservers.remove(observer);
289 void ScriptExecutionContext::closeMessagePorts() {
290 HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end();
291 for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) {
292 ASSERT((*iter)->scriptExecutionContext() == this);
297 bool ScriptExecutionContext::sanitizeScriptError(String& errorMessage, int& lineNumber, String& sourceURL)
299 KURL targetURL = completeURL(sourceURL);
300 if (securityOrigin()->canRequest(targetURL))
302 errorMessage = "Script error.";
303 sourceURL = String();
308 void ScriptExecutionContext::reportException(const String& errorMessage, int lineNumber, const String& sourceURL, PassRefPtr<ScriptCallStack> callStack)
310 if (m_inDispatchErrorEvent) {
311 if (!m_pendingExceptions)
312 m_pendingExceptions = adoptPtr(new Vector<OwnPtr<PendingException> >());
313 m_pendingExceptions->append(adoptPtr(new PendingException(errorMessage, lineNumber, sourceURL, callStack)));
317 // First report the original exception and only then all the nested ones.
319 #if !ENABLE(TIZEN_H0100127367_FACEBOOK_ISSUE)
320 //bagofholding it'a javascript, it could not be found, hence exception occurs
321 if (errorMessage.find("Can't find variable: bagofholding") == notFound)
322 if (!dispatchErrorEvent(errorMessage, lineNumber, sourceURL))
323 logExceptionToConsole(errorMessage, lineNumber, sourceURL, callStack);
326 if (!m_pendingExceptions)
329 for (size_t i = 0; i < m_pendingExceptions->size(); i++) {
330 PendingException* e = m_pendingExceptions->at(i).get();
331 logExceptionToConsole(e->m_errorMessage, e->m_lineNumber, e->m_sourceURL, e->m_callStack);
333 m_pendingExceptions.clear();
336 void ScriptExecutionContext::addConsoleMessage(MessageSource source, MessageType type, MessageLevel level, const String& message)
338 addMessage(source, type, level, message, 1, String(), 0);
341 bool ScriptExecutionContext::dispatchErrorEvent(const String& errorMessage, int lineNumber, const String& sourceURL)
343 EventTarget* target = errorEventTarget();
347 String message = errorMessage;
348 int line = lineNumber;
349 String sourceName = sourceURL;
350 sanitizeScriptError(message, line, sourceName);
352 ASSERT(!m_inDispatchErrorEvent);
353 m_inDispatchErrorEvent = true;
354 RefPtr<ErrorEvent> errorEvent = ErrorEvent::create(message, sourceName, line);
355 target->dispatchEvent(errorEvent);
356 m_inDispatchErrorEvent = false;
357 return errorEvent->defaultPrevented();
360 void ScriptExecutionContext::addTimeout(int timeoutId, DOMTimer* timer)
362 ASSERT(!m_timeouts.contains(timeoutId));
363 m_timeouts.set(timeoutId, timer);
366 void ScriptExecutionContext::removeTimeout(int timeoutId)
368 m_timeouts.remove(timeoutId);
371 DOMTimer* ScriptExecutionContext::findTimeout(int timeoutId)
373 return m_timeouts.get(timeoutId);
376 #if ENABLE(BLOB) || ENABLE(FILE_SYSTEM)
377 FileThread* ScriptExecutionContext::fileThread()
380 m_fileThread = FileThread::create();
381 if (!m_fileThread->start())
384 return m_fileThread.get();
388 void ScriptExecutionContext::adjustMinimumTimerInterval(double oldMinimumTimerInterval)
390 if (minimumTimerInterval() != oldMinimumTimerInterval) {
391 for (TimeoutMap::iterator iter = m_timeouts.begin(); iter != m_timeouts.end(); ++iter) {
392 DOMTimer* timer = iter->second;
393 timer->adjustMinimumTimerInterval(oldMinimumTimerInterval);
398 double ScriptExecutionContext::minimumTimerInterval() const
400 // The default implementation returns the DOMTimer's default
401 // minimum timer interval. FIXME: to make it work with dedicated
402 // workers, we will have to override it in the appropriate
403 // subclass, and provide a way to enumerate a Document's dedicated
404 // workers so we can update them all.
405 return Settings::defaultMinDOMTimerInterval();
408 ScriptExecutionContext::Task::~Task()
413 JSC::JSGlobalData* ScriptExecutionContext::globalData()
416 return JSDOMWindow::commonJSGlobalData();
419 if (isWorkerContext())
420 return static_cast<WorkerContext*>(this)->script()->globalData();
423 ASSERT_NOT_REACHED();
428 } // namespace WebCore