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 45 namespace WebCore { 46 47 class ProcessMessagesSoonTask : public ScriptExecutionContext::Task { 48 public: 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 60 ScriptExecutionContext::ScriptExecutionContext() 61 #if ENABLE(DATABASE) 62 : m_hasOpenDatabases(false) 63 #endif 64 { 65 } 66 67 ScriptExecutionContext::~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 90 DatabaseThread* 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 103 void 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 113 void 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 122 void 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 143 void ScriptExecutionContext::processMessagePortMessagesSoon() 144 { 145 postTask(ProcessMessagesSoonTask::create()); 146 } 147 148 void 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 166 void 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 177 void 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 188 bool 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 200 void 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 210 void 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 220 void 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 230 void ScriptExecutionContext::createdActiveDOMObject(ActiveDOMObject* object, void* upcastPointer) 231 { 232 ASSERT(object); 233 ASSERT(upcastPointer); 234 m_activeDOMObjects.add(object, upcastPointer); 235 } 236 237 void ScriptExecutionContext::destroyedActiveDOMObject(ActiveDOMObject* object) 238 { 239 ASSERT(object); 240 m_activeDOMObjects.remove(object); 241 } 242 243 void ScriptExecutionContext::setSecurityOrigin(PassRefPtr<SecurityOrigin> securityOrigin) 244 { 245 m_securityOrigin = securityOrigin; 246 } 247 248 void ScriptExecutionContext::addTimeout(int timeoutId, DOMTimer* timer) 249 { 250 ASSERT(!m_timeouts.contains(timeoutId)); 251 m_timeouts.set(timeoutId, timer); 252 } 253 254 void ScriptExecutionContext::removeTimeout(int timeoutId) 255 { 256 m_timeouts.remove(timeoutId); 257 } 258 259 DOMTimer* ScriptExecutionContext::findTimeout(int timeoutId) 260 { 261 return m_timeouts.get(timeoutId); 262 } 263 264 ScriptExecutionContext::Task::~Task() 265 { 266 } 267 268 #if USE(JSC) 269 JSC::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