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 "V8Proxy.h"
     42 #include "V8Utilities.h"
     43 #include "WorkerContext.h"
     44 #include "WorkerContextExecutionProxy.h"
     45 
     46 namespace WebCore {
     47 
     48 static void weakEventListenerCallback(v8::Persistent<v8::Value>, void* parameter)
     49 {
     50     V8AbstractEventListener* listener = static_cast<V8AbstractEventListener*>(parameter);
     51     listener->disposeListenerObject();
     52 }
     53 
     54 V8AbstractEventListener::V8AbstractEventListener(bool isAttribute, const WorldContextHandle& worldContext)
     55     : EventListener(JSEventListenerType)
     56     , m_isWeak(true)
     57     , m_isAttribute(isAttribute)
     58     , m_worldContext(worldContext)
     59 {
     60 }
     61 
     62 V8AbstractEventListener::~V8AbstractEventListener()
     63 {
     64     if (!m_listener.IsEmpty()) {
     65         v8::HandleScope scope;
     66         v8::Local<v8::Object> listener = v8::Local<v8::Object>::New(m_listener);
     67         V8EventListenerList::clearWrapper(listener, m_isAttribute);
     68     }
     69     disposeListenerObject();
     70 }
     71 
     72 void V8AbstractEventListener::handleEvent(ScriptExecutionContext* context, Event* event)
     73 {
     74     // The callback function on XMLHttpRequest can clear the event listener and destroys 'this' object. Keep a local reference to it.
     75     // See issue 889829.
     76     RefPtr<V8AbstractEventListener> protect(this);
     77 
     78     v8::HandleScope handleScope;
     79 
     80     v8::Local<v8::Context> v8Context = toV8Context(context, worldContext());
     81     if (v8Context.IsEmpty())
     82         return;
     83 
     84     // Enter the V8 context in which to perform the event handling.
     85     v8::Context::Scope scope(v8Context);
     86 
     87     // Get the V8 wrapper for the event object.
     88     v8::Handle<v8::Value> jsEvent = toV8(event);
     89 
     90     invokeEventHandler(context, event, jsEvent);
     91 
     92     Document::updateStyleForAllDocuments();
     93 }
     94 
     95 void V8AbstractEventListener::disposeListenerObject()
     96 {
     97     if (!m_listener.IsEmpty()) {
     98 #ifndef NDEBUG
     99         V8GCController::unregisterGlobalHandle(this, m_listener);
    100 #endif
    101         m_listener.Dispose();
    102         m_listener.Clear();
    103     }
    104 }
    105 
    106 void V8AbstractEventListener::setListenerObject(v8::Handle<v8::Object> listener)
    107 {
    108     disposeListenerObject();
    109     m_listener = v8::Persistent<v8::Object>::New(listener);
    110 #ifndef NDEBUG
    111     V8GCController::registerGlobalHandle(EVENT_LISTENER, this, m_listener);
    112 #endif
    113     if (m_isWeak)
    114         m_listener.MakeWeak(this, &weakEventListenerCallback);
    115 }
    116 
    117 void V8AbstractEventListener::invokeEventHandler(ScriptExecutionContext* context, Event* event, v8::Handle<v8::Value> jsEvent)
    118 {
    119 
    120     v8::Local<v8::Context> v8Context = toV8Context(context, worldContext());
    121     if (v8Context.IsEmpty())
    122         return;
    123 
    124     // We push the event being processed into the global object, so that it can be exposed by DOMWindow's bindings.
    125     v8::Local<v8::String> eventSymbol = v8::String::NewSymbol("event");
    126     v8::Local<v8::Value> returnValue;
    127 
    128     // In beforeunload/unload handlers, we want to avoid sleeps which do tight loops of calling Date.getTime().
    129     if (event->type() == "beforeunload" || event->type() == "unload")
    130         DateExtension::get()->setAllowSleep(false);
    131 
    132     {
    133         // Catch exceptions thrown in the event handler so they do not propagate to javascript code that caused the event to fire.
    134         v8::TryCatch tryCatch;
    135         tryCatch.SetVerbose(true);
    136 
    137         // Save the old 'event' property so we can restore it later.
    138         v8::Local<v8::Value> savedEvent = v8Context->Global()->GetHiddenValue(eventSymbol);
    139         tryCatch.Reset();
    140 
    141         // Make the event available in the global object, so DOMWindow can expose it.
    142         v8Context->Global()->SetHiddenValue(eventSymbol, jsEvent);
    143         tryCatch.Reset();
    144 
    145         // Call the event handler.
    146         returnValue = callListenerFunction(context, jsEvent, event);
    147         if (!tryCatch.CanContinue())
    148             return;
    149 
    150         // If an error occurs while handling the event, it should be reported.
    151         if (tryCatch.HasCaught()) {
    152             reportException(0, tryCatch);
    153             tryCatch.Reset();
    154         }
    155 
    156         // Restore the old event. This must be done for all exit paths through this method.
    157         if (savedEvent.IsEmpty())
    158             v8Context->Global()->SetHiddenValue(eventSymbol, v8::Undefined());
    159         else
    160             v8Context->Global()->SetHiddenValue(eventSymbol, savedEvent);
    161         tryCatch.Reset();
    162     }
    163 
    164     if (event->type() == "beforeunload" || event->type() == "unload")
    165         DateExtension::get()->setAllowSleep(true);
    166 
    167     ASSERT(!V8Proxy::handleOutOfMemory() || returnValue.IsEmpty());
    168 
    169     if (returnValue.IsEmpty())
    170         return;
    171 
    172     if (!returnValue->IsNull() && !returnValue->IsUndefined() && event->storesResultAsString())
    173         event->storeResult(toWebCoreString(returnValue));
    174 
    175     // Prevent default action if the return value is false;
    176     // FIXME: Add example, and reference to bug entry.
    177     if (m_isAttribute && returnValue->IsBoolean() && !returnValue->BooleanValue())
    178         event->preventDefault();
    179 }
    180 
    181 v8::Local<v8::Object> V8AbstractEventListener::getReceiverObject(Event* event)
    182 {
    183     if (!m_listener.IsEmpty() && !m_listener->IsFunction())
    184         return v8::Local<v8::Object>::New(m_listener);
    185 
    186     EventTarget* target = event->currentTarget();
    187     v8::Handle<v8::Value> value = V8DOMWrapper::convertEventTargetToV8Object(target);
    188     if (value.IsEmpty())
    189         return v8::Local<v8::Object>();
    190     return v8::Local<v8::Object>::New(v8::Handle<v8::Object>::Cast(value));
    191 }
    192 
    193 } // namespace WebCore
    194