1/*
2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2012 Google Inc. All Rights Reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 */
27
28#include "config.h"
29#include "ScriptExecutionContext.h"
30
31#include "CachedScript.h"
32#include "DOMTimer.h"
33#include "ErrorEvent.h"
34#include "MessagePort.h"
35#include "PublicURLManager.h"
36#include "ScriptCallStack.h"
37#include "Settings.h"
38#include "WorkerContext.h"
39#include "WorkerThread.h"
40#include <wtf/MainThread.h>
41
42// FIXME: This is a layering violation.
43#include "JSDOMWindow.h"
44
45#if ENABLE(SQL_DATABASE)
46#include "DatabaseContext.h"
47#endif
48
49namespace WebCore {
50
51class ProcessMessagesSoonTask : public ScriptExecutionContext::Task {
52public:
53    static PassOwnPtr<ProcessMessagesSoonTask> create()
54    {
55        return adoptPtr(new ProcessMessagesSoonTask);
56    }
57
58    virtual void performTask(ScriptExecutionContext* context)
59    {
60        context->dispatchMessagePortEvents();
61    }
62};
63
64class ScriptExecutionContext::PendingException {
65    WTF_MAKE_NONCOPYABLE(PendingException);
66public:
67    PendingException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, PassRefPtr<ScriptCallStack> callStack)
68        : m_errorMessage(errorMessage)
69        , m_lineNumber(lineNumber)
70        , m_columnNumber(columnNumber)
71        , m_sourceURL(sourceURL)
72        , m_callStack(callStack)
73    {
74    }
75    String m_errorMessage;
76    int m_lineNumber;
77    int m_columnNumber;
78    String m_sourceURL;
79    RefPtr<ScriptCallStack> m_callStack;
80};
81
82void ScriptExecutionContext::AddConsoleMessageTask::performTask(ScriptExecutionContext* context)
83{
84    context->addConsoleMessage(m_source, m_level, m_message);
85}
86
87ScriptExecutionContext::ScriptExecutionContext()
88    : m_iteratingActiveDOMObjects(false)
89    , m_inDestructor(false)
90    , m_circularSequentialID(0)
91    , m_inDispatchErrorEvent(false)
92    , m_activeDOMObjectsAreSuspended(false)
93    , m_reasonForSuspendingActiveDOMObjects(static_cast<ActiveDOMObject::ReasonForSuspension>(-1))
94    , m_activeDOMObjectsAreStopped(false)
95{
96}
97
98ScriptExecutionContext::~ScriptExecutionContext()
99{
100    m_inDestructor = true;
101    for (HashSet<ContextDestructionObserver*>::iterator iter = m_destructionObservers.begin(); iter != m_destructionObservers.end(); iter = m_destructionObservers.begin()) {
102        ContextDestructionObserver* observer = *iter;
103        m_destructionObservers.remove(observer);
104        ASSERT(observer->scriptExecutionContext() == this);
105        observer->contextDestroyed();
106    }
107
108    HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end();
109    for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) {
110        ASSERT((*iter)->scriptExecutionContext() == this);
111        (*iter)->contextDestroyed();
112    }
113#if ENABLE(BLOB)
114    if (m_publicURLManager)
115        m_publicURLManager->contextDestroyed();
116#endif
117}
118
119void ScriptExecutionContext::processMessagePortMessagesSoon()
120{
121    postTask(ProcessMessagesSoonTask::create());
122}
123
124void ScriptExecutionContext::dispatchMessagePortEvents()
125{
126    RefPtr<ScriptExecutionContext> protect(this);
127
128    // Make a frozen copy.
129    Vector<MessagePort*> ports;
130    copyToVector(m_messagePorts, ports);
131
132    unsigned portCount = ports.size();
133    for (unsigned i = 0; i < portCount; ++i) {
134        MessagePort* port = ports[i];
135        // The port may be destroyed, and another one created at the same address, but this is safe, as the worst that can happen
136        // as a result is that dispatchMessages() will be called needlessly.
137        if (m_messagePorts.contains(port) && port->started())
138            port->dispatchMessages();
139    }
140}
141
142void ScriptExecutionContext::createdMessagePort(MessagePort* port)
143{
144    ASSERT(port);
145#if ENABLE(WORKERS)
146    ASSERT((isDocument() && isMainThread())
147        || (isWorkerContext() && currentThread() == static_cast<WorkerContext*>(this)->thread()->threadID()));
148#endif
149
150    m_messagePorts.add(port);
151}
152
153void ScriptExecutionContext::destroyedMessagePort(MessagePort* port)
154{
155    ASSERT(port);
156#if ENABLE(WORKERS)
157    ASSERT((isDocument() && isMainThread())
158        || (isWorkerContext() && currentThread() == static_cast<WorkerContext*>(this)->thread()->threadID()));
159#endif
160
161    m_messagePorts.remove(port);
162}
163
164bool ScriptExecutionContext::canSuspendActiveDOMObjects()
165{
166    // No protection against m_activeDOMObjects changing during iteration: canSuspend() shouldn't execute arbitrary JS.
167    m_iteratingActiveDOMObjects = true;
168    ActiveDOMObjectsSet::iterator activeObjectsEnd = m_activeDOMObjects.end();
169    for (ActiveDOMObjectsSet::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
170        ASSERT((*iter)->scriptExecutionContext() == this);
171        ASSERT((*iter)->suspendIfNeededCalled());
172        if (!(*iter)->canSuspend()) {
173            m_iteratingActiveDOMObjects = false;
174            return false;
175        }
176    }
177    m_iteratingActiveDOMObjects = false;
178    return true;
179}
180
181void ScriptExecutionContext::suspendActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why)
182{
183    // No protection against m_activeDOMObjects changing during iteration: suspend() shouldn't execute arbitrary JS.
184    m_iteratingActiveDOMObjects = true;
185    ActiveDOMObjectsSet::iterator activeObjectsEnd = m_activeDOMObjects.end();
186    for (ActiveDOMObjectsSet::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
187        ASSERT((*iter)->scriptExecutionContext() == this);
188        ASSERT((*iter)->suspendIfNeededCalled());
189        (*iter)->suspend(why);
190    }
191    m_iteratingActiveDOMObjects = false;
192    m_activeDOMObjectsAreSuspended = true;
193    m_reasonForSuspendingActiveDOMObjects = why;
194}
195
196void ScriptExecutionContext::resumeActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why)
197{
198    if (m_reasonForSuspendingActiveDOMObjects != why)
199        return;
200
201    m_activeDOMObjectsAreSuspended = false;
202    // No protection against m_activeDOMObjects changing during iteration: resume() shouldn't execute arbitrary JS.
203    m_iteratingActiveDOMObjects = true;
204    ActiveDOMObjectsSet::iterator activeObjectsEnd = m_activeDOMObjects.end();
205    for (ActiveDOMObjectsSet::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
206        ASSERT((*iter)->scriptExecutionContext() == this);
207        ASSERT((*iter)->suspendIfNeededCalled());
208        (*iter)->resume();
209    }
210    m_iteratingActiveDOMObjects = false;
211}
212
213void ScriptExecutionContext::stopActiveDOMObjects()
214{
215    if (m_activeDOMObjectsAreStopped)
216        return;
217    m_activeDOMObjectsAreStopped = true;
218    // No protection against m_activeDOMObjects changing during iteration: stop() shouldn't execute arbitrary JS.
219    m_iteratingActiveDOMObjects = true;
220    ActiveDOMObjectsSet::iterator activeObjectsEnd = m_activeDOMObjects.end();
221    for (ActiveDOMObjectsSet::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
222        ASSERT((*iter)->scriptExecutionContext() == this);
223        ASSERT((*iter)->suspendIfNeededCalled());
224        (*iter)->stop();
225    }
226    m_iteratingActiveDOMObjects = false;
227
228    // Also close MessagePorts. If they were ActiveDOMObjects (they could be) then they could be stopped instead.
229    closeMessagePorts();
230}
231
232void ScriptExecutionContext::suspendActiveDOMObjectIfNeeded(ActiveDOMObject* object)
233{
234    ASSERT(m_activeDOMObjects.contains(object));
235    // Ensure all ActiveDOMObjects are suspended also newly created ones.
236    if (m_activeDOMObjectsAreSuspended)
237        object->suspend(m_reasonForSuspendingActiveDOMObjects);
238    if (m_activeDOMObjectsAreStopped)
239        object->stop();
240}
241
242void ScriptExecutionContext::didCreateActiveDOMObject(ActiveDOMObject* object)
243{
244    ASSERT(object);
245    ASSERT(!m_inDestructor);
246    if (m_iteratingActiveDOMObjects)
247        CRASH();
248    m_activeDOMObjects.add(object);
249}
250
251void ScriptExecutionContext::willDestroyActiveDOMObject(ActiveDOMObject* object)
252{
253    ASSERT(object);
254    if (m_iteratingActiveDOMObjects)
255        CRASH();
256    m_activeDOMObjects.remove(object);
257}
258
259void ScriptExecutionContext::didCreateDestructionObserver(ContextDestructionObserver* observer)
260{
261    ASSERT(observer);
262    ASSERT(!m_inDestructor);
263    m_destructionObservers.add(observer);
264}
265
266void ScriptExecutionContext::willDestroyDestructionObserver(ContextDestructionObserver* observer)
267{
268    ASSERT(observer);
269    m_destructionObservers.remove(observer);
270}
271
272void ScriptExecutionContext::closeMessagePorts() {
273    HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end();
274    for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) {
275        ASSERT((*iter)->scriptExecutionContext() == this);
276        (*iter)->close();
277    }
278}
279
280bool ScriptExecutionContext::sanitizeScriptError(String& errorMessage, int& lineNumber, String& sourceURL, CachedScript* cachedScript)
281{
282    KURL targetURL = completeURL(sourceURL);
283    if (securityOrigin()->canRequest(targetURL) || (cachedScript && cachedScript->passesAccessControlCheck(securityOrigin())))
284        return false;
285    errorMessage = "Script error.";
286    sourceURL = String();
287    lineNumber = 0;
288    return true;
289}
290
291void ScriptExecutionContext::reportException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, PassRefPtr<ScriptCallStack> callStack, CachedScript* cachedScript)
292{
293    if (m_inDispatchErrorEvent) {
294        if (!m_pendingExceptions)
295            m_pendingExceptions = adoptPtr(new Vector<OwnPtr<PendingException> >());
296        m_pendingExceptions->append(adoptPtr(new PendingException(errorMessage, lineNumber, columnNumber, sourceURL, callStack)));
297        return;
298    }
299
300    // First report the original exception and only then all the nested ones.
301    if (!dispatchErrorEvent(errorMessage, lineNumber, sourceURL, cachedScript))
302        logExceptionToConsole(errorMessage, sourceURL, lineNumber, columnNumber, callStack);
303
304    if (!m_pendingExceptions)
305        return;
306
307    for (size_t i = 0; i < m_pendingExceptions->size(); i++) {
308        PendingException* e = m_pendingExceptions->at(i).get();
309        logExceptionToConsole(e->m_errorMessage, e->m_sourceURL, e->m_lineNumber, e->m_columnNumber, e->m_callStack);
310    }
311    m_pendingExceptions.clear();
312}
313
314void ScriptExecutionContext::addConsoleMessage(MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, ScriptState* state, unsigned long requestIdentifier)
315{
316    addMessage(source, level, message, sourceURL, lineNumber, columnNumber, 0, state, requestIdentifier);
317}
318
319bool ScriptExecutionContext::dispatchErrorEvent(const String& errorMessage, int lineNumber, const String& sourceURL, CachedScript* cachedScript)
320{
321    EventTarget* target = errorEventTarget();
322    if (!target)
323        return false;
324
325    String message = errorMessage;
326    int line = lineNumber;
327    String sourceName = sourceURL;
328    sanitizeScriptError(message, line, sourceName, cachedScript);
329
330    ASSERT(!m_inDispatchErrorEvent);
331    m_inDispatchErrorEvent = true;
332    RefPtr<ErrorEvent> errorEvent = ErrorEvent::create(message, sourceName, line);
333    target->dispatchEvent(errorEvent);
334    m_inDispatchErrorEvent = false;
335    return errorEvent->defaultPrevented();
336}
337
338int ScriptExecutionContext::circularSequentialID()
339{
340    ++m_circularSequentialID;
341    if (m_circularSequentialID <= 0)
342        m_circularSequentialID = 1;
343    return m_circularSequentialID;
344}
345
346#if ENABLE(BLOB)
347PublicURLManager& ScriptExecutionContext::publicURLManager()
348{
349    if (!m_publicURLManager)
350        m_publicURLManager = PublicURLManager::create();
351    return *m_publicURLManager;
352}
353#endif
354
355void ScriptExecutionContext::adjustMinimumTimerInterval(double oldMinimumTimerInterval)
356{
357    if (minimumTimerInterval() != oldMinimumTimerInterval) {
358        for (TimeoutMap::iterator iter = m_timeouts.begin(); iter != m_timeouts.end(); ++iter) {
359            DOMTimer* timer = iter->value;
360            timer->adjustMinimumTimerInterval(oldMinimumTimerInterval);
361        }
362    }
363}
364
365double ScriptExecutionContext::minimumTimerInterval() const
366{
367    // The default implementation returns the DOMTimer's default
368    // minimum timer interval. FIXME: to make it work with dedicated
369    // workers, we will have to override it in the appropriate
370    // subclass, and provide a way to enumerate a Document's dedicated
371    // workers so we can update them all.
372    return Settings::defaultMinDOMTimerInterval();
373}
374
375void ScriptExecutionContext::didChangeTimerAlignmentInterval()
376{
377    for (TimeoutMap::iterator iter = m_timeouts.begin(); iter != m_timeouts.end(); ++iter) {
378        DOMTimer* timer = iter->value;
379        timer->didChangeAlignmentInterval();
380    }
381}
382
383double ScriptExecutionContext::timerAlignmentInterval() const
384{
385    return Settings::defaultDOMTimerAlignmentInterval();
386}
387
388ScriptExecutionContext::Task::~Task()
389{
390}
391
392JSC::VM* ScriptExecutionContext::vm()
393{
394     if (isDocument())
395        return JSDOMWindow::commonVM();
396
397#if ENABLE(WORKERS)
398    if (isWorkerContext())
399        return static_cast<WorkerContext*>(this)->script()->vm();
400#endif
401
402    ASSERT_NOT_REACHED();
403    return 0;
404}
405
406#if ENABLE(SQL_DATABASE)
407void ScriptExecutionContext::setDatabaseContext(DatabaseContext* databaseContext)
408{
409    ASSERT(!m_databaseContext);
410    m_databaseContext = databaseContext;
411}
412#endif
413
414} // namespace WebCore
415