1/* 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 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. 12 * 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. 24 * 25 */ 26 27#include "config.h" 28#include "ScriptExecutionContext.h" 29 30#include "ActiveDOMObject.h" 31#include "Database.h" 32#include "DatabaseTask.h" 33#include "DatabaseThread.h" 34#include "MessagePort.h" 35#include "SecurityOrigin.h" 36#include "WorkerContext.h" 37#include "WorkerThread.h" 38#include <wtf/MainThread.h> 39#include <wtf/PassRefPtr.h> 40 41#if USE(JSC) 42#include "JSDOMWindow.h" 43#endif 44 45namespace WebCore { 46 47class ProcessMessagesSoonTask : public ScriptExecutionContext::Task { 48public: 49 static PassOwnPtr<ProcessMessagesSoonTask> create() 50 { 51 return new ProcessMessagesSoonTask; 52 } 53 54 virtual void performTask(ScriptExecutionContext* context) 55 { 56 context->dispatchMessagePortEvents(); 57 } 58}; 59 60ScriptExecutionContext::ScriptExecutionContext() 61#if ENABLE(DATABASE) 62 : m_hasOpenDatabases(false) 63#endif 64{ 65} 66 67ScriptExecutionContext::~ScriptExecutionContext() 68{ 69 HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end(); 70 for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) { 71 ASSERT(iter->first->scriptExecutionContext() == this); 72 iter->first->contextDestroyed(); 73 } 74 75 HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end(); 76 for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) { 77 ASSERT((*iter)->scriptExecutionContext() == this); 78 (*iter)->contextDestroyed(); 79 } 80#if ENABLE(DATABASE) 81 if (m_databaseThread) { 82 ASSERT(m_databaseThread->terminationRequested()); 83 m_databaseThread = 0; 84 } 85#endif 86} 87 88#if ENABLE(DATABASE) 89 90DatabaseThread* ScriptExecutionContext::databaseThread() 91{ 92 if (!m_databaseThread && !m_hasOpenDatabases) { 93 // Create the database thread on first request - but not if at least one database was already opened, 94 // because in that case we already had a database thread and terminated it and should not create another. 95 m_databaseThread = DatabaseThread::create(); 96 if (!m_databaseThread->start()) 97 m_databaseThread = 0; 98 } 99 100 return m_databaseThread.get(); 101} 102 103void ScriptExecutionContext::addOpenDatabase(Database* database) 104{ 105 ASSERT(isContextThread()); 106 if (!m_openDatabaseSet) 107 m_openDatabaseSet.set(new DatabaseSet()); 108 109 ASSERT(!m_openDatabaseSet->contains(database)); 110 m_openDatabaseSet->add(database); 111} 112 113void ScriptExecutionContext::removeOpenDatabase(Database* database) 114{ 115 ASSERT(isContextThread()); 116 ASSERT(m_openDatabaseSet && m_openDatabaseSet->contains(database)); 117 if (!m_openDatabaseSet) 118 return; 119 m_openDatabaseSet->remove(database); 120} 121 122void ScriptExecutionContext::stopDatabases(DatabaseTaskSynchronizer* cleanupSync) 123{ 124 ASSERT(isContextThread()); 125 if (m_openDatabaseSet) { 126 DatabaseSet::iterator i = m_openDatabaseSet->begin(); 127 DatabaseSet::iterator end = m_openDatabaseSet->end(); 128 for (; i != end; ++i) { 129 (*i)->stop(); 130 if (m_databaseThread) 131 m_databaseThread->unscheduleDatabaseTasks(*i); 132 } 133 } 134 135 if (m_databaseThread) 136 m_databaseThread->requestTermination(cleanupSync); 137 else if (cleanupSync) 138 cleanupSync->taskCompleted(); 139} 140 141#endif 142 143void ScriptExecutionContext::processMessagePortMessagesSoon() 144{ 145 postTask(ProcessMessagesSoonTask::create()); 146} 147 148void ScriptExecutionContext::dispatchMessagePortEvents() 149{ 150 RefPtr<ScriptExecutionContext> protect(this); 151 152 // Make a frozen copy. 153 Vector<MessagePort*> ports; 154 copyToVector(m_messagePorts, ports); 155 156 unsigned portCount = ports.size(); 157 for (unsigned i = 0; i < portCount; ++i) { 158 MessagePort* port = ports[i]; 159 // The port may be destroyed, and another one created at the same address, but this is safe, as the worst that can happen 160 // as a result is that dispatchMessages() will be called needlessly. 161 if (m_messagePorts.contains(port) && port->started()) 162 port->dispatchMessages(); 163 } 164} 165 166void ScriptExecutionContext::createdMessagePort(MessagePort* port) 167{ 168 ASSERT(port); 169#if ENABLE(WORKERS) 170 ASSERT((isDocument() && isMainThread()) 171 || (isWorkerContext() && currentThread() == static_cast<WorkerContext*>(this)->thread()->threadID())); 172#endif 173 174 m_messagePorts.add(port); 175} 176 177void ScriptExecutionContext::destroyedMessagePort(MessagePort* port) 178{ 179 ASSERT(port); 180#if ENABLE(WORKERS) 181 ASSERT((isDocument() && isMainThread()) 182 || (isWorkerContext() && currentThread() == static_cast<WorkerContext*>(this)->thread()->threadID())); 183#endif 184 185 m_messagePorts.remove(port); 186} 187 188bool ScriptExecutionContext::canSuspendActiveDOMObjects() 189{ 190 // No protection against m_activeDOMObjects changing during iteration: canSuspend() shouldn't execute arbitrary JS. 191 HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end(); 192 for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) { 193 ASSERT(iter->first->scriptExecutionContext() == this); 194 if (!iter->first->canSuspend()) 195 return false; 196 } 197 return true; 198} 199 200void ScriptExecutionContext::suspendActiveDOMObjects() 201{ 202 // No protection against m_activeDOMObjects changing during iteration: suspend() shouldn't execute arbitrary JS. 203 HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end(); 204 for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) { 205 ASSERT(iter->first->scriptExecutionContext() == this); 206 iter->first->suspend(); 207 } 208} 209 210void ScriptExecutionContext::resumeActiveDOMObjects() 211{ 212 // No protection against m_activeDOMObjects changing during iteration: resume() shouldn't execute arbitrary JS. 213 HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end(); 214 for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) { 215 ASSERT(iter->first->scriptExecutionContext() == this); 216 iter->first->resume(); 217 } 218} 219 220void ScriptExecutionContext::stopActiveDOMObjects() 221{ 222 // No protection against m_activeDOMObjects changing during iteration: stop() shouldn't execute arbitrary JS. 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->stop(); 227 } 228} 229 230void ScriptExecutionContext::createdActiveDOMObject(ActiveDOMObject* object, void* upcastPointer) 231{ 232 ASSERT(object); 233 ASSERT(upcastPointer); 234 m_activeDOMObjects.add(object, upcastPointer); 235} 236 237void ScriptExecutionContext::destroyedActiveDOMObject(ActiveDOMObject* object) 238{ 239 ASSERT(object); 240 m_activeDOMObjects.remove(object); 241} 242 243void ScriptExecutionContext::setSecurityOrigin(PassRefPtr<SecurityOrigin> securityOrigin) 244{ 245 m_securityOrigin = securityOrigin; 246} 247 248void ScriptExecutionContext::addTimeout(int timeoutId, DOMTimer* timer) 249{ 250 ASSERT(!m_timeouts.contains(timeoutId)); 251 m_timeouts.set(timeoutId, timer); 252} 253 254void ScriptExecutionContext::removeTimeout(int timeoutId) 255{ 256 m_timeouts.remove(timeoutId); 257} 258 259DOMTimer* ScriptExecutionContext::findTimeout(int timeoutId) 260{ 261 return m_timeouts.get(timeoutId); 262} 263 264ScriptExecutionContext::Task::~Task() 265{ 266} 267 268#if USE(JSC) 269JSC::JSGlobalData* ScriptExecutionContext::globalData() 270{ 271 if (isDocument()) 272 return JSDOMWindow::commonJSGlobalData(); 273 274#if ENABLE(WORKERS) 275 if (isWorkerContext()) 276 return static_cast<WorkerContext*>(this)->script()->globalData(); 277#endif 278 279 ASSERT_NOT_REACHED(); 280 return 0; 281} 282#endif 283 284} // namespace WebCore 285