Home | History | Annotate | Download | only in dom
      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/ScriptExecutionContext.h"
     30 
     31 #include "core/dom/ContextLifecycleNotifier.h"
     32 #include "core/dom/ErrorEvent.h"
     33 #include "core/dom/EventTarget.h"
     34 #include "core/dom/MessagePort.h"
     35 #include "core/html/PublicURLManager.h"
     36 #include "core/inspector/InspectorInstrumentation.h"
     37 #include "core/inspector/ScriptCallStack.h"
     38 #include "core/page/DOMTimer.h"
     39 #include "core/workers/WorkerGlobalScope.h"
     40 #include "core/workers/WorkerThread.h"
     41 #include "modules/webdatabase/DatabaseContext.h"
     42 #include "wtf/MainThread.h"
     43 
     44 namespace WebCore {
     45 
     46 class ProcessMessagesSoonTask : public ScriptExecutionContext::Task {
     47 public:
     48     static PassOwnPtr<ProcessMessagesSoonTask> create()
     49     {
     50         return adoptPtr(new ProcessMessagesSoonTask);
     51     }
     52 
     53     virtual void performTask(ScriptExecutionContext* context)
     54     {
     55         context->dispatchMessagePortEvents();
     56     }
     57 };
     58 
     59 class ScriptExecutionContext::PendingException {
     60     WTF_MAKE_NONCOPYABLE(PendingException);
     61 public:
     62     PendingException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, PassRefPtr<ScriptCallStack> callStack)
     63         : m_errorMessage(errorMessage)
     64         , m_lineNumber(lineNumber)
     65         , m_columnNumber(columnNumber)
     66         , m_sourceURL(sourceURL)
     67         , m_callStack(callStack)
     68     {
     69     }
     70     String m_errorMessage;
     71     int m_lineNumber;
     72     int m_columnNumber;
     73     String m_sourceURL;
     74     RefPtr<ScriptCallStack> m_callStack;
     75 };
     76 
     77 void ScriptExecutionContext::AddConsoleMessageTask::performTask(ScriptExecutionContext* context)
     78 {
     79     context->addConsoleMessage(m_source, m_level, m_message);
     80 }
     81 
     82 ScriptExecutionContext::ScriptExecutionContext()
     83     : m_circularSequentialID(0)
     84     , m_inDispatchErrorEvent(false)
     85     , m_activeDOMObjectsAreSuspended(false)
     86     , m_reasonForSuspendingActiveDOMObjects(static_cast<ActiveDOMObject::ReasonForSuspension>(-1))
     87     , m_activeDOMObjectsAreStopped(false)
     88 {
     89 }
     90 
     91 ScriptExecutionContext::~ScriptExecutionContext()
     92 {
     93     HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end();
     94     for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) {
     95         ASSERT((*iter)->scriptExecutionContext() == this);
     96         (*iter)->contextDestroyed();
     97     }
     98 }
     99 
    100 void ScriptExecutionContext::processMessagePortMessagesSoon()
    101 {
    102     postTask(ProcessMessagesSoonTask::create());
    103 }
    104 
    105 void ScriptExecutionContext::dispatchMessagePortEvents()
    106 {
    107     RefPtr<ScriptExecutionContext> protect(this);
    108 
    109     // Make a frozen copy.
    110     Vector<MessagePort*> ports;
    111     copyToVector(m_messagePorts, ports);
    112 
    113     unsigned portCount = ports.size();
    114     for (unsigned i = 0; i < portCount; ++i) {
    115         MessagePort* port = ports[i];
    116         // The port may be destroyed, and another one created at the same address, but this is safe, as the worst that can happen
    117         // as a result is that dispatchMessages() will be called needlessly.
    118         if (m_messagePorts.contains(port) && port->started())
    119             port->dispatchMessages();
    120     }
    121 }
    122 
    123 void ScriptExecutionContext::createdMessagePort(MessagePort* port)
    124 {
    125     ASSERT(port);
    126     ASSERT((isDocument() && isMainThread())
    127         || (isWorkerGlobalScope() && toWorkerGlobalScope(this)->thread()->isCurrentThread()));
    128 
    129     m_messagePorts.add(port);
    130 }
    131 
    132 void ScriptExecutionContext::destroyedMessagePort(MessagePort* port)
    133 {
    134     ASSERT(port);
    135     ASSERT((isDocument() && isMainThread())
    136         || (isWorkerGlobalScope() && toWorkerGlobalScope(this)->thread()->isCurrentThread()));
    137 
    138     m_messagePorts.remove(port);
    139 }
    140 
    141 bool ScriptExecutionContext::canSuspendActiveDOMObjects()
    142 {
    143     return lifecycleNotifier()->canSuspendActiveDOMObjects();
    144 }
    145 
    146 bool ScriptExecutionContext::hasPendingActivity()
    147 {
    148     if (lifecycleNotifier()->hasPendingActivity())
    149         return true;
    150 
    151     HashSet<MessagePort*>::const_iterator messagePortsEnd = m_messagePorts.end();
    152     for (HashSet<MessagePort*>::const_iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) {
    153         if ((*iter)->hasPendingActivity())
    154             return true;
    155     }
    156 
    157     return false;
    158 }
    159 
    160 void ScriptExecutionContext::suspendActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why)
    161 {
    162     lifecycleNotifier()->notifySuspendingActiveDOMObjects(why);
    163     m_activeDOMObjectsAreSuspended = true;
    164     m_reasonForSuspendingActiveDOMObjects = why;
    165 }
    166 
    167 void ScriptExecutionContext::resumeActiveDOMObjects()
    168 {
    169     m_activeDOMObjectsAreSuspended = false;
    170     lifecycleNotifier()->notifyResumingActiveDOMObjects();
    171 }
    172 
    173 void ScriptExecutionContext::stopActiveDOMObjects()
    174 {
    175     m_activeDOMObjectsAreStopped = true;
    176     lifecycleNotifier()->notifyStoppingActiveDOMObjects();
    177     // Also close MessagePorts. If they were ActiveDOMObjects (they could be) then they could be stopped instead.
    178     closeMessagePorts();
    179 }
    180 
    181 void ScriptExecutionContext::suspendActiveDOMObjectIfNeeded(ActiveDOMObject* object)
    182 {
    183     ASSERT(lifecycleNotifier()->contains(object));
    184     // Ensure all ActiveDOMObjects are suspended also newly created ones.
    185     if (m_activeDOMObjectsAreSuspended)
    186         object->suspend(m_reasonForSuspendingActiveDOMObjects);
    187 }
    188 
    189 void ScriptExecutionContext::closeMessagePorts() {
    190     HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end();
    191     for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) {
    192         ASSERT((*iter)->scriptExecutionContext() == this);
    193         (*iter)->close();
    194     }
    195 }
    196 
    197 bool ScriptExecutionContext::shouldSanitizeScriptError(const String& sourceURL, AccessControlStatus corsStatus)
    198 {
    199     return !(securityOrigin()->canRequest(completeURL(sourceURL)) || corsStatus == SharableCrossOrigin);
    200 }
    201 
    202 void ScriptExecutionContext::reportException(PassRefPtr<ErrorEvent> event, PassRefPtr<ScriptCallStack> callStack, AccessControlStatus corsStatus)
    203 {
    204     RefPtr<ErrorEvent> errorEvent = event;
    205     if (m_inDispatchErrorEvent) {
    206         if (!m_pendingExceptions)
    207             m_pendingExceptions = adoptPtr(new Vector<OwnPtr<PendingException> >());
    208         m_pendingExceptions->append(adoptPtr(new PendingException(errorEvent->message(), errorEvent->lineno(), errorEvent->colno(), errorEvent->filename(), callStack)));
    209         return;
    210     }
    211 
    212     // First report the original exception and only then all the nested ones.
    213     if (!dispatchErrorEvent(errorEvent, corsStatus))
    214         logExceptionToConsole(errorEvent->message(), errorEvent->filename(), errorEvent->lineno(), errorEvent->colno(), callStack);
    215 
    216     if (!m_pendingExceptions)
    217         return;
    218 
    219     for (size_t i = 0; i < m_pendingExceptions->size(); i++) {
    220         PendingException* e = m_pendingExceptions->at(i).get();
    221         logExceptionToConsole(e->m_errorMessage, e->m_sourceURL, e->m_lineNumber, e->m_columnNumber, e->m_callStack);
    222     }
    223     m_pendingExceptions.clear();
    224 }
    225 
    226 void ScriptExecutionContext::addConsoleMessage(MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber, ScriptState* state, unsigned long requestIdentifier)
    227 {
    228     addMessage(source, level, message, sourceURL, lineNumber, 0, state, requestIdentifier);
    229 }
    230 
    231 bool ScriptExecutionContext::dispatchErrorEvent(PassRefPtr<ErrorEvent> event, AccessControlStatus corsStatus)
    232 {
    233     EventTarget* target = errorEventTarget();
    234     if (!target)
    235         return false;
    236 
    237     RefPtr<ErrorEvent> errorEvent = event;
    238     if (shouldSanitizeScriptError(errorEvent->filename(), corsStatus))
    239         errorEvent = ErrorEvent::createSanitizedError();
    240 
    241     ASSERT(!m_inDispatchErrorEvent);
    242     m_inDispatchErrorEvent = true;
    243     target->dispatchEvent(errorEvent);
    244     m_inDispatchErrorEvent = false;
    245     return errorEvent->defaultPrevented();
    246 }
    247 
    248 int ScriptExecutionContext::circularSequentialID()
    249 {
    250     ++m_circularSequentialID;
    251     if (m_circularSequentialID <= 0)
    252         m_circularSequentialID = 1;
    253     return m_circularSequentialID;
    254 }
    255 
    256 int ScriptExecutionContext::installNewTimeout(PassOwnPtr<ScheduledAction> action, int timeout, bool singleShot)
    257 {
    258     int timeoutID;
    259     while (true) {
    260         timeoutID = circularSequentialID();
    261         if (!m_timeouts.contains(timeoutID))
    262             break;
    263     }
    264     TimeoutMap::AddResult result = m_timeouts.add(timeoutID, DOMTimer::create(this, action, timeout, singleShot, timeoutID));
    265     ASSERT(result.isNewEntry);
    266     DOMTimer* timer = result.iterator->value.get();
    267 
    268     timer->suspendIfNeeded();
    269 
    270     return timer->timeoutID();
    271 }
    272 
    273 void ScriptExecutionContext::removeTimeoutByID(int timeoutID)
    274 {
    275     if (timeoutID <= 0)
    276         return;
    277     m_timeouts.remove(timeoutID);
    278 }
    279 
    280 PublicURLManager& ScriptExecutionContext::publicURLManager()
    281 {
    282     if (!m_publicURLManager)
    283         m_publicURLManager = PublicURLManager::create(this);
    284     return *m_publicURLManager;
    285 }
    286 
    287 void ScriptExecutionContext::didChangeTimerAlignmentInterval()
    288 {
    289     for (TimeoutMap::iterator iter = m_timeouts.begin(); iter != m_timeouts.end(); ++iter)
    290         iter->value->didChangeAlignmentInterval();
    291 }
    292 
    293 double ScriptExecutionContext::timerAlignmentInterval() const
    294 {
    295     return DOMTimer::visiblePageAlignmentInterval();
    296 }
    297 
    298 ContextLifecycleNotifier* ScriptExecutionContext::lifecycleNotifier()
    299 {
    300     return static_cast<ContextLifecycleNotifier*>(LifecycleContext::lifecycleNotifier());
    301 }
    302 
    303 PassOwnPtr<LifecycleNotifier> ScriptExecutionContext::createLifecycleNotifier()
    304 {
    305     return ContextLifecycleNotifier::create(this);
    306 }
    307 
    308 ScriptExecutionContext::Task::~Task()
    309 {
    310 }
    311 
    312 void ScriptExecutionContext::setDatabaseContext(DatabaseContext* databaseContext)
    313 {
    314     m_databaseContext = databaseContext;
    315 }
    316 
    317 } // namespace WebCore
    318