/* * Copyright (C) 2008 Apple Inc. All Rights Reserved. * Copyright (C) 2012 Google Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "sky/engine/core/dom/ExecutionContext.h" #include "sky/engine/core/dom/ContextLifecycleNotifier.h" #include "sky/engine/core/events/ErrorEvent.h" #include "sky/engine/core/events/EventTarget.h" #include "sky/engine/core/inspector/ScriptCallStack.h" #include "sky/engine/wtf/MainThread.h" namespace blink { class ExecutionContext::PendingException { WTF_MAKE_NONCOPYABLE(PendingException); public: PendingException(const String& errorMessage, int lineNumber, int columnNumber, int scriptId, const String& sourceURL, PassRefPtr callStack) : m_errorMessage(errorMessage) , m_lineNumber(lineNumber) , m_columnNumber(columnNumber) , m_scriptId(scriptId) , m_sourceURL(sourceURL) , m_callStack(callStack) { } String m_errorMessage; int m_lineNumber; int m_columnNumber; int m_scriptId; String m_sourceURL; RefPtr m_callStack; }; ExecutionContext::ExecutionContext() : m_client(0) , m_circularSequentialID(0) , m_inDispatchErrorEvent(false) , m_activeDOMObjectsAreSuspended(false) , m_activeDOMObjectsAreStopped(false) { } ExecutionContext::~ExecutionContext() { } bool ExecutionContext::hasPendingActivity() { return lifecycleNotifier().hasPendingActivity(); } void ExecutionContext::stopActiveDOMObjects() { m_activeDOMObjectsAreStopped = true; lifecycleNotifier().notifyStoppingActiveDOMObjects(); } unsigned ExecutionContext::activeDOMObjectCount() { return lifecycleNotifier().activeDOMObjects().size(); } void ExecutionContext::suspendActiveDOMObjectIfNeeded(ActiveDOMObject* object) { ASSERT(lifecycleNotifier().contains(object)); // Ensure all ActiveDOMObjects are suspended also newly created ones. if (m_activeDOMObjectsAreSuspended) object->suspend(); } void ExecutionContext::reportException(PassRefPtr event, int scriptId, PassRefPtr callStack) { RefPtr errorEvent = event; if (m_inDispatchErrorEvent) { if (!m_pendingExceptions) m_pendingExceptions = adoptPtr(new Vector >()); m_pendingExceptions->append(adoptPtr(new PendingException(errorEvent->messageForConsole(), errorEvent->lineno(), errorEvent->colno(), scriptId, errorEvent->filename(), callStack))); return; } // First report the original exception and only then all the nested ones. if (!dispatchErrorEvent(errorEvent) && m_client) m_client->logExceptionToConsole(errorEvent->messageForConsole(), scriptId, errorEvent->filename(), errorEvent->lineno(), errorEvent->colno(), callStack); if (!m_pendingExceptions) return; for (size_t i = 0; i < m_pendingExceptions->size(); i++) { PendingException* e = m_pendingExceptions->at(i).get(); if (m_client) m_client->logExceptionToConsole(e->m_errorMessage, e->m_scriptId, e->m_sourceURL, e->m_lineNumber, e->m_columnNumber, e->m_callStack); } m_pendingExceptions.clear(); } void ExecutionContext::addConsoleMessage(PassRefPtr consoleMessage) { if (!m_client) return; m_client->addMessage(consoleMessage); } bool ExecutionContext::dispatchErrorEvent(PassRefPtr event) { if (!m_client) return false; EventTarget* target = m_client->errorEventTarget(); if (!target) return false; RefPtr errorEvent = event; ASSERT(!m_inDispatchErrorEvent); m_inDispatchErrorEvent = true; target->dispatchEvent(errorEvent); m_inDispatchErrorEvent = false; return errorEvent->defaultPrevented(); } int ExecutionContext::circularSequentialID() { ++m_circularSequentialID; if (m_circularSequentialID <= 0) m_circularSequentialID = 1; return m_circularSequentialID; } int ExecutionContext::installNewTimeout(PassOwnPtr action, int timeout, bool singleShot) { int timeoutID; while (true) { timeoutID = circularSequentialID(); if (!m_timeouts.contains(timeoutID)) break; } TimeoutMap::AddResult result = m_timeouts.add(timeoutID, DOMTimer::create(this, action, timeout, singleShot, timeoutID)); ASSERT(result.isNewEntry); DOMTimer* timer = result.storedValue->value.get(); timer->suspendIfNeeded(); return timer->timeoutID(); } void ExecutionContext::removeTimeoutByID(int timeoutID) { if (timeoutID <= 0) return; m_timeouts.remove(timeoutID); } void ExecutionContext::didChangeTimerAlignmentInterval() { for (TimeoutMap::iterator iter = m_timeouts.begin(); iter != m_timeouts.end(); ++iter) iter->value->didChangeAlignmentInterval(); } const KURL& ExecutionContext::url() const { if (!m_client) { DEFINE_STATIC_LOCAL(KURL, emptyURL, ()); return emptyURL; } return virtualURL(); } KURL ExecutionContext::completeURL(const String& url) const { if (!m_client) { DEFINE_STATIC_LOCAL(KURL, emptyURL, ()); return emptyURL; } return virtualCompleteURL(url); } LocalDOMWindow* ExecutionContext::executingWindow() const { RELEASE_ASSERT(m_client); return m_client->executingWindow(); } double ExecutionContext::timerAlignmentInterval() const { if (!m_client) return DOMTimer::visiblePageAlignmentInterval(); return m_client->timerAlignmentInterval(); } PassOwnPtr > ExecutionContext::createLifecycleNotifier() { return ContextLifecycleNotifier::create(this); } ContextLifecycleNotifier& ExecutionContext::lifecycleNotifier() { return static_cast(LifecycleContext::lifecycleNotifier()); } bool ExecutionContext::isIteratingOverObservers() const { return m_lifecycleNotifier && m_lifecycleNotifier->isIteratingOverObservers(); } } // namespace blink