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 "core/dom/ExecutionContext.h"
30
31#include "core/dom/AddConsoleMessageTask.h"
32#include "core/dom/ContextLifecycleNotifier.h"
33#include "core/dom/ExecutionContextTask.h"
34#include "core/events/ErrorEvent.h"
35#include "core/events/EventTarget.h"
36#include "core/html/PublicURLManager.h"
37#include "core/inspector/InspectorInstrumentation.h"
38#include "core/inspector/ScriptCallStack.h"
39#include "core/workers/WorkerGlobalScope.h"
40#include "core/workers/WorkerThread.h"
41#include "wtf/MainThread.h"
42
43namespace blink {
44
45class ExecutionContext::PendingException : public NoBaseWillBeGarbageCollectedFinalized<ExecutionContext::PendingException> {
46    WTF_MAKE_NONCOPYABLE(PendingException);
47public:
48    PendingException(const String& errorMessage, int lineNumber, int columnNumber, int scriptId, const String& sourceURL, PassRefPtrWillBeRawPtr<ScriptCallStack> callStack)
49        : m_errorMessage(errorMessage)
50        , m_lineNumber(lineNumber)
51        , m_columnNumber(columnNumber)
52        , m_scriptId(scriptId)
53        , m_sourceURL(sourceURL)
54        , m_callStack(callStack)
55    {
56    }
57    void trace(Visitor* visitor)
58    {
59        visitor->trace(m_callStack);
60    }
61    String m_errorMessage;
62    int m_lineNumber;
63    int m_columnNumber;
64    int m_scriptId;
65    String m_sourceURL;
66    RefPtrWillBeMember<ScriptCallStack> m_callStack;
67};
68
69ExecutionContext::ExecutionContext()
70    : m_sandboxFlags(SandboxNone)
71    , m_circularSequentialID(0)
72    , m_inDispatchErrorEvent(false)
73    , m_activeDOMObjectsAreSuspended(false)
74    , m_activeDOMObjectsAreStopped(false)
75{
76}
77
78ExecutionContext::~ExecutionContext()
79{
80}
81
82bool ExecutionContext::hasPendingActivity()
83{
84    return lifecycleNotifier().hasPendingActivity();
85}
86
87void ExecutionContext::suspendActiveDOMObjects()
88{
89    lifecycleNotifier().notifySuspendingActiveDOMObjects();
90    m_activeDOMObjectsAreSuspended = true;
91}
92
93void ExecutionContext::resumeActiveDOMObjects()
94{
95    m_activeDOMObjectsAreSuspended = false;
96    lifecycleNotifier().notifyResumingActiveDOMObjects();
97}
98
99void ExecutionContext::stopActiveDOMObjects()
100{
101    m_activeDOMObjectsAreStopped = true;
102    lifecycleNotifier().notifyStoppingActiveDOMObjects();
103}
104
105unsigned ExecutionContext::activeDOMObjectCount()
106{
107    return lifecycleNotifier().activeDOMObjects().size();
108}
109
110void ExecutionContext::suspendScheduledTasks()
111{
112    suspendActiveDOMObjects();
113    tasksWereSuspended();
114}
115
116void ExecutionContext::resumeScheduledTasks()
117{
118    resumeActiveDOMObjects();
119    tasksWereResumed();
120}
121
122void ExecutionContext::suspendActiveDOMObjectIfNeeded(ActiveDOMObject* object)
123{
124    ASSERT(lifecycleNotifier().contains(object));
125    // Ensure all ActiveDOMObjects are suspended also newly created ones.
126    if (m_activeDOMObjectsAreSuspended)
127        object->suspend();
128}
129
130bool ExecutionContext::shouldSanitizeScriptError(const String& sourceURL, AccessControlStatus corsStatus)
131{
132    return !(securityOrigin()->canRequest(completeURL(sourceURL)) || corsStatus == SharableCrossOrigin);
133}
134
135void ExecutionContext::reportException(PassRefPtrWillBeRawPtr<ErrorEvent> event, int scriptId, PassRefPtrWillBeRawPtr<ScriptCallStack> callStack, AccessControlStatus corsStatus)
136{
137    RefPtrWillBeRawPtr<ErrorEvent> errorEvent = event;
138    if (m_inDispatchErrorEvent) {
139        if (!m_pendingExceptions)
140            m_pendingExceptions = adoptPtrWillBeNoop(new WillBeHeapVector<OwnPtrWillBeMember<PendingException> >());
141        m_pendingExceptions->append(adoptPtrWillBeNoop(new PendingException(errorEvent->messageForConsole(), errorEvent->lineno(), errorEvent->colno(), scriptId, errorEvent->filename(), callStack)));
142        return;
143    }
144
145    // First report the original exception and only then all the nested ones.
146    if (!dispatchErrorEvent(errorEvent, corsStatus))
147        logExceptionToConsole(errorEvent->messageForConsole(), scriptId, errorEvent->filename(), errorEvent->lineno(), errorEvent->colno(), callStack);
148
149    if (!m_pendingExceptions)
150        return;
151
152    for (size_t i = 0; i < m_pendingExceptions->size(); i++) {
153        PendingException* e = m_pendingExceptions->at(i).get();
154        logExceptionToConsole(e->m_errorMessage, e->m_scriptId, e->m_sourceURL, e->m_lineNumber, e->m_columnNumber, e->m_callStack);
155    }
156    m_pendingExceptions.clear();
157}
158
159bool ExecutionContext::dispatchErrorEvent(PassRefPtrWillBeRawPtr<ErrorEvent> event, AccessControlStatus corsStatus)
160{
161    EventTarget* target = errorEventTarget();
162    if (!target)
163        return false;
164
165    RefPtrWillBeRawPtr<ErrorEvent> errorEvent = event;
166    if (shouldSanitizeScriptError(errorEvent->filename(), corsStatus))
167        errorEvent = ErrorEvent::createSanitizedError(errorEvent->world());
168
169    ASSERT(!m_inDispatchErrorEvent);
170    m_inDispatchErrorEvent = true;
171    target->dispatchEvent(errorEvent);
172    m_inDispatchErrorEvent = false;
173    return errorEvent->defaultPrevented();
174}
175
176int ExecutionContext::circularSequentialID()
177{
178    ++m_circularSequentialID;
179    if (m_circularSequentialID <= 0)
180        m_circularSequentialID = 1;
181    return m_circularSequentialID;
182}
183
184int ExecutionContext::installNewTimeout(PassOwnPtr<ScheduledAction> action, int timeout, bool singleShot)
185{
186    int timeoutID;
187    while (true) {
188        timeoutID = circularSequentialID();
189        if (!m_timeouts.contains(timeoutID))
190            break;
191    }
192    TimeoutMap::AddResult result = m_timeouts.add(timeoutID, DOMTimer::create(this, action, timeout, singleShot, timeoutID));
193    ASSERT(result.isNewEntry);
194    DOMTimer* timer = result.storedValue->value.get();
195
196    timer->suspendIfNeeded();
197
198    return timer->timeoutID();
199}
200
201void ExecutionContext::removeTimeoutByID(int timeoutID)
202{
203    if (timeoutID <= 0)
204        return;
205    m_timeouts.remove(timeoutID);
206}
207
208PublicURLManager& ExecutionContext::publicURLManager()
209{
210    if (!m_publicURLManager)
211        m_publicURLManager = PublicURLManager::create(this);
212    return *m_publicURLManager;
213}
214
215void ExecutionContext::didChangeTimerAlignmentInterval()
216{
217    for (TimeoutMap::iterator iter = m_timeouts.begin(); iter != m_timeouts.end(); ++iter)
218        iter->value->didChangeAlignmentInterval();
219}
220
221SecurityOrigin* ExecutionContext::securityOrigin()
222{
223    return securityContext().securityOrigin();
224}
225
226ContentSecurityPolicy* ExecutionContext::contentSecurityPolicy()
227{
228    return securityContext().contentSecurityPolicy();
229}
230
231const KURL& ExecutionContext::url() const
232{
233    return virtualURL();
234}
235
236KURL ExecutionContext::completeURL(const String& url) const
237{
238    return virtualCompleteURL(url);
239}
240
241PassOwnPtr<LifecycleNotifier<ExecutionContext> > ExecutionContext::createLifecycleNotifier()
242{
243    return ContextLifecycleNotifier::create(this);
244}
245
246ContextLifecycleNotifier& ExecutionContext::lifecycleNotifier()
247{
248    return static_cast<ContextLifecycleNotifier&>(LifecycleContext<ExecutionContext>::lifecycleNotifier());
249}
250
251bool ExecutionContext::isIteratingOverObservers() const
252{
253    return m_lifecycleNotifier && m_lifecycleNotifier->isIteratingOverObservers();
254}
255
256void ExecutionContext::enforceSandboxFlags(SandboxFlags mask)
257{
258    m_sandboxFlags |= mask;
259
260    // The SandboxOrigin is stored redundantly in the security origin.
261    if (isSandboxed(SandboxOrigin) && securityContext().securityOrigin() && !securityContext().securityOrigin()->isUnique()) {
262        securityContext().setSecurityOrigin(SecurityOrigin::createUnique());
263        didUpdateSecurityOrigin();
264    }
265}
266
267void ExecutionContext::trace(Visitor* visitor)
268{
269#if ENABLE(OILPAN)
270    visitor->trace(m_pendingExceptions);
271    HeapSupplementable<ExecutionContext>::trace(visitor);
272#endif
273    LifecycleContext<ExecutionContext>::trace(visitor);
274}
275
276} // namespace blink
277