Merge from Chromium at DEPS revision r212014
This commit was generated by merge_to_master.py.
Change-Id: Ib07d03553a1e81485ac4ffc92b91e977c4875409
diff --git a/Source/bindings/v8/V8CustomElementLifecycleCallbacks.cpp b/Source/bindings/v8/V8CustomElementLifecycleCallbacks.cpp
new file mode 100644
index 0000000..baf8d90
--- /dev/null
+++ b/Source/bindings/v8/V8CustomElementLifecycleCallbacks.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "bindings/v8/V8CustomElementLifecycleCallbacks.h"
+
+#include "V8Element.h"
+#include "bindings/v8/DOMDataStore.h"
+#include "bindings/v8/ScriptController.h"
+#include "bindings/v8/V8Binding.h"
+#include "bindings/v8/V8HiddenPropertyName.h"
+#include "core/dom/ScriptExecutionContext.h"
+#include "wtf/PassRefPtr.h"
+
+namespace WebCore {
+
+#define CALLBACK_LIST(V) \
+ V(created, Created) \
+ V(enteredDocument, EnteredDocument) \
+ V(leftDocument, LeftDocument) \
+ V(attributeChanged, AttributeChanged)
+
+PassRefPtr<V8CustomElementLifecycleCallbacks> V8CustomElementLifecycleCallbacks::create(ScriptExecutionContext* scriptExecutionContext, v8::Handle<v8::Object> prototype, v8::Handle<v8::Function> created, v8::Handle<v8::Function> enteredDocument, v8::Handle<v8::Function> leftDocument, v8::Handle<v8::Function> attributeChanged)
+{
+ // A given object can only be used as a Custom Element prototype
+ // once; see customElementIsInterfacePrototypeObject
+#define SET_HIDDEN_PROPERTY(Value, Name) \
+ ASSERT(prototype->GetHiddenValue(V8HiddenPropertyName::customElement##Name()).IsEmpty()); \
+ if (!Value.IsEmpty()) \
+ prototype->SetHiddenValue(V8HiddenPropertyName::customElement##Name(), Value);
+
+ CALLBACK_LIST(SET_HIDDEN_PROPERTY)
+#undef SET_HIDDEN_PROPERTY
+
+ return adoptRef(new V8CustomElementLifecycleCallbacks(scriptExecutionContext, prototype, created, enteredDocument, leftDocument, attributeChanged));
+}
+
+static CustomElementLifecycleCallbacks::CallbackType flagSet(v8::Handle<v8::Function> enteredDocument, v8::Handle<v8::Function> leftDocument, v8::Handle<v8::Function> attributeChanged)
+{
+ // V8 Custom Elements always run created to swizzle prototypes.
+ int flags = CustomElementLifecycleCallbacks::Created;
+
+ if (!enteredDocument.IsEmpty())
+ flags |= CustomElementLifecycleCallbacks::EnteredDocument;
+
+ if (!leftDocument.IsEmpty())
+ flags |= CustomElementLifecycleCallbacks::LeftDocument;
+
+ if (!attributeChanged.IsEmpty())
+ flags |= CustomElementLifecycleCallbacks::AttributeChanged;
+
+ return CustomElementLifecycleCallbacks::CallbackType(flags);
+}
+
+template <typename T>
+static void weakCallback(v8::Isolate*, v8::Persistent<T>*, ScopedPersistent<T>* handle)
+{
+ handle->clear();
+}
+
+V8CustomElementLifecycleCallbacks::V8CustomElementLifecycleCallbacks(ScriptExecutionContext* scriptExecutionContext, v8::Handle<v8::Object> prototype, v8::Handle<v8::Function> created, v8::Handle<v8::Function> enteredDocument, v8::Handle<v8::Function> leftDocument, v8::Handle<v8::Function> attributeChanged)
+ : CustomElementLifecycleCallbacks(flagSet(enteredDocument, leftDocument, attributeChanged))
+ , ActiveDOMCallback(scriptExecutionContext)
+ , m_world(DOMWrapperWorld::current())
+ , m_prototype(prototype)
+ , m_created(created)
+ , m_enteredDocument(enteredDocument)
+ , m_leftDocument(leftDocument)
+ , m_attributeChanged(attributeChanged)
+{
+ m_prototype.makeWeak(&m_prototype, weakCallback<v8::Object>);
+
+#define MAKE_WEAK(Var, _) \
+ if (!m_##Var.isEmpty()) \
+ m_##Var.makeWeak(&m_##Var, weakCallback<v8::Function>);
+
+ CALLBACK_LIST(MAKE_WEAK)
+#undef MAKE_WEAK
+}
+
+void V8CustomElementLifecycleCallbacks::created(Element* element)
+{
+ if (!canInvokeCallback())
+ return;
+
+ element->setIsUpgradedCustomElement();
+
+ v8::HandleScope handleScope;
+ v8::Handle<v8::Context> context = toV8Context(scriptExecutionContext(), m_world.get());
+ if (context.IsEmpty())
+ return;
+
+ v8::Context::Scope scope(context);
+ v8::Isolate* isolate = context->GetIsolate();
+
+ v8::Handle<v8::Object> receiver = DOMDataStore::current(isolate)->get<V8Element>(element);
+ if (!receiver.IsEmpty()) {
+ // Swizzle the prototype of the existing wrapper. We don't need to
+ // worry about non-existent wrappers; they will get the right
+ // prototype when wrapped.
+ v8::Handle<v8::Object> prototype = m_prototype.newLocal(isolate);
+ if (prototype.IsEmpty())
+ return;
+ receiver->SetPrototype(prototype);
+ }
+
+ v8::Handle<v8::Function> callback = m_created.newLocal(isolate);
+ if (callback.IsEmpty())
+ return;
+
+ if (receiver.IsEmpty())
+ receiver = toV8(element, context->Global(), isolate).As<v8::Object>();
+
+ ASSERT(!receiver.IsEmpty());
+
+ v8::TryCatch exceptionCatcher;
+ exceptionCatcher.SetVerbose(true);
+ ScriptController::callFunctionWithInstrumentation(scriptExecutionContext(), callback, receiver, 0, 0);
+}
+
+void V8CustomElementLifecycleCallbacks::enteredDocument(Element* element)
+{
+ call(m_enteredDocument, element);
+}
+
+void V8CustomElementLifecycleCallbacks::leftDocument(Element* element)
+{
+ call(m_leftDocument, element);
+}
+
+void V8CustomElementLifecycleCallbacks::attributeChanged(Element* element, const AtomicString& name, const AtomicString& oldValue, const AtomicString& newValue)
+{
+ if (!canInvokeCallback())
+ return;
+
+ v8::HandleScope handleScope;
+ v8::Handle<v8::Context> context = toV8Context(scriptExecutionContext(), m_world.get());
+ if (context.IsEmpty())
+ return;
+
+ v8::Context::Scope scope(context);
+ v8::Isolate* isolate = context->GetIsolate();
+
+ v8::Handle<v8::Object> receiver = toV8(element, context->Global(), isolate).As<v8::Object>();
+ ASSERT(!receiver.IsEmpty());
+
+ v8::Handle<v8::Function> callback = m_attributeChanged.newLocal(isolate);
+ if (callback.IsEmpty())
+ return;
+
+ v8::Handle<v8::Value> argv[] = {
+ v8String(name, isolate),
+ oldValue.isNull() ? v8::Handle<v8::Value>(v8::Null()) : v8::Handle<v8::Value>(v8String(oldValue, isolate)),
+ newValue.isNull() ? v8::Handle<v8::Value>(v8::Null()) : v8::Handle<v8::Value>(v8String(newValue, isolate))
+ };
+
+ v8::TryCatch exceptionCatcher;
+ exceptionCatcher.SetVerbose(true);
+ ScriptController::callFunctionWithInstrumentation(scriptExecutionContext(), callback, receiver, WTF_ARRAY_LENGTH(argv), argv);
+}
+
+void V8CustomElementLifecycleCallbacks::call(const ScopedPersistent<v8::Function>& weakCallback, Element* element)
+{
+ if (!canInvokeCallback())
+ return;
+
+ v8::HandleScope handleScope;
+ v8::Handle<v8::Context> context = toV8Context(scriptExecutionContext(), m_world.get());
+ if (context.IsEmpty())
+ return;
+
+ v8::Context::Scope scope(context);
+ v8::Isolate* isolate = context->GetIsolate();
+
+ v8::Handle<v8::Function> callback = weakCallback.newLocal(isolate);
+ if (callback.IsEmpty())
+ return;
+
+ v8::Handle<v8::Object> receiver = toV8(element, context->Global(), isolate).As<v8::Object>();
+ ASSERT(!receiver.IsEmpty());
+
+ v8::TryCatch exceptionCatcher;
+ exceptionCatcher.SetVerbose(true);
+ ScriptController::callFunctionWithInstrumentation(scriptExecutionContext(), callback, receiver, 0, 0);
+}
+
+} // namespace WebCore