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:
create()49 static PassOwnPtr<ProcessMessagesSoonTask> create()
50 {
51 return new ProcessMessagesSoonTask;
52 }
53
performTask(ScriptExecutionContext * context)54 virtual void performTask(ScriptExecutionContext* context)
55 {
56 context->dispatchMessagePortEvents();
57 }
58 };
59
ScriptExecutionContext()60 ScriptExecutionContext::ScriptExecutionContext()
61 #if ENABLE(DATABASE)
62 : m_hasOpenDatabases(false)
63 #endif
64 {
65 }
66
~ScriptExecutionContext()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
databaseThread()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
addOpenDatabase(Database * database)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
removeOpenDatabase(Database * database)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
stopDatabases(DatabaseTaskSynchronizer * cleanupSync)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
processMessagePortMessagesSoon()143 void ScriptExecutionContext::processMessagePortMessagesSoon()
144 {
145 postTask(ProcessMessagesSoonTask::create());
146 }
147
dispatchMessagePortEvents()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
createdMessagePort(MessagePort * port)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
destroyedMessagePort(MessagePort * port)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
canSuspendActiveDOMObjects()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
suspendActiveDOMObjects()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
resumeActiveDOMObjects()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
stopActiveDOMObjects()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
createdActiveDOMObject(ActiveDOMObject * object,void * upcastPointer)230 void ScriptExecutionContext::createdActiveDOMObject(ActiveDOMObject* object, void* upcastPointer)
231 {
232 ASSERT(object);
233 ASSERT(upcastPointer);
234 m_activeDOMObjects.add(object, upcastPointer);
235 }
236
destroyedActiveDOMObject(ActiveDOMObject * object)237 void ScriptExecutionContext::destroyedActiveDOMObject(ActiveDOMObject* object)
238 {
239 ASSERT(object);
240 m_activeDOMObjects.remove(object);
241 }
242
setSecurityOrigin(PassRefPtr<SecurityOrigin> securityOrigin)243 void ScriptExecutionContext::setSecurityOrigin(PassRefPtr<SecurityOrigin> securityOrigin)
244 {
245 m_securityOrigin = securityOrigin;
246 }
247
addTimeout(int timeoutId,DOMTimer * timer)248 void ScriptExecutionContext::addTimeout(int timeoutId, DOMTimer* timer)
249 {
250 ASSERT(!m_timeouts.contains(timeoutId));
251 m_timeouts.set(timeoutId, timer);
252 }
253
removeTimeout(int timeoutId)254 void ScriptExecutionContext::removeTimeout(int timeoutId)
255 {
256 m_timeouts.remove(timeoutId);
257 }
258
findTimeout(int timeoutId)259 DOMTimer* ScriptExecutionContext::findTimeout(int timeoutId)
260 {
261 return m_timeouts.get(timeoutId);
262 }
263
~Task()264 ScriptExecutionContext::Task::~Task()
265 {
266 }
267
268 #if USE(JSC)
globalData()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