Home | History | Annotate | Download | only in v8
      1 /*
      2  * Copyright (C) 2013 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 "bindings/v8/V8CustomElementLifecycleCallbacks.h"
     33 
     34 #include "V8Element.h"
     35 #include "bindings/v8/CustomElementBinding.h"
     36 #include "bindings/v8/DOMDataStore.h"
     37 #include "bindings/v8/ScriptController.h"
     38 #include "bindings/v8/V8Binding.h"
     39 #include "bindings/v8/V8HiddenPropertyName.h"
     40 #include "bindings/v8/V8PerContextData.h"
     41 #include "core/dom/ExecutionContext.h"
     42 #include "wtf/PassOwnPtr.h"
     43 
     44 namespace WebCore {
     45 
     46 #define CALLBACK_LIST(V)                  \
     47     V(created, Created)                   \
     48     V(attached, Attached)           \
     49     V(detached, Detached)                 \
     50     V(attributeChanged, AttributeChanged)
     51 
     52 PassRefPtr<V8CustomElementLifecycleCallbacks> V8CustomElementLifecycleCallbacks::create(ExecutionContext* executionContext, v8::Handle<v8::Object> prototype, v8::Handle<v8::Function> created, v8::Handle<v8::Function> attached, v8::Handle<v8::Function> detached, v8::Handle<v8::Function> attributeChanged)
     53 {
     54     v8::Isolate* isolate = toIsolate(executionContext);
     55     // A given object can only be used as a Custom Element prototype
     56     // once; see customElementIsInterfacePrototypeObject
     57 #define SET_HIDDEN_PROPERTY(Value, Name) \
     58     ASSERT(prototype->GetHiddenValue(V8HiddenPropertyName::customElement##Name(isolate)).IsEmpty()); \
     59     if (!Value.IsEmpty()) \
     60         prototype->SetHiddenValue(V8HiddenPropertyName::customElement##Name(isolate), Value);
     61 
     62     CALLBACK_LIST(SET_HIDDEN_PROPERTY)
     63 #undef SET_HIDDEN_PROPERTY
     64 
     65     return adoptRef(new V8CustomElementLifecycleCallbacks(executionContext, prototype, created, attached, detached, attributeChanged));
     66 }
     67 
     68 static CustomElementLifecycleCallbacks::CallbackType flagSet(v8::Handle<v8::Function> attached, v8::Handle<v8::Function> detached, v8::Handle<v8::Function> attributeChanged)
     69 {
     70     // V8 Custom Elements always run created to swizzle prototypes.
     71     int flags = CustomElementLifecycleCallbacks::Created;
     72 
     73     if (!attached.IsEmpty())
     74         flags |= CustomElementLifecycleCallbacks::Attached;
     75 
     76     if (!detached.IsEmpty())
     77         flags |= CustomElementLifecycleCallbacks::Detached;
     78 
     79     if (!attributeChanged.IsEmpty())
     80         flags |= CustomElementLifecycleCallbacks::AttributeChanged;
     81 
     82     return CustomElementLifecycleCallbacks::CallbackType(flags);
     83 }
     84 
     85 template <typename T>
     86 static void weakCallback(const v8::WeakCallbackData<T, ScopedPersistent<T> >& data)
     87 {
     88     data.GetParameter()->clear();
     89 }
     90 
     91 V8CustomElementLifecycleCallbacks::V8CustomElementLifecycleCallbacks(ExecutionContext* executionContext, v8::Handle<v8::Object> prototype, v8::Handle<v8::Function> created, v8::Handle<v8::Function> attached, v8::Handle<v8::Function> detached, v8::Handle<v8::Function> attributeChanged)
     92     : CustomElementLifecycleCallbacks(flagSet(attached, detached, attributeChanged))
     93     , ActiveDOMCallback(executionContext)
     94     , m_owner(0)
     95     , m_world(DOMWrapperWorld::current())
     96     , m_prototype(toIsolate(executionContext), prototype)
     97     , m_created(toIsolate(executionContext), created)
     98     , m_attached(toIsolate(executionContext), attached)
     99     , m_detached(toIsolate(executionContext), detached)
    100     , m_attributeChanged(toIsolate(executionContext), attributeChanged)
    101 {
    102     m_prototype.setWeak(&m_prototype, weakCallback<v8::Object>);
    103 
    104 #define MAKE_WEAK(Var, _) \
    105     if (!m_##Var.isEmpty()) \
    106         m_##Var.setWeak(&m_##Var, weakCallback<v8::Function>);
    107 
    108     CALLBACK_LIST(MAKE_WEAK)
    109 #undef MAKE_WEAK
    110 }
    111 
    112 V8PerContextData* V8CustomElementLifecycleCallbacks::creationContextData()
    113 {
    114     if (!executionContext())
    115         return 0;
    116 
    117     v8::Handle<v8::Context> context = toV8Context(executionContext(), m_world.get());
    118     if (context.IsEmpty())
    119         return 0;
    120 
    121     return V8PerContextData::from(context);
    122 }
    123 
    124 V8CustomElementLifecycleCallbacks::~V8CustomElementLifecycleCallbacks()
    125 {
    126     if (!m_owner)
    127         return;
    128 
    129     v8::HandleScope handleScope(toIsolate(executionContext()));
    130     if (V8PerContextData* perContextData = creationContextData())
    131         perContextData->clearCustomElementBinding(m_owner);
    132 }
    133 
    134 bool V8CustomElementLifecycleCallbacks::setBinding(CustomElementDefinition* owner, PassOwnPtr<CustomElementBinding> binding)
    135 {
    136     ASSERT(!m_owner);
    137 
    138     V8PerContextData* perContextData = creationContextData();
    139     if (!perContextData)
    140         return false;
    141 
    142     m_owner = owner;
    143 
    144     // Bindings retrieve the prototype when needed from per-context data.
    145     perContextData->addCustomElementBinding(owner, binding);
    146 
    147     return true;
    148 }
    149 
    150 void V8CustomElementLifecycleCallbacks::created(Element* element)
    151 {
    152     if (!canInvokeCallback())
    153         return;
    154 
    155     element->setCustomElementState(Element::Upgraded);
    156 
    157     v8::Isolate* isolate = toIsolate(executionContext());
    158     v8::HandleScope handleScope(isolate);
    159     v8::Handle<v8::Context> context = toV8Context(executionContext(), m_world.get());
    160     if (context.IsEmpty())
    161         return;
    162 
    163     v8::Context::Scope scope(context);
    164 
    165     v8::Handle<v8::Object> receiver = DOMDataStore::current(isolate).get<V8Element>(element, isolate);
    166     if (!receiver.IsEmpty()) {
    167         // Swizzle the prototype of the existing wrapper. We don't need to
    168         // worry about non-existent wrappers; they will get the right
    169         // prototype when wrapped.
    170         v8::Handle<v8::Object> prototype = m_prototype.newLocal(isolate);
    171         if (prototype.IsEmpty())
    172             return;
    173         receiver->SetPrototype(prototype);
    174     }
    175 
    176     v8::Handle<v8::Function> callback = m_created.newLocal(isolate);
    177     if (callback.IsEmpty())
    178         return;
    179 
    180     if (receiver.IsEmpty())
    181         receiver = toV8(element, context->Global(), isolate).As<v8::Object>();
    182 
    183     ASSERT(!receiver.IsEmpty());
    184 
    185     v8::TryCatch exceptionCatcher;
    186     exceptionCatcher.SetVerbose(true);
    187     ScriptController::callFunction(executionContext(), callback, receiver, 0, 0, isolate);
    188 }
    189 
    190 void V8CustomElementLifecycleCallbacks::attached(Element* element)
    191 {
    192     call(m_attached, element);
    193 }
    194 
    195 void V8CustomElementLifecycleCallbacks::detached(Element* element)
    196 {
    197     call(m_detached, element);
    198 }
    199 
    200 void V8CustomElementLifecycleCallbacks::attributeChanged(Element* element, const AtomicString& name, const AtomicString& oldValue, const AtomicString& newValue)
    201 {
    202     if (!canInvokeCallback())
    203         return;
    204 
    205     v8::Isolate* isolate = toIsolate(executionContext());
    206     v8::HandleScope handleScope(isolate);
    207     v8::Handle<v8::Context> context = toV8Context(executionContext(), m_world.get());
    208     if (context.IsEmpty())
    209         return;
    210 
    211     v8::Context::Scope scope(context);
    212 
    213     v8::Handle<v8::Object> receiver = toV8(element, context->Global(), isolate).As<v8::Object>();
    214     ASSERT(!receiver.IsEmpty());
    215 
    216     v8::Handle<v8::Function> callback = m_attributeChanged.newLocal(isolate);
    217     if (callback.IsEmpty())
    218         return;
    219 
    220     v8::Handle<v8::Value> argv[] = {
    221         v8String(isolate, name),
    222         oldValue.isNull() ? v8::Handle<v8::Value>(v8::Null(isolate)) : v8::Handle<v8::Value>(v8String(isolate, oldValue)),
    223         newValue.isNull() ? v8::Handle<v8::Value>(v8::Null(isolate)) : v8::Handle<v8::Value>(v8String(isolate, newValue))
    224     };
    225 
    226     v8::TryCatch exceptionCatcher;
    227     exceptionCatcher.SetVerbose(true);
    228     ScriptController::callFunction(executionContext(), callback, receiver, WTF_ARRAY_LENGTH(argv), argv, isolate);
    229 }
    230 
    231 void V8CustomElementLifecycleCallbacks::call(const ScopedPersistent<v8::Function>& weakCallback, Element* element)
    232 {
    233     if (!canInvokeCallback())
    234         return;
    235 
    236     v8::HandleScope handleScope(toIsolate(executionContext()));
    237     v8::Handle<v8::Context> context = toV8Context(executionContext(), m_world.get());
    238     if (context.IsEmpty())
    239         return;
    240 
    241     v8::Context::Scope scope(context);
    242     v8::Isolate* isolate = context->GetIsolate();
    243 
    244     v8::Handle<v8::Function> callback = weakCallback.newLocal(isolate);
    245     if (callback.IsEmpty())
    246         return;
    247 
    248     v8::Handle<v8::Object> receiver = toV8(element, context->Global(), isolate).As<v8::Object>();
    249     ASSERT(!receiver.IsEmpty());
    250 
    251     v8::TryCatch exceptionCatcher;
    252     exceptionCatcher.SetVerbose(true);
    253     ScriptController::callFunction(executionContext(), callback, receiver, 0, 0, isolate);
    254 }
    255 
    256 } // namespace WebCore
    257