• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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)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 
flagSet(v8::Handle<v8::Function> attached,v8::Handle<v8::Function> detached,v8::Handle<v8::Function> attributeChanged)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>
weakCallback(const v8::WeakCallbackData<T,ScopedPersistent<T>> & data)86 static void weakCallback(const v8::WeakCallbackData<T, ScopedPersistent<T> >& data)
87 {
88     data.GetParameter()->clear();
89 }
90 
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)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 
creationContextData()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 
~V8CustomElementLifecycleCallbacks()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 
setBinding(CustomElementDefinition * owner,PassOwnPtr<CustomElementBinding> binding)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 
created(Element * element)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 
attached(Element * element)190 void V8CustomElementLifecycleCallbacks::attached(Element* element)
191 {
192     call(m_attached, element);
193 }
194 
detached(Element * element)195 void V8CustomElementLifecycleCallbacks::detached(Element* element)
196 {
197     call(m_detached, element);
198 }
199 
attributeChanged(Element * element,const AtomicString & name,const AtomicString & oldValue,const AtomicString & newValue)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 
call(const ScopedPersistent<v8::Function> & weakCallback,Element * element)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