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