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