Home | History | Annotate | Download | only in v8
      1 /*
      2  * Copyright (C) 2006, 2007, 2008, 2009 Google 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 are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "V8AbstractEventListener.h"
     33 
     34 #include "DateExtension.h"
     35 #include "Document.h"
     36 #include "Event.h"
     37 #include "Frame.h"
     38 #include "V8Binding.h"
     39 #include "V8Event.h"
     40 #include "V8EventListenerList.h"
     41 #include "V8HiddenPropertyName.h"
     42 #include "V8Proxy.h"
     43 #include "V8Utilities.h"
     44 #include "WorkerContext.h"
     45 #include "WorkerContextExecutionProxy.h"
     46 
     47 namespace WebCore {
     48 
     49 static void weakEventListenerCallback(v8::Persistent<v8::Value>, void* parameter)
     50 {
     51     V8AbstractEventListener* listener = static_cast<V8AbstractEventListener*>(parameter);
     52     listener->disposeListenerObject();
     53 }
     54 
     55 V8AbstractEventListener::V8AbstractEventListener(bool isAttribute, const WorldContextHandle& worldContext)
     56     : EventListener(JSEventListenerType)
     57     , m_isWeak(true)
     58     , m_isAttribute(isAttribute)
     59     , m_worldContext(worldContext)
     60 {
     61 }
     62 
     63 V8AbstractEventListener::~V8AbstractEventListener()
     64 {
     65     if (!m_listener.IsEmpty()) {
     66         v8::HandleScope scope;
     67         v8::Local<v8::Object> listener = v8::Local<v8::Object>::New(m_listener);
     68         V8EventListenerList::clearWrapper(listener, m_isAttribute);
     69     }
     70     disposeListenerObject();
     71 }
     72 
     73 void V8AbstractEventListener::handleEvent(ScriptExecutionContext* context, Event* event)
     74 {
     75 #ifdef ANDROID
     76     // Monkey data shows that we can crash here, due to script executing while the
     77     // page's frame has been detached (in the middle of a navigation).
     78     // See b/5201341
     79     if (!context)
     80         return;
     81 #endif
     82 
     83     // Don't reenter V8 if execution was terminated in this instance of V8.
     84     if (context->isJSExecutionForbidden())
     85         return;
     86 
     87     ASSERT(event);
     88 
     89     // The callback function on XMLHttpRequest can clear the event listener and destroys 'this' object. Keep a local reference to it.
     90     // See issue 889829.
     91     RefPtr<V8AbstractEventListener> protect(this);
     92 
     93     v8::HandleScope handleScope;
     94 
     95     v8::Local<v8::Context> v8Context = toV8Context(context, worldContext());
     96     if (v8Context.IsEmpty())
     97         return;
     98 
     99     // Enter the V8 context in which to perform the event handling.
    100     v8::Context::Scope scope(v8Context);
    101 
    102     // Get the V8 wrapper for the event object.
    103     v8::Handle<v8::Value> jsEvent = toV8(event);
    104     ASSERT(!jsEvent.IsEmpty());
    105 
    106     invokeEventHandler(context, event, jsEvent);
    107 }
    108 
    109 void V8AbstractEventListener::disposeListenerObject()
    110 {
    111     if (!m_listener.IsEmpty()) {
    112 #ifndef NDEBUG
    113         V8GCController::unregisterGlobalHandle(this, m_listener);
    114 #endif
    115         m_listener.Dispose();
    116         m_listener.Clear();
    117     }
    118 }
    119 
    120 void V8AbstractEventListener::setListenerObject(v8::Handle<v8::Object> listener)
    121 {
    122     disposeListenerObject();
    123     m_listener = v8::Persistent<v8::Object>::New(listener);
    124 #ifndef NDEBUG
    125     V8GCController::registerGlobalHandle(EVENT_LISTENER, this, m_listener);
    126 #endif
    127     if (m_isWeak)
    128         m_listener.MakeWeak(this, &weakEventListenerCallback);
    129 }
    130 
    131 void V8AbstractEventListener::invokeEventHandler(ScriptExecutionContext* context, Event* event, v8::Handle<v8::Value> jsEvent)
    132 {
    133     // If jsEvent is empty, attempt to set it as a hidden value would crash v8.
    134     if (jsEvent.IsEmpty())
    135         return;
    136 
    137     v8::Local<v8::Context> v8Context = toV8Context(context, worldContext());
    138     if (v8Context.IsEmpty())
    139         return;
    140 
    141     // We push the event being processed into the global object, so that it can be exposed by DOMWindow's bindings.
    142     v8::Handle<v8::String> eventSymbol = V8HiddenPropertyName::event();
    143     v8::Local<v8::Value> returnValue;
    144 
    145     // In beforeunload/unload handlers, we want to avoid sleeps which do tight loops of calling Date.getTime().
    146     if (event->type() == "beforeunload" || event->type() == "unload")
    147         DateExtension::get()->setAllowSleep(false);
    148 
    149     {
    150         // Catch exceptions thrown in the event handler so they do not propagate to javascript code that caused the event to fire.
    151         v8::TryCatch tryCatch;
    152         tryCatch.SetVerbose(true);
    153 
    154         // Save the old 'event' property so we can restore it later.
    155         v8::Local<v8::Value> savedEvent = v8Context->Global()->GetHiddenValue(eventSymbol);
    156         tryCatch.Reset();
    157 
    158         // Make the event available in the global object, so DOMWindow can expose it.
    159         v8Context->Global()->SetHiddenValue(eventSymbol, jsEvent);
    160         tryCatch.Reset();
    161 
    162         returnValue = callListenerFunction(context, jsEvent, event);
    163         if (tryCatch.HasCaught())
    164             event->target()->uncaughtExceptionInEventHandler();
    165 
    166         if (!tryCatch.CanContinue()) { // Result of TerminateExecution().
    167 #if ENABLE(WORKERS)
    168             if (context->isWorkerContext())
    169                 static_cast<WorkerContext*>(context)->script()->forbidExecution();
    170 #endif
    171             return;
    172         }
    173         tryCatch.Reset();
    174 
    175         // Restore the old event. This must be done for all exit paths through this method.
    176         if (savedEvent.IsEmpty())
    177             v8Context->Global()->SetHiddenValue(eventSymbol, v8::Undefined());
    178         else
    179             v8Context->Global()->SetHiddenValue(eventSymbol, savedEvent);
    180         tryCatch.Reset();
    181     }
    182 
    183     if (event->type() == "beforeunload" || event->type() == "unload")
    184         DateExtension::get()->setAllowSleep(true);
    185 
    186     ASSERT(!V8Proxy::handleOutOfMemory() || returnValue.IsEmpty());
    187 
    188     if (returnValue.IsEmpty())
    189         return;
    190 
    191     if (!returnValue->IsNull() && !returnValue->IsUndefined() && event->storesResultAsString())
    192         event->storeResult(toWebCoreString(returnValue));
    193 
    194     // Prevent default action if the return value is false;
    195     // FIXME: Add example, and reference to bug entry.
    196     if (m_isAttribute && returnValue->IsBoolean() && !returnValue->BooleanValue())
    197         event->preventDefault();
    198 }
    199 
    200 v8::Local<v8::Object> V8AbstractEventListener::getReceiverObject(Event* event)
    201 {
    202     if (!m_listener.IsEmpty() && !m_listener->IsFunction())
    203         return v8::Local<v8::Object>::New(m_listener);
    204 
    205     EventTarget* target = event->currentTarget();
    206     v8::Handle<v8::Value> value = V8DOMWrapper::convertEventTargetToV8Object(target);
    207     if (value.IsEmpty())
    208         return v8::Local<v8::Object>();
    209     return v8::Local<v8::Object>::New(v8::Handle<v8::Object>::Cast(value));
    210 }
    211 
    212 } // namespace WebCore
    213