[Custom Elements] Implement bare-bone document.register()
authormorrita@google.com <morrita@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 24 Feb 2013 13:21:42 +0000 (13:21 +0000)
committermorrita@google.com <morrita@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 24 Feb 2013 13:21:42 +0000 (13:21 +0000)
https://bugs.webkit.org/show_bug.cgi?id=100229

Reviewed by Adam Barth.

Source/WebCore:

This change implements a prefixed version of document.register(), with minimal feature support.
- The feature is guarded by ENABLE(CUSTOM_ELEMENTS) and RuntimeEnabledFeatures::customDOMElementsEnabled().
- This bare-bone version only recognizes "name" and "prototype" parameters. It doesn't support default value of "prototype" parameter.
- Currently only V8 is supported. JSC binding needs its own binding implementation.

= Major new classes under dom/:

The dom module gets two new classes:
- CustomElementConstructor: A return value of document.register()
  which holds the custom element definition.
- CustomElementRegistry: A collection of CustomElementConstructor objects.
  CustomElementRegistry instance is created per Document and is owned by the Document.

CustomElementConstructor knows the definition of each custom
element, which is registered by document.register(). The name and
other options are held by this object. CustomElementRegistry owns a set
of the registered constructors. The registry guarantees invariants
like validity and uniqueness of the element names.

= A change on make_names.pl

This change tweaks make_names.pl (or generated HTMLElementFactory)
to hook the creations of unknown elements. Some of element names
which come to the fallback path can be one of registered custom
element.

= [V8WrapAsFunction] extended attribute:

The document.register() API returns a constructor
function. However, the V8 binding currently doesn't support it. To
make it possible, this change introduces "V8WrapAsFunction"
extended attribute for annotating CustomElementConstructor IDL
interface.

V8WrapAsFunction wraps the annotated interface with a JavaScript
function, which calls the original object as a function, or as a
constructor depends on the context.

With this wrapper function, there are two levels of indirection
between native C++ object and author-visible JS function:

[JS Adaptor Function] <-(hidden property)-> [JS Wrapper Object] -(internal field)-> [C++ Native object]

The code generator generates the binding code which deals with
this indirection.  Also, there is a set of helper functions in
V8AdaptorFunction.h/cpp which takes care of this indirection.
V8DOMWrapper.cpp/h works as a facade for these APIs and is used from
the generated code.

This redundancy comes from limitations of both V8 bindings and V8
embedding API. See bug 108138 for details.

= V8HTMLCustomElement

Unlike built-in HTML elements, any custom element has no
corresponding C++ class. Instead, document.register() should allow
passing a prototype object for the elements being registered.

V8HTMLCustomElement handles this lack of native class.  It behaves
like a native side proxy of non-native HTMLElement subclasses.  It
connects each custom element to an appropriate native element,
which is HTMLElement at this time. This restriction will be
relaxed later. See Bug 110436 for details.

= Custom DOM elements and multiple worlds

In this patch, custom element registration and instantiation is not allowed
in non-main world and document.register() API just fails there.

Reviewed by Adam Barth.

Tests: fast/dom/custom/document-register-basic.html
       fast/dom/custom/document-register-reentrant-null-constructor.html
       fast/dom/custom/document-register-reentrant-returning-fake.html
       fast/dom/custom/document-register-reentrant-throwing-constructor.html

* DerivedSources.make:
* WebCore.gypi:
* bindings/generic/RuntimeEnabledFeatures.cpp:
* bindings/generic/RuntimeEnabledFeatures.h:
(RuntimeEnabledFeatures):
(WebCore::RuntimeEnabledFeatures::customDOMElementsEnabled):
(WebCore::RuntimeEnabledFeatures::setCustomDOMElements):
* bindings/scripts/CodeGeneratorV8.pm:
(GenerateHeader):
* bindings/scripts/IDLAttributes.txt:
* bindings/v8/CustomElementHelpers.cpp: Added.
(WebCore::CustomElementHelpers::initializeConstructorWrapper):
(WebCore::hasNoBuiltinsInPrototype):
(WebCore::CustomElementHelpers::isValidPrototypeParameter):
(WebCore::CustomElementHelpers::isFeatureAllowed):
* bindings/v8/CustomElementHelpers.h: Copied from Source/WebCore/bindings/v8/V8HiddenPropertyName.h.
(CustomElementHelpers):
* bindings/v8/V8AdaptorFunction.cpp: Added.
(WebCore::V8AdaptorFunction::getTemplate):
(WebCore::V8AdaptorFunction::configureTemplate):
(WebCore::V8AdaptorFunction::invocationCallback):
(WebCore::V8AdaptorFunction::wrap):
* bindings/v8/V8AdaptorFunction.h: Added.
(V8AdaptorFunction):
(WebCore::V8AdaptorFunction::unwrap):
(WebCore::V8AdaptorFunction::get):
* bindings/v8/V8DOMConfiguration.cpp:
(WebCore::V8DOMConfiguration::configureTemplate):
* bindings/v8/V8DOMWrapper.cpp:
(WebCore::V8DOMWrapper::toFunction):
(WebCore::V8DOMWrapper::fromFunction):
* bindings/v8/V8DOMWrapper.h:
(V8DOMWrapper):
* bindings/v8/V8HTMLCustomElement.cpp: Added.
(WebCore::V8HTMLCustomElement::createWrapper):
* bindings/v8/V8HTMLCustomElement.h: Copied from Source/WebCore/bindings/v8/V8HiddenPropertyName.h.
(V8HTMLCustomElement):
(WebCore::V8HTMLCustomElement::toV8):
(WebCore::HTMLCustomElement::toV8):
* bindings/v8/V8HiddenPropertyName.h:
* bindings/v8/custom/V8CustomElementConstructorCustom.cpp: Added.
(WebCore::V8CustomElementConstructor::callAsFunctionCallback):
* dom/CustomElementConstructor.cpp: Copied from Source/WebCore/bindings/v8/V8HiddenPropertyName.h.
(WebCore::CustomElementConstructor::create):
(WebCore::CustomElementConstructor::CustomElementConstructor):
(WebCore::CustomElementConstructor::~CustomElementConstructor):
(WebCore::CustomElementConstructor::createElement):
* dom/CustomElementConstructor.h: Copied from Source/WebCore/bindings/v8/V8HiddenPropertyName.h.
(CustomElementConstructor):
(WebCore::CustomElementConstructor::document):
(WebCore::CustomElementConstructor::tagName):
(WebCore::CustomElementConstructor::name):
* dom/CustomElementConstructor.idl: Added.
* dom/CustomElementRegistry.cpp: Added.
(WebCore::CustomElementRegistry::CustomElementRegistry):
(WebCore::CustomElementRegistry::~CustomElementRegistry):
(WebCore::CustomElementRegistry::constructorOf):
(WebCore::CustomElementRegistry::isValidName):
(WebCore::CustomElementRegistry::registerElement):
(WebCore::CustomElementRegistry::find):
(WebCore::CustomElementRegistry::createElement):
(WebCore::CustomElementRegistry::document):
* dom/CustomElementRegistry.h: Added.
(CustomElementRegistry):
* dom/Document.cpp:
(WebCore::Document::removedLastRef):
(WebCore::Document::registerElement):
(WebCore::Document::registry):
* dom/Document.h:
(Document):
* dom/make_names.pl:
(printWrapperFactoryCppFile):
* html/HTMLDocument.idl:

Source/WebKit/chromium:

Added enableCustomDOMElements flag.

* features.gypi:
* public/WebRuntimeFeatures.h:
(WebRuntimeFeatures):
* src/WebRuntimeFeatures.cpp:
(WebKit::WebRuntimeFeatures::enableCustomDOMElements):
(WebKit):
(WebKit::WebRuntimeFeatures::isCustomDOMElementsEnabled):

Tools:

Added enableCustomDOMElements flag.

* DumpRenderTree/chromium/TestShell.cpp:
(TestShell::TestShell):

LayoutTests:

* fast/dom/custom/document-register-basic-expected.txt: Added.
* fast/dom/custom/document-register-basic.html: Added.
* fast/dom/custom/document-register-reentrant-null-constructor-expected.txt: Added.
* fast/dom/custom/document-register-reentrant-null-constructor.html: Added.
* fast/dom/custom/document-register-reentrant-returning-fake-expected.txt: Added.
* fast/dom/custom/document-register-reentrant-returning-fake.html: Added.
* fast/dom/custom/document-register-reentrant-throwing-constructor-expected.txt: Added.
* fast/dom/custom/document-register-reentrant-throwing-constructor.html: Added.
* fast/dom/custom/resources/document-register-fuzz.js: Added.
* platform/mac/TestExpectations:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@143865 268f45cc-cd09-0410-ab3c-d52691b4dbfc

44 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/dom/custom/document-register-basic-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/custom/document-register-basic.html [new file with mode: 0644]
LayoutTests/fast/dom/custom/document-register-reentrant-null-constructor-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/custom/document-register-reentrant-null-constructor.html [new file with mode: 0644]
LayoutTests/fast/dom/custom/document-register-reentrant-returning-fake-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/custom/document-register-reentrant-returning-fake.html [new file with mode: 0644]
LayoutTests/fast/dom/custom/document-register-reentrant-throwing-constructor-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/custom/document-register-reentrant-throwing-constructor.html [new file with mode: 0644]
LayoutTests/fast/dom/custom/resources/document-register-fuzz.js [new file with mode: 0644]
LayoutTests/platform/mac/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/DerivedSources.make
Source/WebCore/WebCore.gypi
Source/WebCore/bindings/generic/RuntimeEnabledFeatures.cpp
Source/WebCore/bindings/generic/RuntimeEnabledFeatures.h
Source/WebCore/bindings/scripts/CodeGeneratorV8.pm
Source/WebCore/bindings/scripts/IDLAttributes.txt
Source/WebCore/bindings/v8/CustomElementHelpers.cpp [new file with mode: 0644]
Source/WebCore/bindings/v8/CustomElementHelpers.h [new file with mode: 0644]
Source/WebCore/bindings/v8/V8AdaptorFunction.cpp [new file with mode: 0644]
Source/WebCore/bindings/v8/V8AdaptorFunction.h [new file with mode: 0644]
Source/WebCore/bindings/v8/V8DOMConfiguration.cpp
Source/WebCore/bindings/v8/V8DOMWrapper.cpp
Source/WebCore/bindings/v8/V8DOMWrapper.h
Source/WebCore/bindings/v8/V8HTMLCustomElement.cpp [new file with mode: 0644]
Source/WebCore/bindings/v8/V8HTMLCustomElement.h [new file with mode: 0644]
Source/WebCore/bindings/v8/V8HiddenPropertyName.h
Source/WebCore/bindings/v8/custom/V8CustomElementConstructorCustom.cpp [new file with mode: 0644]
Source/WebCore/dom/CustomElementConstructor.cpp [new file with mode: 0644]
Source/WebCore/dom/CustomElementConstructor.h [new file with mode: 0644]
Source/WebCore/dom/CustomElementConstructor.idl [new file with mode: 0644]
Source/WebCore/dom/CustomElementRegistry.cpp [new file with mode: 0644]
Source/WebCore/dom/CustomElementRegistry.h [new file with mode: 0644]
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/dom/make_names.pl
Source/WebCore/html/HTMLDocument.idl
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/features.gypi
Source/WebKit/chromium/public/WebRuntimeFeatures.h
Source/WebKit/chromium/src/WebRuntimeFeatures.cpp
Tools/ChangeLog
Tools/DumpRenderTree/chromium/TestShell.cpp

index 0b0896c..3ff72c1 100644 (file)
@@ -1,3 +1,21 @@
+2013-02-24  Hajime Morrita  <morrita@google.com>
+
+        [Custom Elements] Implement bare-bone document.register()
+        https://bugs.webkit.org/show_bug.cgi?id=100229
+
+        Reviewed by Adam Barth.
+
+        * fast/dom/custom/document-register-basic-expected.txt: Added.
+        * fast/dom/custom/document-register-basic.html: Added.
+        * fast/dom/custom/document-register-reentrant-null-constructor-expected.txt: Added.
+        * fast/dom/custom/document-register-reentrant-null-constructor.html: Added.
+        * fast/dom/custom/document-register-reentrant-returning-fake-expected.txt: Added.
+        * fast/dom/custom/document-register-reentrant-returning-fake.html: Added.
+        * fast/dom/custom/document-register-reentrant-throwing-constructor-expected.txt: Added.
+        * fast/dom/custom/document-register-reentrant-throwing-constructor.html: Added.
+        * fast/dom/custom/resources/document-register-fuzz.js: Added.
+        * platform/mac/TestExpectations:
+
 2013-02-24  Benjamin Poulain  <benjamin@webkit.org>
 
         Add timeout to the Chromium expectation of state-url-sets-links-visited.html
diff --git a/LayoutTests/fast/dom/custom/document-register-basic-expected.txt b/LayoutTests/fast/dom/custom/document-register-basic-expected.txt
new file mode 100644 (file)
index 0000000..26b6219
--- /dev/null
@@ -0,0 +1,46 @@
+Testing document.register() basic behaviors.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.register('foo', createRegisterParamters()) threw exception Error: InvalidCharacterError: DOM Exception 5.
+PASS document.register('xfoo', createRegisterParamters()) threw exception Error: InvalidCharacterError: DOM Exception 5.
+PASS document.register('missing-glyph', createRegisterParamters()) threw exception Error: InvalidCharacterError: DOM Exception 5.
+PASS typeof fooConstructor is 'function'
+PASS fooConstructor.prototype.__proto__ is HTMLElement.prototype
+PASS fooConstructor.prototype.thisIsPrototype is true
+PASS document.register('x-foo', createRegisterParamters()) threw exception Error: InvalidStateError: DOM Exception 11.
+PASS document.register('X-FOO', createRegisterParamters()) threw exception Error: InvalidStateError: DOM Exception 11.
+PASS document.register('x-bad-a', { prototype: HTMLElement.prototype }) threw exception Error: InvalidStateError: DOM Exception 11.
+PASS document.register('x-bad-b', { prototype: {} }) threw exception Error: InvalidStateError: DOM Exception 11.
+PASS document.register('x-bad-c', { prototype: Object.create(Document.prototype) }) threw exception Error: InvalidStateError: DOM Exception 11.
+PASS document.register('x-bad-d', { prototype: Object.create(HTMLSpanElement.prototype) }) threw exception Error: InvalidStateError: DOM Exception 11.
+PASS fooConstructor() threw exception TypeError: DOM object constructor cannot be called as a function..
+PASS createdFoo.__proto__ is fooConstructor.prototype
+PASS createdFoo.constructor is fooConstructor
+PASS createdFoo.tagName is 'X-FOO'
+PASS createdFoo.textContent is 'Hello'
+PASS createdFoo.lastChild is childDiv
+PASS parsedFoo.__proto__ is fooConstructor.prototype
+PASS parsedFoo.tagName is 'X-FOO'
+PASS parsedFoo.someProperty is container.firstChild.someProperty
+PASS createdBar.tagName is 'X-BAR'
+PASS createdBaz.tagName is 'X-BAZ'
+PASS createdBaz.thisIsPrototype is true
+PASS createdBaz.thisIsAlsoPrototype is true
+PASS createdUpperBar.constructor is barConstructor
+PASS createdUpperBar.tagName is 'X-BAR'
+PASS createdMixedBar.constructor is barConstructor
+PASS createdMixedBar.tagName is 'X-BAR'
+PASS container.firstChild.constructor is barConstructor
+PASS container.firstChild.tagName is 'X-BAR'
+PASS container.lastChild.constructor is barConstructor
+PASS container.lastChild.tagName is 'X-BAR'
+PASS (new (document.register('y-bar', createRegisterParamters()))()).tagName is 'Y-BAR'
+PASS (new (document.register('yz-bar', createRegisterParamters()))()).tagName is 'YZ-BAR'
+PASS (new (document.register('y-z-bar', createRegisterParamters()))()).tagName is 'Y-Z-BAR'
+PASS (new (document.register('y--bar', createRegisterParamters()))()).tagName is 'Y--BAR'
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/custom/document-register-basic.html b/LayoutTests/fast/dom/custom/document-register-basic.html
new file mode 100644 (file)
index 0000000..105778c
--- /dev/null
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<div id="container"></div>
+<script>
+description("Testing document.register() basic behaviors.");
+
+if (window.testRunner)
+    testRunner.dumpAsText();
+document.register = document.register || document.webkitRegister;
+
+function createRegisterParamters()
+{
+    return {
+        prototype: Object.create(HTMLElement.prototype, { thisIsPrototype: { value: true } })
+    };
+}
+
+// Invalid names
+shouldThrow("document.register('foo', createRegisterParamters())", "'Error: InvalidCharacterError: DOM Exception 5'");
+shouldThrow("document.register('xfoo', createRegisterParamters())", "'Error: InvalidCharacterError: DOM Exception 5'");
+shouldThrow("document.register('missing-glyph', createRegisterParamters())", "'Error: InvalidCharacterError: DOM Exception 5'");
+
+var fooConstructor = document.register("x-foo", createRegisterParamters());
+shouldBe("typeof fooConstructor", "'function'");
+shouldBe("fooConstructor.prototype.__proto__", "HTMLElement.prototype");
+shouldBeTrue("fooConstructor.prototype.thisIsPrototype");
+
+// Name conflicts
+shouldThrow("document.register('x-foo', createRegisterParamters())", "'Error: InvalidStateError: DOM Exception 11'");
+shouldThrow("document.register('X-FOO', createRegisterParamters())", "'Error: InvalidStateError: DOM Exception 11'");
+
+// Bad prototype: constructor property exists
+shouldThrow("document.register('x-bad-a', { prototype: HTMLElement.prototype })", "'Error: InvalidStateError: DOM Exception 11'");
+// Bad prototype: missing __proto__
+shouldThrow("document.register('x-bad-b', { prototype: {} })", "'Error: InvalidStateError: DOM Exception 11'");
+// Bad prototype: wrong __proto__
+shouldThrow("document.register('x-bad-c', { prototype: Object.create(Document.prototype) })", "'Error: InvalidStateError: DOM Exception 11'");
+// Bug 110436 - Elements other than HTMLEmement should be allowed as a superclass.
+shouldThrow("document.register('x-bad-d', { prototype: Object.create(HTMLSpanElement.prototype) })", "'Error: InvalidStateError: DOM Exception 11'");
+// Call as function
+shouldThrow("fooConstructor()", "'TypeError: DOM object constructor cannot be called as a function.'")
+
+// Constructor initiated instantiation
+var createdFoo = new fooConstructor();
+
+// JS built-in properties
+shouldBe("createdFoo.__proto__", "fooConstructor.prototype");
+shouldBe("createdFoo.constructor", "fooConstructor");
+
+// Native getter
+shouldBe("createdFoo.tagName", "'X-FOO'");
+
+// Native setter
+createdFoo.innerHTML = "Hello";
+shouldBe("createdFoo.textContent", "'Hello'");
+
+// Native method
+var childDiv = document.createElement("div");
+createdFoo.appendChild(childDiv);
+shouldBe("createdFoo.lastChild", "childDiv");
+
+// Parser initiated instantiation
+var container = document.getElementById("container");
+container.innerHTML = "<x-foo></x-foo>";
+parsedFoo = container.firstChild;
+
+shouldBe("parsedFoo.__proto__", "fooConstructor.prototype");
+shouldBe("parsedFoo.tagName", "'X-FOO'");
+
+// Ensuring the wrapper is retained
+parsedFoo.someProperty = "hello";
+shouldBe("parsedFoo.someProperty", "container.firstChild.someProperty");
+
+// Having another constructor
+var barConstructor = document.register("x-bar", createRegisterParamters());
+var createdBar = new barConstructor();
+shouldBe("createdBar.tagName", "'X-BAR'");
+
+// Having a subclass
+var bazConstructor = document.register("x-baz", { prototype: Object.create(fooConstructor.prototype, { thisIsAlsoPrototype: { value: true } }) });
+var createdBaz = new bazConstructor();
+shouldBe("createdBaz.tagName", "'X-BAZ'");
+shouldBeTrue("createdBaz.thisIsPrototype");
+shouldBeTrue("createdBaz.thisIsAlsoPrototype");
+
+// With irregular cases
+var createdUpperBar = document.createElement("X-BAR");
+var createdMixedBar = document.createElement("X-Bar");
+shouldBe("createdUpperBar.constructor", "barConstructor");
+shouldBe("createdUpperBar.tagName", "'X-BAR'");
+shouldBe("createdMixedBar.constructor", "barConstructor");
+shouldBe("createdMixedBar.tagName", "'X-BAR'");
+
+container.innerHTML = "<X-BAR></X-BAR><X-Bar></X-Bar>";
+shouldBe("container.firstChild.constructor", "barConstructor");
+shouldBe("container.firstChild.tagName", "'X-BAR'");
+shouldBe("container.lastChild.constructor", "barConstructor");
+shouldBe("container.lastChild.tagName", "'X-BAR'");
+
+// Strange but valid names
+shouldBe("(new (document.register('y-bar', createRegisterParamters()))()).tagName", "'Y-BAR'");
+shouldBe("(new (document.register('yz-bar', createRegisterParamters()))()).tagName", "'YZ-BAR'");
+shouldBe("(new (document.register('y-z-bar', createRegisterParamters()))()).tagName", "'Y-Z-BAR'");
+shouldBe("(new (document.register('y--bar', createRegisterParamters()))()).tagName", "'Y--BAR'");
+
+</script>
+<script src="../../js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/dom/custom/document-register-reentrant-null-constructor-expected.txt b/LayoutTests/fast/dom/custom/document-register-reentrant-null-constructor-expected.txt
new file mode 100644 (file)
index 0000000..a65b598
--- /dev/null
@@ -0,0 +1,9 @@
+Fuzzing document.register() through getters. PASS uless crash.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/custom/document-register-reentrant-null-constructor.html b/LayoutTests/fast/dom/custom/document-register-reentrant-null-constructor.html
new file mode 100644 (file)
index 0000000..808a00b
--- /dev/null
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../js/resources/js-test-pre.js"></script>
+<script src="./resources/document-register-fuzz.js"></script>
+</head>
+<body>
+<div id="container"></div>
+<script>
+description("Fuzzing document.register() through getters. PASS uless crash.");
+
+setupObjectHooks({
+    prototypeGet: function() { },
+    prototypeSet: function(value) { },
+    constructorGet: function() { },
+    constructorSet: function(value) { }
+});
+
+exerciseDocumentRegister();
+</script>
+<script src="../../js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/dom/custom/document-register-reentrant-returning-fake-expected.txt b/LayoutTests/fast/dom/custom/document-register-reentrant-returning-fake-expected.txt
new file mode 100644 (file)
index 0000000..a65b598
--- /dev/null
@@ -0,0 +1,9 @@
+Fuzzing document.register() through getters. PASS uless crash.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/custom/document-register-reentrant-returning-fake.html b/LayoutTests/fast/dom/custom/document-register-reentrant-returning-fake.html
new file mode 100644 (file)
index 0000000..e7e90fb
--- /dev/null
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../js/resources/js-test-pre.js"></script>
+<script src="./resources/document-register-fuzz.js"></script>
+</head>
+<body>
+<div id="container"></div>
+<script>
+description("Fuzzing document.register() through getters. PASS uless crash.");
+
+var badPrototype = Image.prototype;
+var badConstructor = Image.prototype.constructor;
+
+setupObjectHooks({
+    prototypeGet: function() { return badPrototype; },
+    prototypeSet: function(value) {  },
+    constructorGet: function() { return badConstructor; },
+    constructorSet: function(value) {  }
+});
+
+exerciseDocumentRegister();
+</script>
+<script src="../../js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/dom/custom/document-register-reentrant-throwing-constructor-expected.txt b/LayoutTests/fast/dom/custom/document-register-reentrant-throwing-constructor-expected.txt
new file mode 100644 (file)
index 0000000..b1fc929
--- /dev/null
@@ -0,0 +1,10 @@
+Fuzzing document.register() through getters. PASS uless crash.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Constructor object isn't created.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/custom/document-register-reentrant-throwing-constructor.html b/LayoutTests/fast/dom/custom/document-register-reentrant-throwing-constructor.html
new file mode 100644 (file)
index 0000000..3bc290e
--- /dev/null
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../js/resources/js-test-pre.js"></script>
+<script src="./resources/document-register-fuzz.js"></script>
+</head>
+<body>
+<div id="container"></div>
+<script>
+description("Fuzzing document.register() through getters. PASS uless crash.");
+
+setupObjectHooks({
+    prototypeGet: function() { throw "Error"; },
+    prototypeSet: function(value) { throw "Error"; },
+    constructorGet: function() { throw "Error"; },
+    constructorSet: function(value) { throw "Error"; }
+});
+
+exerciseDocumentRegister();
+</script>
+<script src="../../js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/dom/custom/resources/document-register-fuzz.js b/LayoutTests/fast/dom/custom/resources/document-register-fuzz.js
new file mode 100644 (file)
index 0000000..ed2d337
--- /dev/null
@@ -0,0 +1,49 @@
+
+function setupObjectHooks(hooks)
+{
+    // Wrapper for these object should be materialized before setting hooks.
+    console.log;
+    document.webkitRegister
+    HTMLSpanElement.prototype;
+
+    Object.defineProperty(Object.prototype, "prototype", {
+        get: function() { return hooks.prototypeGet(); },
+        set: function(value) { return hooks.prototypeSet(value); }
+    });
+
+    Object.defineProperty(Object.prototype, "constructor", {
+        get: function() { return hooks.constructorGet(); },
+        set: function(value) { return hooks.constructorSet(value); }
+    });
+
+    return hooks;
+}
+
+function exerciseDocumentRegister()
+{
+    var myConstructor = null;
+    var myPrototype = Object.create(HTMLElement.prototype);
+    try {
+        myConstructor = document.webkitRegister("x-do-nothing", { prototype: myPrototype });
+    } catch (e) { }
+
+    try {
+        if (!myConstructor) {
+            debug("Constructor object isn't created.");
+            return;
+        }
+
+        if (myConstructor.prototype != myPrototype) {
+            console.log("FAIL: bad prototype");
+            return;
+         }
+
+        var element = new myConstructor();
+        if (!element)
+            return;
+        if (element.constructor != myConstructor) {
+            console.log("FAIL: bad constructor");
+            return;
+         }
+    } catch (e) { console.log(e); }
+}
index 8e2eca7..5ff92f7 100644 (file)
@@ -1112,6 +1112,9 @@ webkit.org/b/76489 compositing/webgl/webgl-reflection.html [ ImageOnlyFailure ]
 webkit.org/b/76439 [ Debug ] fast/dom/shadow/content-element-api.html [ Failure ]
 webkit.org/b/76439 [ Debug ] fast/dom/shadow/content-element-outside-shadow.html [ Failure ]
 
+# ENABLE(CUSTOM_ELEMENTS) is disabled.
+fast/dom/custom
+
 # CSS Variables are not yet enabled.
 webkit.org/b/85580 fast/css/variables
 webkit.org/b/85580 inspector/styles/variables
index 5cd66c8..615070f 100644 (file)
@@ -1,3 +1,160 @@
+2013-02-24  Hajime Morrita  <morrita@google.com>
+
+        [Custom Elements] Implement bare-bone document.register()
+        https://bugs.webkit.org/show_bug.cgi?id=100229
+
+        Reviewed by Adam Barth.
+
+        This change implements a prefixed version of document.register(), with minimal feature support.
+        - The feature is guarded by ENABLE(CUSTOM_ELEMENTS) and RuntimeEnabledFeatures::customDOMElementsEnabled().
+        - This bare-bone version only recognizes "name" and "prototype" parameters. It doesn't support default value of "prototype" parameter.
+        - Currently only V8 is supported. JSC binding needs its own binding implementation.
+
+        = Major new classes under dom/:
+
+        The dom module gets two new classes:
+        - CustomElementConstructor: A return value of document.register()
+          which holds the custom element definition.
+        - CustomElementRegistry: A collection of CustomElementConstructor objects.
+          CustomElementRegistry instance is created per Document and is owned by the Document.
+
+        CustomElementConstructor knows the definition of each custom
+        element, which is registered by document.register(). The name and
+        other options are held by this object. CustomElementRegistry owns a set
+        of the registered constructors. The registry guarantees invariants
+        like validity and uniqueness of the element names.
+
+        = A change on make_names.pl
+
+        This change tweaks make_names.pl (or generated HTMLElementFactory)
+        to hook the creations of unknown elements. Some of element names
+        which come to the fallback path can be one of registered custom
+        element.
+
+        = [V8WrapAsFunction] extended attribute:
+
+        The document.register() API returns a constructor
+        function. However, the V8 binding currently doesn't support it. To
+        make it possible, this change introduces "V8WrapAsFunction"
+        extended attribute for annotating CustomElementConstructor IDL
+        interface.
+
+        V8WrapAsFunction wraps the annotated interface with a JavaScript
+        function, which calls the original object as a function, or as a
+        constructor depends on the context.
+
+        With this wrapper function, there are two levels of indirection
+        between native C++ object and author-visible JS function:
+
+        [JS Adaptor Function] <-(hidden property)-> [JS Wrapper Object] -(internal field)-> [C++ Native object]
+
+        The code generator generates the binding code which deals with
+        this indirection.  Also, there is a set of helper functions in
+        V8AdaptorFunction.h/cpp which takes care of this indirection.
+        V8DOMWrapper.cpp/h works as a facade for these APIs and is used from
+        the generated code.
+
+        This redundancy comes from limitations of both V8 bindings and V8
+        embedding API. See bug 108138 for details.
+
+        = V8HTMLCustomElement
+
+        Unlike built-in HTML elements, any custom element has no
+        corresponding C++ class. Instead, document.register() should allow
+        passing a prototype object for the elements being registered.
+
+        V8HTMLCustomElement handles this lack of native class.  It behaves
+        like a native side proxy of non-native HTMLElement subclasses.  It
+        connects each custom element to an appropriate native element,
+        which is HTMLElement at this time. This restriction will be
+        relaxed later. See Bug 110436 for details.
+
+        = Custom DOM elements and multiple worlds
+
+        In this patch, custom element registration and instantiation is not allowed
+        in non-main world and document.register() API just fails there.
+
+        Reviewed by Adam Barth.
+
+        Tests: fast/dom/custom/document-register-basic.html
+               fast/dom/custom/document-register-reentrant-null-constructor.html
+               fast/dom/custom/document-register-reentrant-returning-fake.html
+               fast/dom/custom/document-register-reentrant-throwing-constructor.html
+
+        * DerivedSources.make:
+        * WebCore.gypi:
+        * bindings/generic/RuntimeEnabledFeatures.cpp:
+        * bindings/generic/RuntimeEnabledFeatures.h:
+        (RuntimeEnabledFeatures):
+        (WebCore::RuntimeEnabledFeatures::customDOMElementsEnabled):
+        (WebCore::RuntimeEnabledFeatures::setCustomDOMElements):
+        * bindings/scripts/CodeGeneratorV8.pm:
+        (GenerateHeader):
+        * bindings/scripts/IDLAttributes.txt:
+        * bindings/v8/CustomElementHelpers.cpp: Added.
+        (WebCore::CustomElementHelpers::initializeConstructorWrapper):
+        (WebCore::hasNoBuiltinsInPrototype):
+        (WebCore::CustomElementHelpers::isValidPrototypeParameter):
+        (WebCore::CustomElementHelpers::isFeatureAllowed):
+        * bindings/v8/CustomElementHelpers.h: Copied from Source/WebCore/bindings/v8/V8HiddenPropertyName.h.
+        (CustomElementHelpers):
+        * bindings/v8/V8AdaptorFunction.cpp: Added.
+        (WebCore::V8AdaptorFunction::getTemplate):
+        (WebCore::V8AdaptorFunction::configureTemplate):
+        (WebCore::V8AdaptorFunction::invocationCallback):
+        (WebCore::V8AdaptorFunction::wrap):
+        * bindings/v8/V8AdaptorFunction.h: Added.
+        (V8AdaptorFunction):
+        (WebCore::V8AdaptorFunction::unwrap):
+        (WebCore::V8AdaptorFunction::get):
+        * bindings/v8/V8DOMConfiguration.cpp:
+        (WebCore::V8DOMConfiguration::configureTemplate):
+        * bindings/v8/V8DOMWrapper.cpp:
+        (WebCore::V8DOMWrapper::toFunction):
+        (WebCore::V8DOMWrapper::fromFunction):
+        * bindings/v8/V8DOMWrapper.h:
+        (V8DOMWrapper):
+        * bindings/v8/V8HTMLCustomElement.cpp: Added.
+        (WebCore::V8HTMLCustomElement::createWrapper):
+        * bindings/v8/V8HTMLCustomElement.h: Copied from Source/WebCore/bindings/v8/V8HiddenPropertyName.h.
+        (V8HTMLCustomElement):
+        (WebCore::V8HTMLCustomElement::toV8):
+        (WebCore::HTMLCustomElement::toV8):
+        * bindings/v8/V8HiddenPropertyName.h:
+        * bindings/v8/custom/V8CustomElementConstructorCustom.cpp: Added.
+        (WebCore::V8CustomElementConstructor::callAsFunctionCallback):
+        * dom/CustomElementConstructor.cpp: Copied from Source/WebCore/bindings/v8/V8HiddenPropertyName.h.
+        (WebCore::CustomElementConstructor::create):
+        (WebCore::CustomElementConstructor::CustomElementConstructor):
+        (WebCore::CustomElementConstructor::~CustomElementConstructor):
+        (WebCore::CustomElementConstructor::createElement):
+        * dom/CustomElementConstructor.h: Copied from Source/WebCore/bindings/v8/V8HiddenPropertyName.h.
+        (CustomElementConstructor):
+        (WebCore::CustomElementConstructor::document):
+        (WebCore::CustomElementConstructor::tagName):
+        (WebCore::CustomElementConstructor::name):
+        * dom/CustomElementConstructor.idl: Added.
+        * dom/CustomElementRegistry.cpp: Added.
+        (WebCore::CustomElementRegistry::CustomElementRegistry):
+        (WebCore::CustomElementRegistry::~CustomElementRegistry):
+        (WebCore::CustomElementRegistry::constructorOf):
+        (WebCore::CustomElementRegistry::isValidName):
+        (WebCore::CustomElementRegistry::registerElement):
+        (WebCore::CustomElementRegistry::find):
+        (WebCore::CustomElementRegistry::createElement):
+        (WebCore::CustomElementRegistry::document):
+        * dom/CustomElementRegistry.h: Added.
+        (CustomElementRegistry):
+        * dom/Document.cpp:
+        (WebCore::Document::removedLastRef):
+        (WebCore::Document::registerElement):
+        (WebCore::Document::registry):
+        * dom/Document.h:
+        (Document):
+        * dom/make_names.pl:
+        (printWrapperFactoryCppFile):
+        * html/HTMLDocument.idl:
+
 2013-02-24  Eugene Klyuchnikov  <eustas@chromium.org>
 
         Web Inspector: [Timeline] Shrink CPU bars to make it easier to see frame information popup.
index 24eecf1..476ad8e 100644 (file)
@@ -220,6 +220,7 @@ BINDING_IDLS = \
     $(WebCore)/dom/Clipboard.idl \
     $(WebCore)/dom/Comment.idl \
     $(WebCore)/dom/CompositionEvent.idl \
+    $(WebCore)/dom/CustomElementConstructor.idl \
     $(WebCore)/dom/CustomEvent.idl \
     $(WebCore)/dom/DOMCoreException.idl \
     $(WebCore)/dom/DOMError.idl \
index 28b78ca..fb69071 100644 (file)
             'dom/Clipboard.idl',
             'dom/Comment.idl',
             'dom/CompositionEvent.idl',
+            'dom/CustomElementConstructor.idl',
             'dom/CustomEvent.idl',
             'dom/DOMCoreException.idl',
             'dom/DOMError.idl',
             'bindings/v8/ArrayValue.h',
             'bindings/v8/BindingState.cpp',
             'bindings/v8/BindingState.h',
+            'bindings/v8/CustomElementHelpers.cpp',
+            'bindings/v8/CustomElementHelpers.h',
             'bindings/v8/DOMDataStore.cpp',
             'bindings/v8/DOMDataStore.h',
             'bindings/v8/DOMRequestState.h',
             'bindings/v8/SharedPersistent.h',
             'bindings/v8/V8AbstractEventListener.cpp',
             'bindings/v8/V8AbstractEventListener.h',
+            'bindings/v8/V8AdaptorFunction.cpp',
+            'bindings/v8/V8AdaptorFunction.h',
             'bindings/v8/V8Binding.cpp',
             'bindings/v8/V8Binding.h',
             'bindings/v8/V8BindingMacros.h',
             'bindings/v8/V8GCController.h',
             'bindings/v8/V8GCForContextDispose.cpp',
             'bindings/v8/V8GCForContextDispose.h',
+            'bindings/v8/V8HTMLCustomElement.cpp',
+            'bindings/v8/V8HTMLCustomElement.h',
             'bindings/v8/V8HiddenPropertyName.cpp',
             'bindings/v8/V8HiddenPropertyName.h',
             'bindings/v8/V8Initializer.cpp',
             'bindings/v8/custom/V8ConsoleCustom.cpp',
             'bindings/v8/custom/V8CoordinatesCustom.cpp',
             'bindings/v8/custom/V8CryptoCustom.cpp',
+            'bindings/v8/custom/V8CustomElementConstructorCustom.cpp',
             'bindings/v8/custom/V8CustomEventCustom.cpp',
             'bindings/v8/custom/V8CustomSQLStatementErrorCallback.cpp',
             'bindings/v8/custom/V8CustomXPathNSResolver.cpp',
             'dom/ContextFeatures.cpp',
             'dom/ContextFeatures.h',
             'dom/CrossThreadTask.h',
+            'dom/CustomElementConstructor.cpp',
+            'dom/CustomElementConstructor.h',
+            'dom/CustomElementRegistry.cpp',
+            'dom/CustomElementRegistry.h',
             'dom/CustomEvent.cpp',
             'dom/CustomEvent.h',
             'dom/DOMAllInOne.cpp',
index 2a2da6f..f41143e 100644 (file)
@@ -190,6 +190,10 @@ bool RuntimeEnabledFeatures::isShadowDOMEnabled = false;
 bool RuntimeEnabledFeatures::isAuthorShadowDOMForAnyElementEnabled = false;
 #endif
 
+#if ENABLE(CUSTOM_ELEMENTS)
+bool RuntimeEnabledFeatures::isCustomDOMElementsEnabled = false;
+#endif
+
 #if ENABLE(STYLE_SCOPED)
 bool RuntimeEnabledFeatures::isStyleScopedEnabled = false;
 #endif
index cec6419..2537e8c 100644 (file)
@@ -218,6 +218,11 @@ public:
     static void setAuthorShadowDOMForAnyElementEnabled(bool isEnabled) { isAuthorShadowDOMForAnyElementEnabled = isEnabled; }
 #endif
 
+#if ENABLE(CUSTOM_ELEMENTS)
+    static bool customDOMElementsEnabled() { return isCustomDOMElementsEnabled; }
+    static void setCustomDOMElements(bool isEnabled) { isCustomDOMElementsEnabled = isEnabled; }
+#endif
+
 #if ENABLE(STYLE_SCOPED)
     static bool styleScopedEnabled() { return isStyleScopedEnabled; }
     static void setStyleScopedEnabled(bool isEnabled) { isStyleScopedEnabled = isEnabled; }
@@ -343,6 +348,10 @@ private:
     static bool isAuthorShadowDOMForAnyElementEnabled;
 #endif
 
+#if ENABLE(CUSTOM_ELEMENTS)
+    static bool isCustomDOMElementsEnabled;
+#endif
+
 #if ENABLE(STYLE_SCOPED)
     static bool isStyleScopedEnabled;
 #endif
index 414b17a..3b5cbf0 100644 (file)
@@ -352,13 +352,20 @@ END
         push(@headerContent, "false;\n");
     }
 
+    my $fromFunctionOpening = "";
+    my $fromFunctionClosing = "";
+    if ($interface->extendedAttributes->{"V8WrapAsFunction"}) {
+        $fromFunctionOpening = "V8DOMWrapper::fromFunction(";
+        $fromFunctionClosing = ")";
+    }
+
     push(@headerContent, <<END);
     static bool HasInstance(v8::Handle<v8::Value>, v8::Isolate*);
     static v8::Persistent<v8::FunctionTemplate> GetRawTemplate(v8::Isolate*);
     static v8::Persistent<v8::FunctionTemplate> GetTemplate(v8::Isolate*);
     static ${nativeType}* toNative(v8::Handle<v8::Object> object)
     {
-        return reinterpret_cast<${nativeType}*>(object->GetAlignedPointerFromInternalField(v8DOMWrapperObjectIndex));
+        return reinterpret_cast<${nativeType}*>(${fromFunctionOpening}object${fromFunctionClosing}->GetAlignedPointerFromInternalField(v8DOMWrapperObjectIndex));
     }
     static void derefObject(void*);
     static WrapperTypeInfo info;
@@ -539,6 +546,9 @@ END
     } else {
 
         my $createWrapperCall = $customWrap ? "${v8InterfaceName}::wrap" : "${v8InterfaceName}::createWrapper";
+        my $returningWrapper = $interface->extendedAttributes->{"V8WrapAsFunction"} ? "V8DOMWrapper::toFunction(wrapper)" : "wrapper";
+        my $returningCreatedWrapperOpening = $interface->extendedAttributes->{"V8WrapAsFunction"} ? "V8DOMWrapper::toFunction(" : "";
+        my $returningCreatedWrapperClosing = $interface->extendedAttributes->{"V8WrapAsFunction"} ? ", impl->name(), isolate)" : "";
 
         if ($customWrap) {
             push(@headerContent, <<END);
@@ -552,7 +562,7 @@ inline v8::Handle<v8::Object> wrap(${nativeType}* impl, v8::Handle<v8::Object> c
 {
     ASSERT(impl);
     ASSERT(DOMDataStore::getWrapper(impl, isolate).IsEmpty());
-    return $createWrapperCall(impl, creationContext, isolate);
+    return ${returningCreatedWrapperOpening}$createWrapperCall(impl, creationContext, isolate)${returningCreatedWrapperClosing};
 }
 END
         }
@@ -565,7 +575,7 @@ inline v8::Handle<v8::Value> toV8(${nativeType}* impl, v8::Handle<v8::Object> cr
         return v8NullWithCheck(isolate);
     v8::Handle<v8::Value> wrapper = DOMDataStore::getWrapper(impl, isolate);
     if (!wrapper.IsEmpty())
-        return wrapper;
+        return $returningWrapper;
     return wrap(impl, creationContext, isolate);
 }
 
@@ -576,7 +586,7 @@ inline v8::Handle<v8::Value> toV8Fast(${nativeType}* impl, const HolderContainer
         return v8Null(container.GetIsolate());
     v8::Handle<v8::Object> wrapper = DOMDataStore::getWrapperFast(impl, container, wrappable);
     if (!wrapper.IsEmpty())
-        return wrapper;
+        return $returningWrapper;
     return wrap(impl, container.Holder(), container.GetIsolate());
 }
 END
diff --git a/Source/WebCore/bindings/v8/CustomElementHelpers.cpp b/Source/WebCore/bindings/v8/CustomElementHelpers.cpp
new file mode 100644 (file)
index 0000000..258b914
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2012 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 "CustomElementHelpers.h"
+
+#include "DOMWrapperWorld.h"
+#include "V8CustomElementConstructor.h"
+#include "V8HTMLParagraphElement.h"
+#include "V8HTMLSpanElement.h"
+
+namespace WebCore {
+
+#if ENABLE(CUSTOM_ELEMENTS)
+
+bool CustomElementHelpers::initializeConstructorWrapper(CustomElementConstructor* constructor, const ScriptValue& prototype, ScriptState* state)
+{
+    ASSERT(isFeatureAllowed(state));
+    ASSERT(!prototype.v8Value().IsEmpty() && prototype.v8Value()->IsObject());
+    v8::Handle<v8::Value> wrapperValue = toV8(constructor, state->context()->Global(), state->context()->GetIsolate());
+    if (wrapperValue.IsEmpty() || !wrapperValue->IsObject())
+        return false;
+    v8::Handle<v8::Function> wrapper = v8::Handle<v8::Function>::Cast(wrapperValue);
+    // - Object::ForceSet() nor Object::SetAccessor Doesn't work against the "prototype" property of function objects.
+    // - Set()-ing here is safe because
+    //   - Hooking Object.prototype's defineProperty() with "prototype" or "constructor" also doesn't affect on these properties of function objects and
+    //   - Using Set() is okay becaues each function has "prototype" property from start and Objects.prototype cannot intercept the property access.
+    v8::Handle<v8::String> prototypeKey = v8String("prototype", state->context()->GetIsolate());
+    ASSERT(wrapper->HasOwnProperty(prototypeKey));
+    wrapper->Set(prototypeKey, prototype.v8Value(), v8::ReadOnly);
+
+    v8::Handle<v8::String> constructorKey = v8String("constructor", state->context()->GetIsolate());
+    v8::Handle<v8::Object> prototypeObject = v8::Handle<v8::Object>::Cast(prototype.v8Value());
+    ASSERT(!prototypeObject->HasOwnProperty(constructorKey));
+    prototypeObject->ForceSet(constructorKey, wrapper, v8::ReadOnly);
+    return true;
+}
+
+// See FIXME on the caller side comment.
+static bool hasNoBuiltinsInPrototype(v8::Handle<v8::Object> htmlPrototype, v8::Handle<v8::Value> chain)
+{
+    while (!chain.IsEmpty()) {
+        if (chain == htmlPrototype)
+            return true;
+        if (!chain->IsObject())
+            return false;
+        // The internal field count indicates the object might be a native backed, built-in object.
+        if (v8::Handle<v8::Object>::Cast(chain)->InternalFieldCount())
+            return false;
+        chain = v8::Handle<v8::Object>::Cast(chain)->GetPrototype();
+    }
+
+    return false;
+}
+
+bool CustomElementHelpers::isValidPrototypeParameter(const ScriptValue& prototype, ScriptState* state)
+{
+    if (prototype.v8Value().IsEmpty() || !prototype.v8Value()->IsObject())
+        return false;
+
+    // document.register() sets the constructor property, so the prototype shouldn't have one.
+    v8::Handle<v8::Object> prototypeObject = v8::Handle<v8::Object>::Cast(prototype.v8Value());
+    if (prototypeObject->HasOwnProperty(v8String("constructor", state->context()->GetIsolate())))
+        return false;
+    V8PerContextData* perContextData = V8PerContextData::from(state->context());
+    //
+    // FIXME: https://bugs.webkit.org/show_bug.cgi?id=110436
+    //   document.register() should allow arbitrary HTMLElement subclassses.
+    //   Currently it supports custom elements which are
+    //   - direct subclasses of HTMLElement or
+    //   - subclasses of other custom elements
+    //
+    v8::Handle<v8::Object> htmlConstructor = v8::Handle<v8::Object>::Cast(perContextData->constructorForType(&V8HTMLElement::info));
+    if (htmlConstructor.IsEmpty())
+        return false;
+    v8::Handle<v8::Object> htmlPrototype = v8::Handle<v8::Object>::Cast(htmlConstructor->Get(v8String("prototype", state->context()->GetIsolate())));
+    if (htmlPrototype.IsEmpty())
+        return false;
+    if (!hasNoBuiltinsInPrototype(htmlPrototype, prototypeObject))
+        return false;
+    return true;
+}
+
+bool CustomElementHelpers::isFeatureAllowed(ScriptState* state)
+{
+    return isFeatureAllowed(state->context());
+}
+
+bool CustomElementHelpers::isFeatureAllowed(v8::Handle<v8::Context> context)
+{
+    if (DOMWrapperWorld* world = DOMWrapperWorld::getWorld(context))
+        return world->isMainWorld();
+    return true;
+}
+
+
+#endif // ENABLE(CUSTOM_ELEMENTS)
+
+} // namespace WebCore
diff --git a/Source/WebCore/bindings/v8/CustomElementHelpers.h b/Source/WebCore/bindings/v8/CustomElementHelpers.h
new file mode 100644 (file)
index 0000000..08d1bcb
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef CustomElementHelpers_h
+#define CustomElementHelpers_h
+
+#include "ExceptionCode.h"
+#include "ScriptValue.h"
+
+namespace WebCore {
+
+#if ENABLE(CUSTOM_ELEMENTS)
+
+class CustomElementConstructor;
+class ScriptState;
+
+class CustomElementHelpers {
+public:
+    static bool initializeConstructorWrapper(CustomElementConstructor*, const ScriptValue& prototype, ScriptState*);
+    static bool isValidPrototypeParameter(const ScriptValue&, ScriptState*);
+    static bool isFeatureAllowed(ScriptState*);
+
+    static bool isFeatureAllowed(v8::Handle<v8::Context>);
+};
+
+#endif // ENABLE(CUSTOM_ELEMENTS)
+
+} // namespace WebCore
+
+#endif // CustomElementHelpers_h
diff --git a/Source/WebCore/bindings/v8/V8AdaptorFunction.cpp b/Source/WebCore/bindings/v8/V8AdaptorFunction.cpp
new file mode 100644 (file)
index 0000000..bbc1d32
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2012 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 "V8AdaptorFunction.h"
+
+#include "V8PerIsolateData.h"
+#include <wtf/Vector.h>
+
+#if ENABLE(CUSTOM_ELEMENTS)
+
+namespace WebCore {
+
+WrapperTypeInfo V8AdaptorFunction::info = { V8AdaptorFunction::getTemplate, 0, 0, 0, 0, 0, 0, WrapperTypeObjectPrototype };
+
+v8::Persistent<v8::FunctionTemplate> V8AdaptorFunction::getTemplate(v8::Isolate* isolate)
+{
+    ASSERT(isolate);
+    V8PerIsolateData* data = V8PerIsolateData::from(isolate);
+    V8PerIsolateData::TemplateMap::iterator result = data->rawTemplateMap().find(&info);
+    if (result != data->rawTemplateMap().end())
+        return result->value;
+    // The lifetime is of newTemplate is delegated to the TemplateMap thus this won't be leaked.
+    v8::Persistent<v8::FunctionTemplate> newTemplate = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New());
+    data->rawTemplateMap().add(&info, configureTemplate(newTemplate));
+    return newTemplate;
+}
+
+v8::Persistent<v8::FunctionTemplate> V8AdaptorFunction::configureTemplate(v8::Persistent<v8::FunctionTemplate> functionTemplate)
+{
+    functionTemplate->SetCallHandler(&V8AdaptorFunction::invocationCallback);
+    return functionTemplate;
+}
+
+v8::Handle<v8::Value> V8AdaptorFunction::invocationCallback(const v8::Arguments& args)
+{
+    v8::Handle<v8::Object> wrapped = v8::Handle<v8::Object>::Cast(args.Callee()->GetHiddenValue(V8HiddenPropertyName::adaptorFunctionPeer()));
+    // FIXME: This can be faster if we can access underlying native callback directly.
+    // We won't need this once https://bugs.webkit.org/show_bug.cgi?id=108138 is addressed.
+    Vector<v8::Handle<v8::Value> > argArray(args.Length());
+    for (int i = 0; i < args.Length(); ++i)
+        argArray.append(args[i]);
+    if (args.IsConstructCall())
+        return wrapped->CallAsConstructor(argArray.size(), argArray.data());
+    return wrapped->CallAsFunction(args.This(), argArray.size(), argArray.data());
+}
+
+v8::Handle<v8::Function> V8AdaptorFunction::wrap(v8::Handle<v8::Object> object, const AtomicString& name, v8::Isolate* isolate)
+{
+    if (object.IsEmpty() || !object->IsObject())
+        return v8::Handle<v8::Function>();
+    v8::Handle<v8::Function> adaptor = v8::Handle<v8::Function>::Cast(getTemplate(isolate)->GetFunction());
+    if (adaptor.IsEmpty())
+        return v8::Handle<v8::Function>();
+    adaptor->SetName(v8String(name.string(), isolate));
+    adaptor->SetHiddenValue(V8HiddenPropertyName::adaptorFunctionPeer(), object);
+    object->SetHiddenValue(V8HiddenPropertyName::adaptorFunctionPeer(), adaptor);
+    return adaptor;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(CUSTOM_ELEMENTS)
diff --git a/Source/WebCore/bindings/v8/V8AdaptorFunction.h b/Source/WebCore/bindings/v8/V8AdaptorFunction.h
new file mode 100644 (file)
index 0000000..e3d6bb5
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef V8AdaptorFunction_h
+#define V8AdaptorFunction_h
+
+#include "V8Binding.h"
+#include "V8HiddenPropertyName.h"
+#include "WrapperTypeInfo.h"
+#include <wtf/PassRefPtr.h>
+
+#if ENABLE(CUSTOM_ELEMENTS)
+
+namespace WebCore {
+
+//
+// FIXME(https://bugs.webkit.org/show_bug.cgi?id=108138):
+// V8AdaptorFunction class and V8WrapAsFunction IDL attribute are needed for two reasons:
+// - 1) V8 doesn't allow expanding the internal field of function objects. https://code.google.com/p/v8/issues/detail?id=837
+//      WebKit need it to associate each wrapper to its backing C++ object. We store it in a hidden property of the wrapped object.
+// - 2) Binding codebase assumes every wrapper object is created through ObjectTemplate::NewInstance(), not FunctionTemplate::GetFunction().
+// Once 1) is addresssed, we could attack 2) with it. 
+//
+class V8AdaptorFunction {
+public:
+    static WrapperTypeInfo info;
+    static v8::Handle<v8::Object> unwrap(v8::Handle<v8::Function>);
+    static v8::Handle<v8::Function> wrap(v8::Handle<v8::Object>, const AtomicString& name, v8::Isolate*);
+    static v8::Handle<v8::Function> get(v8::Handle<v8::Object>);
+
+    static v8::Persistent<v8::FunctionTemplate> getTemplate(v8::Isolate* = 0);
+    static v8::Persistent<v8::FunctionTemplate> configureTemplate(v8::Persistent<v8::FunctionTemplate>);
+    static v8::Handle<v8::Value> invocationCallback(const v8::Arguments&);
+};
+
+inline v8::Handle<v8::Object> V8AdaptorFunction::unwrap(v8::Handle<v8::Function> function)
+{
+    v8::Handle<v8::Value> wrapped = function->GetHiddenValue(V8HiddenPropertyName::adaptorFunctionPeer());
+    ASSERT(!wrapped.IsEmpty());
+    ASSERT(wrapped->IsObject());
+    return v8::Handle<v8::Object>::Cast(wrapped);
+}
+
+inline v8::Handle<v8::Function> V8AdaptorFunction::get(v8::Handle<v8::Object> object)
+{
+    v8::Handle<v8::Value> adaptorFunction = object->GetHiddenValue(V8HiddenPropertyName::adaptorFunctionPeer());
+    ASSERT(!adaptorFunction.IsEmpty());
+    ASSERT(adaptorFunction->IsFunction());
+    return v8::Handle<v8::Function>::Cast(adaptorFunction);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(CUSTOM_ELEMENTS)
+#endif // V8AdaptorFunction_h
index 0bba6fc..90dfb41 100644 (file)
@@ -33,6 +33,8 @@
 
 namespace WebCore {
 
+const int prototypeInternalFieldcount = 1;
+
 void V8DOMConfiguration::batchConfigureAttributes(v8::Handle<v8::ObjectTemplate> instance, v8::Handle<v8::ObjectTemplate> prototype, const BatchedAttribute* attributes, size_t attributeCount, v8::Isolate* isolate)
 {
     for (size_t i = 0; i < attributeCount; ++i)
@@ -60,8 +62,15 @@ v8::Local<v8::Signature> V8DOMConfiguration::configureTemplate(v8::Persistent<v8
     functionDescriptor->SetClassName(v8::String::NewSymbol(interfaceName));
     v8::Local<v8::ObjectTemplate> instance = functionDescriptor->InstanceTemplate();
     instance->SetInternalFieldCount(fieldCount);
-    if (!parentClass.IsEmpty())
+    if (!parentClass.IsEmpty()) {
         functionDescriptor->Inherit(parentClass);
+        // Marks the prototype object as one of native-backed objects.
+        // This is needed since bug 110436 asks WebKit to tell native-initiated prototypes from pure-JS ones.
+        // This doesn't mark kinds "root" classes like Node, where setting this changes prototype chain structure.
+        v8::Local<v8::ObjectTemplate> prototype = functionDescriptor->PrototypeTemplate();
+        prototype->SetInternalFieldCount(prototypeInternalFieldcount);
+    }
+
     if (attributeCount)
         batchConfigureAttributes(instance, functionDescriptor->PrototypeTemplate(), attributes, attributeCount, isolate);
     v8::Local<v8::Signature> defaultSignature = v8::Signature::New(functionDescriptor);
index 342778a..4d8c226 100644 (file)
@@ -31,6 +31,7 @@
 #include "config.h"
 #include "V8DOMWrapper.h"
 
+#include "V8AdaptorFunction.h"
 #include "V8Binding.h"
 #include "V8DOMWindow.h"
 #include "V8HTMLCollection.h"
@@ -137,4 +138,25 @@ bool V8DOMWrapper::isWrapperOfType(v8::Handle<v8::Value> value, WrapperTypeInfo*
     return typeInfo == type;
 }
 
+#if ENABLE(CUSTOM_ELEMENTS)
+
+v8::Handle<v8::Function> V8DOMWrapper::toFunction(v8::Handle<v8::Value> object)
+{
+    return V8AdaptorFunction::get(v8::Handle<v8::Object>::Cast(object));
+}
+
+v8::Handle<v8::Function> V8DOMWrapper::toFunction(v8::Handle<v8::Object> object, const AtomicString& name, v8::Isolate* isolate)
+{
+    return V8AdaptorFunction::wrap(object, name, isolate);
+}
+
+v8::Handle<v8::Object> V8DOMWrapper::fromFunction(v8::Handle<v8::Object> object)
+{
+    if (!object->IsFunction())
+        return object;
+    return V8AdaptorFunction::unwrap(v8::Handle<v8::Function>::Cast(object));
+}
+
+#endif // ENABLE(CUSTOM_ELEMENTS)
+
 }  // namespace WebCore
index 39dce47..18441a9 100644 (file)
@@ -62,6 +62,13 @@ namespace WebCore {
 
         static bool isDOMWrapper(v8::Handle<v8::Value>);
         static bool isWrapperOfType(v8::Handle<v8::Value>, WrapperTypeInfo*);
+
+#if ENABLE(CUSTOM_ELEMENTS)
+        // Used for V8WrapAsFunction, which is used only by CUSTOM_ELEMENTS
+        static v8::Handle<v8::Function> toFunction(v8::Handle<v8::Value>);
+        static v8::Handle<v8::Function> toFunction(v8::Handle<v8::Object>, const AtomicString& name, v8::Isolate*);
+        static v8::Handle<v8::Object> fromFunction(v8::Handle<v8::Object>);
+#endif // ENABLE(CUSTOM_ELEMENTS)
     };
 
     inline void V8DOMWrapper::setNativeInfo(v8::Handle<v8::Object> wrapper, WrapperTypeInfo* type, void* object)
diff --git a/Source/WebCore/bindings/v8/V8HTMLCustomElement.cpp b/Source/WebCore/bindings/v8/V8HTMLCustomElement.cpp
new file mode 100644 (file)
index 0000000..f47f359
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2012 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"
+
+#if ENABLE(CUSTOM_ELEMENTS)
+
+#include "V8HTMLCustomElement.h"
+
+#include "CustomElementHelpers.h"
+#include "CustomElementRegistry.h"
+#include "HTMLElement.h"
+#include "V8CustomElementConstructor.h"
+#include "V8HTMLSpanElement.h"
+
+namespace WebCore {
+
+// FIXME: Each custom elements should have its own GetTemplate method so that it can be derived from different super element.
+WrapperTypeInfo V8HTMLCustomElement::info = { &V8HTMLElement::GetTemplate, V8HTMLElement::derefObject, 0, V8HTMLElement::toEventTarget, 0, 0, &V8HTMLElement::info, WrapperTypeObjectPrototype };
+
+v8::Handle<v8::Object> V8HTMLCustomElement::createWrapper(PassRefPtr<HTMLElement> impl, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
+{
+    ASSERT(impl);
+
+    RefPtr<CustomElementConstructor> constructor = CustomElementRegistry::constructorOf(impl.get());
+    if (!constructor) {
+        v8::Handle<v8::Value> wrapperValue = WebCore::toV8(toHTMLUnknownElement(impl.get()), creationContext, isolate);
+        if (!wrapperValue.IsEmpty() && wrapperValue->IsObject())
+            return v8::Handle<v8::Object>::Cast(wrapperValue);
+        return v8::Handle<v8::Object>();
+    }
+
+    v8::Handle<v8::Object> wrapper = V8DOMWrapper::createWrapper(creationContext, &info, impl.get(), isolate);
+    if (wrapper.IsEmpty())
+        return wrapper;
+
+    // The constructor and registered lifecycle callbacks should be visible only from main world.
+    // FIXME: This shouldn't be needed once each custom element has its own FunctionTemplate
+    // https://bugs.webkit.org/show_bug.cgi?id=108138
+    if (CustomElementHelpers::isFeatureAllowed(creationContext->CreationContext())) {
+        v8::Handle<v8::Value> wrapperValue = WebCore::toV8(constructor.get(), creationContext, isolate);
+        if (wrapperValue.IsEmpty() || !wrapperValue->IsObject())
+            return v8::Handle<v8::Object>();
+        v8::Handle<v8::Object> constructorWapper = v8::Handle<v8::Object>::Cast(wrapperValue);
+        wrapper->SetPrototype(constructorWapper->Get(v8String("prototype", isolate)));
+    }
+
+    V8DOMWrapper::associateObjectWithWrapper(impl, &info, wrapper, isolate, WrapperConfiguration::Dependent);
+    return wrapper;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(CUSTOM_ELEMENTS)
diff --git a/Source/WebCore/bindings/v8/V8HTMLCustomElement.h b/Source/WebCore/bindings/v8/V8HTMLCustomElement.h
new file mode 100644 (file)
index 0000000..f380a01
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef V8HTMLCustomElement_h
+#define V8HTMLCustomElement_h
+
+#include "V8Binding.h"
+#include "V8DOMWrapper.h"
+#include "V8HTMLElement.h"
+#include "V8HTMLUnknownElement.h"
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class HTMLElement;
+
+#if ENABLE(CUSTOM_ELEMENTS)
+
+class V8HTMLCustomElement {
+public:
+    static WrapperTypeInfo info;
+    static v8::Handle<v8::Value> toV8(HTMLElement*, v8::Handle<v8::Object> creationContext = v8::Handle<v8::Object>(), v8::Isolate* = 0);
+    static v8::Handle<v8::Object> wrap(HTMLElement*, v8::Handle<v8::Object> creationContext, v8::Isolate*);
+
+private:
+    static v8::Handle<v8::Object> createWrapper(PassRefPtr<HTMLElement>, v8::Handle<v8::Object>, v8::Isolate*);
+};
+
+inline v8::Handle<v8::Value> V8HTMLCustomElement::toV8(HTMLElement* impl, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
+{
+    if (UNLIKELY(!impl))
+        return v8NullWithCheck(isolate);
+    v8::Handle<v8::Object> wrapper = DOMDataStore::getWrapper(impl, isolate);
+    if (!wrapper.IsEmpty())
+        return wrapper;
+    return V8HTMLCustomElement::wrap(impl, creationContext, isolate);
+}
+
+inline v8::Handle<v8::Object> V8HTMLCustomElement::wrap(HTMLElement* impl, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
+{
+    ASSERT(impl);
+    ASSERT(DOMDataStore::getWrapper(impl, isolate).IsEmpty());
+    return V8HTMLCustomElement::createWrapper(impl, creationContext, isolate);
+}
+
+#else // ENABLE(CUSTOM_ELEMENTS)
+
+class V8HTMLCustomElement {
+public:
+    static v8::Handle<v8::Object> wrap(HTMLElement*, v8::Handle<v8::Object> creationContext = v8::Handle<v8::Object>(), v8::Isolate* = 0);
+};
+
+inline v8::Handle<v8::Object> HTMLCustomElement::wrap(HTMLElement* impl, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
+{
+    return wrap(toHTMLUnknownElement(impl), creationContext, isolate);
+}
+
+#endif // ENABLE(CUSTOM_ELEMENTS)
+
+} // namespace WebCore
+
+#endif // V8HTMLCustomElement_h
index 508bdd3..03cd57f 100644 (file)
@@ -45,6 +45,7 @@ namespace WebCore {
     V(scriptState) \
     V(sleepFunction) \
     V(state) \
+    V(adaptorFunctionPeer) \
     V(toStringString) \
     V(typedArrayHiddenCopyMethod)
 
diff --git a/Source/WebCore/bindings/v8/custom/V8CustomElementConstructorCustom.cpp b/Source/WebCore/bindings/v8/custom/V8CustomElementConstructorCustom.cpp
new file mode 100644 (file)
index 0000000..34374f6
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+* Copyright (C) 2012 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 "V8CustomElementConstructor.h"
+
+#include "CustomElementConstructor.h"
+#include "V8Binding.h"
+#include "V8HTMLCustomElement.h"
+
+namespace WebCore {
+
+v8::Handle<v8::Value> V8CustomElementConstructor::callAsFunctionCallback(const v8::Arguments& args)
+{
+    if (!args.IsConstructCall())
+        return throwTypeError("DOM object constructor cannot be called as a function.", args.GetIsolate());
+    if (ConstructorMode::current() == ConstructorMode::WrapExistingObject)
+        return args.Holder();
+
+    CustomElementConstructor* impl = toNative(args.Holder());
+    RefPtr<HTMLElement> element = impl->createElement();
+    if (!element)
+        return v8Undefined();
+    return V8HTMLCustomElement::toV8(element.get(), args.Holder(), args.GetIsolate());
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/dom/CustomElementConstructor.cpp b/Source/WebCore/dom/CustomElementConstructor.cpp
new file mode 100644 (file)
index 0000000..49ccf9e
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 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"
+
+#if ENABLE(CUSTOM_ELEMENTS)
+
+#include "CustomElementConstructor.h"
+
+#include "CustomElementHelpers.h"
+#include "Document.h"
+#include "HTMLElement.h"
+#include <wtf/Assertions.h>
+
+namespace WebCore {
+
+PassRefPtr<CustomElementConstructor> CustomElementConstructor::create(ScriptState* state, Document* document, const QualifiedName& tagName, const String& name, const ScriptValue& prototype)
+{
+    RefPtr<CustomElementConstructor> created = adoptRef(new CustomElementConstructor(document, tagName, name));
+    if (!CustomElementHelpers::initializeConstructorWrapper(created.get(), prototype, state))
+        return 0;
+    return created.release();
+}
+
+CustomElementConstructor::CustomElementConstructor(Document* document, const QualifiedName& tagName, const String& name)
+    : ContextDestructionObserver(document)
+    , m_tagName(tagName)
+    , m_name(name)
+{
+}
+
+CustomElementConstructor::~CustomElementConstructor()
+{
+}
+
+PassRefPtr<HTMLElement> CustomElementConstructor::createElement() const
+{
+    if (!document())
+        return 0;
+    return HTMLElement::create(m_tagName, document());
+}
+
+}
+
+#endif // ENABLE(CUSTOM_ELEMENTS)
diff --git a/Source/WebCore/dom/CustomElementConstructor.h b/Source/WebCore/dom/CustomElementConstructor.h
new file mode 100644 (file)
index 0000000..578eb17
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef CustomElementConstructor_h
+#define CustomElementConstructor_h
+
+#if ENABLE(CUSTOM_ELEMENTS)
+
+#include "ContextDestructionObserver.h"
+#include "Document.h"
+#include "QualifiedName.h"
+#include <wtf/Forward.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/text/AtomicString.h>
+
+namespace WebCore {
+
+class Document;
+class HTMLElement;
+class ScriptState;
+class ScriptValue;
+
+class CustomElementConstructor : public RefCounted<CustomElementConstructor> , public ContextDestructionObserver {
+public:
+    static PassRefPtr<CustomElementConstructor> create(ScriptState*, Document*, const QualifiedName&, const String&, const ScriptValue&);
+
+    virtual ~CustomElementConstructor();
+
+    Document* document() const { return static_cast<Document*>(m_scriptExecutionContext); }
+    const QualifiedName& tagName() const { return m_tagName; }
+    const AtomicString& name() const { return m_name; }
+
+    PassRefPtr<HTMLElement> createElement() const;
+    
+private:
+    CustomElementConstructor(Document*, const QualifiedName&, const String&);
+
+    QualifiedName m_tagName;
+    AtomicString m_name;
+};
+
+}
+
+#endif // ENABLE(CUSTOM_ELEMENTS)
+
+#endif // CustomElementConstructor_h
diff --git a/Source/WebCore/dom/CustomElementConstructor.idl b/Source/WebCore/dom/CustomElementConstructor.idl
new file mode 100644 (file)
index 0000000..47c530f
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012, 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:
+ * 1.  Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2.  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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+[
+    Conditional=CUSTOM_ELEMENTS,
+    V8EnabledAtRuntime=customDOMElements,
+    V8WrapAsFunction,
+    CustomCall
+] interface CustomElementConstructor {
+};
diff --git a/Source/WebCore/dom/CustomElementRegistry.cpp b/Source/WebCore/dom/CustomElementRegistry.cpp
new file mode 100644 (file)
index 0000000..385e95b
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. 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"
+
+#if ENABLE(CUSTOM_ELEMENTS)
+
+#include "CustomElementRegistry.h"
+
+#include "CustomElementConstructor.h"
+#include "CustomElementHelpers.h"
+#include "Dictionary.h"
+#include "Document.h"
+#include "HTMLElement.h"
+#include "HTMLNames.h"
+#include "RuntimeEnabledFeatures.h"
+#include <wtf/ASCIICType.h>
+
+#if ENABLE(SVG)
+#include "SVGNames.h"
+#endif
+
+#if ENABLE(MATHML)
+#include "MathMLNames.h"
+#endif
+
+namespace WebCore {
+
+CustomElementRegistry::CustomElementRegistry(Document* document)
+    : ContextDestructionObserver(document)
+{
+}
+
+CustomElementRegistry::~CustomElementRegistry()
+{
+}
+
+PassRefPtr<CustomElementConstructor> CustomElementRegistry::constructorOf(HTMLElement* element)
+{
+    RefPtr<CustomElementRegistry> self = element->document()->registry();
+    if (!self)
+        return 0;
+    return self->find(element->tagQName());
+}
+
+bool CustomElementRegistry::isValidName(const AtomicString& name)
+{
+    size_t hyphenPosition = name.find('-');
+    if (hyphenPosition == notFound)
+        return false;
+
+    DEFINE_STATIC_LOCAL(Vector<AtomicString>, reservedNames, ());
+    if (reservedNames.isEmpty()) {
+#if ENABLE(MATHML)
+        reservedNames.append(MathMLNames::annotation_xmlTag.localName());
+#endif
+
+#if ENABLE(SVG)
+        reservedNames.append(SVGNames::color_profileTag.localName());
+        reservedNames.append(SVGNames::font_faceTag.localName());
+        reservedNames.append(SVGNames::font_face_srcTag.localName());
+        reservedNames.append(SVGNames::font_face_uriTag.localName());
+        reservedNames.append(SVGNames::font_face_formatTag.localName());
+        reservedNames.append(SVGNames::font_face_nameTag.localName());
+        reservedNames.append(SVGNames::missing_glyphTag.localName());
+#endif
+    }
+
+    if (notFound != reservedNames.find(name))
+        return false;
+
+    return Document::isValidName(name.string());
+}
+
+PassRefPtr<CustomElementConstructor> CustomElementRegistry::registerElement(ScriptState* state, const AtomicString& name, const Dictionary& options, ExceptionCode& ec)
+{
+    RefPtr<CustomElementRegistry> protect(this);
+
+    if (!CustomElementHelpers::isFeatureAllowed(state))
+        return 0;
+
+    QualifiedName newName(nullAtom, name.lower(), HTMLNames::xhtmlNamespaceURI);
+    if (!isValidName(newName.localName())) {
+        ec = INVALID_CHARACTER_ERR;
+        return 0;
+    }
+        
+    if (find(newName)) {
+        ec = INVALID_STATE_ERR;
+        return 0;
+    }
+
+    ScriptValue prototypeValue;
+    if (!options.get("prototype", prototypeValue)) {
+        // FIXME: Implement the default value handling.
+        // Currently default value of the "prototype" parameter, which
+        // is HTMLSpanElement.prototype, has an ambiguity about its
+        // behavior. The spec should be fixed before WebKit implements
+        // it. https://www.w3.org/Bugs/Public/show_bug.cgi?id=20801
+        ec = INVALID_STATE_ERR;
+        return 0;
+    }
+
+    if (!CustomElementHelpers::isValidPrototypeParameter(prototypeValue, state)) {
+        ec = INVALID_STATE_ERR;
+        return 0;
+    }
+    
+    // An script execution could happen in isValidPrototypeParameter(), which kills the document.
+    if (!document()) {
+        ec = INVALID_STATE_ERR;
+        return 0;
+    }
+
+    RefPtr<CustomElementConstructor> constructor = CustomElementConstructor::create(state, document(), newName, "HTMLCustomElement", prototypeValue);
+    if (!constructor) {
+        ec = INVALID_STATE_ERR;
+        return 0;
+    }
+        
+    m_constructors.add(constructor->tagName().impl(), constructor);
+    return constructor;
+}
+
+PassRefPtr<CustomElementConstructor> CustomElementRegistry::find(const QualifiedName& name) const
+{
+    ConstructorMap::const_iterator found = m_constructors.find(name.impl());
+    return (found != m_constructors.end()) ? found->value : 0;
+}
+
+PassRefPtr<HTMLElement> CustomElementRegistry::createElement(const QualifiedName& name) const
+{
+    if (RefPtr<CustomElementConstructor> found = find(name))
+        return found->createElement();
+    return 0;
+}
+
+inline Document* CustomElementRegistry::document() const
+{
+    return static_cast<Document*>(m_scriptExecutionContext);
+}
+
+}
+
+#endif // ENABLE(CUSTOM_ELEMENTS)
diff --git a/Source/WebCore/dom/CustomElementRegistry.h b/Source/WebCore/dom/CustomElementRegistry.h
new file mode 100644 (file)
index 0000000..7b58c13
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. 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.
+ */
+
+#ifndef CustomElementRegistry_h
+#define CustomElementRegistry_h
+
+#if ENABLE(CUSTOM_ELEMENTS)
+
+#include "ContextDestructionObserver.h"
+#include "ExceptionCode.h"
+#include "QualifiedName.h"
+#include "ScriptValue.h"
+#include "Supplementable.h"
+#include <wtf/Forward.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class CustomElementConstructor;
+class Dictionary;
+class Document;
+class HTMLElement;
+class ScriptExecutionContext;
+class QualifiedName;
+
+class CustomElementRegistry : public RefCounted<CustomElementRegistry> , public ContextDestructionObserver {
+    WTF_MAKE_NONCOPYABLE(CustomElementRegistry); WTF_MAKE_FAST_ALLOCATED;
+public:
+    explicit CustomElementRegistry(Document*);
+    ~CustomElementRegistry();
+
+    PassRefPtr<CustomElementConstructor> registerElement(WebCore::ScriptState*, const AtomicString& name, const Dictionary& options, ExceptionCode&);
+    PassRefPtr<CustomElementConstructor> find(const QualifiedName&) const;
+    PassRefPtr<HTMLElement> createElement(const QualifiedName&) const;
+    Document* document() const;
+
+    static PassRefPtr<CustomElementConstructor> constructorOf(HTMLElement*);
+    
+private:
+    static bool isValidName(const AtomicString&);
+
+    typedef HashMap<QualifiedName::QualifiedNameImpl*, RefPtr<CustomElementConstructor> >ConstructorMap;
+
+    ConstructorMap m_constructors;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(CUSTOM_ELEMENTS)
+#endif
index 78c1bc2..9b6f448 100644 (file)
 #include "ContentSecurityPolicy.h"
 #include "ContextFeatures.h"
 #include "CookieJar.h"
+#include "CustomElementConstructor.h"
+#include "CustomElementRegistry.h"
 #include "DOMImplementation.h"
 #include "DOMNamedFlowCollection.h"
 #include "DOMSelection.h"
 #include "DOMWindow.h"
 #include "DateComponents.h"
+#include "Dictionary.h"
 #include "DocumentEventQueue.h"
 #include "DocumentFragment.h"
 #include "DocumentLoader.h"
@@ -669,6 +672,10 @@ void Document::dispose()
 
     detachParser();
 
+#if ENABLE(CUSTOM_ELEMENTS)
+        m_registry.clear();
+#endif
+
     // removeDetachedChildren() doesn't always unregister IDs,
     // so tear down scope information upfront to avoid having stale references in the map.
     destroyTreeScopeData();
@@ -823,6 +830,25 @@ PassRefPtr<Element> Document::createElement(const AtomicString& name, ExceptionC
     return createElement(QualifiedName(nullAtom, name, nullAtom), false);
 }
 
+#if ENABLE(CUSTOM_ELEMENTS)
+PassRefPtr<CustomElementConstructor> Document::registerElement(WebCore::ScriptState* state, const AtomicString& name, ExceptionCode& ec)
+{
+    return registerElement(state, name, Dictionary(), ec);
+}
+
+PassRefPtr<CustomElementConstructor> Document::registerElement(WebCore::ScriptState* state, const AtomicString& name, const Dictionary& options, ExceptionCode& ec)
+{
+    if (!m_registry)
+        m_registry = adoptRef(new CustomElementRegistry(this));
+    return m_registry->registerElement(state, name, options, ec);
+}
+
+PassRefPtr<CustomElementRegistry> Document::registry() const
+{
+    return m_registry;
+}
+#endif // ENABLE(CUSTOM_ELEMENTS)
+
 PassRefPtr<DocumentFragment> Document::createDocumentFragment()
 {
     return DocumentFragment::create(document());
index 961e51a..ec4cec9 100644 (file)
@@ -71,6 +71,8 @@ class CanvasRenderingContext;
 class CharacterData;
 class Comment;
 class ContextFeatures;
+class CustomElementConstructor;
+class CustomElementRegistry;
 class DOMImplementation;
 class DOMNamedFlowCollection;
 class DOMSelection;
@@ -1142,6 +1144,12 @@ public:
     TextAutosizer* textAutosizer() { return m_textAutosizer.get(); }
 #endif
 
+#if ENABLE(CUSTOM_ELEMENTS)
+    PassRefPtr<CustomElementConstructor> registerElement(WebCore::ScriptState*, const AtomicString& name, ExceptionCode&);
+    PassRefPtr<CustomElementConstructor> registerElement(WebCore::ScriptState*, const AtomicString& name, const Dictionary& options, ExceptionCode&);
+    PassRefPtr<CustomElementRegistry> registry() const;
+#endif
+
     void adjustFloatQuadsForScrollAndAbsoluteZoomAndFrameScale(Vector<FloatQuad>&, RenderObject*);
     void adjustFloatRectForScrollAndAbsoluteZoomAndFrameScale(FloatRect&, RenderObject*);
 
@@ -1518,6 +1526,10 @@ private:
     OwnPtr<TextAutosizer> m_textAutosizer;
 #endif
 
+#if ENABLE(CUSTOM_ELEMENTS)
+    RefPtr<CustomElementRegistry> m_registry;
+#endif
+
     bool m_scheduledTasksAreSuspended;
     
     bool m_visualUpdatesAllowed;
index 7233e32..341eac0 100755 (executable)
@@ -1172,8 +1172,9 @@ END
 ;
     } elsif ($wrapperFactoryType eq "V8") {
         print F <<END
+#include "V8HTMLCustomElement.h"
 #include "V8$parameters{namespace}Element.h"
-        
+
 #include <v8.h>
 END
 ;
@@ -1274,6 +1275,11 @@ END
     return V8SVGElement::createWrapper(element, creationContext, isolate);
 END
 ;
+        } elsif ($parameters{namespace} eq "HTML") {
+            print F <<END
+    return V8HTMLCustomElement::wrap(element, creationContext, isolate);
+END
+;
         } else {
             print F <<END
     return wrap(to$parameters{fallbackInterfaceName}(element), creationContext, isolate);
index 25f0a5c..7c29fcb 100644 (file)
     readonly attribute Element activeElement;
     boolean hasFocus();
 
+#if defined(ENABLE_CUSTOM_ELEMENTS) && ENABLE_CUSTOM_ELEMENTS
+    [V8EnabledAtRuntime=customDOMElements, Conditional=CUSTOM_ELEMENTS, ImplementedAs=registerElement, CallWith=ScriptState]
+    CustomElementConstructor webkitRegister(in DOMString name, in [Optional] Dictionary options) raises(DOMException);
+#endif
+
     // Deprecated attributes
              [TreatNullAs=NullString] attribute DOMString bgColor;
              [TreatNullAs=NullString] attribute DOMString fgColor;
index 17f7e12..2b96f15 100644 (file)
@@ -1,3 +1,20 @@
+2013-02-24  Hajime Morrita  <morrita@google.com>
+
+        [Custom Elements] Implement bare-bone document.register()
+        https://bugs.webkit.org/show_bug.cgi?id=100229
+
+        Reviewed by Adam Barth.
+
+        Added enableCustomDOMElements flag.
+
+        * features.gypi:
+        * public/WebRuntimeFeatures.h:
+        (WebRuntimeFeatures):
+        * src/WebRuntimeFeatures.cpp:
+        (WebKit::WebRuntimeFeatures::enableCustomDOMElements):
+        (WebKit):
+        (WebKit::WebRuntimeFeatures::isCustomDOMElementsEnabled):
+
 2013-02-23  Mark Pilgrim  <pilgrim@chromium.org>
 
         [Chromium] WebKit::initialize should take a Platform* now that WebKitPlatformSupport is empty
index 77b2c76..12e90fb 100644 (file)
@@ -54,6 +54,7 @@
       'ENABLE_CSS_TRANSFORMS_ANIMATIONS_UNPREFIXED=0',
       'ENABLE_CSS_VARIABLES=1',
       'ENABLE_CSS_STICKY_POSITION=1',
+      'ENABLE_CUSTOM_ELEMENTS=1',
       'ENABLE_CUSTOM_SCHEME_HANDLER=0',
       'ENABLE_DASHBOARD_SUPPORT=0',
       'ENABLE_DATA_TRANSFER_ITEMS=1',
index e394d75..a055f08 100644 (file)
@@ -125,6 +125,9 @@ public:
     WEBKIT_EXPORT static void enableShadowDOM(bool);
     WEBKIT_EXPORT static bool isShadowDOMEnabled();
 
+    WEBKIT_EXPORT static void enableCustomDOMElements(bool);
+    WEBKIT_EXPORT static bool isCustomDOMElementsEnabled();
+
     WEBKIT_EXPORT static void enableStyleScoped(bool);
     WEBKIT_EXPORT static bool isStyleScopedEnabled();
 
index a18434e..51bfdfa 100644 (file)
@@ -481,6 +481,25 @@ bool WebRuntimeFeatures::isShadowDOMEnabled()
 #endif
 }
 
+void WebRuntimeFeatures::enableCustomDOMElements(bool enable)
+{
+#if ENABLE(CUSTOM_ELEMENTS)
+    RuntimeEnabledFeatures::setCustomDOMElements(enable);
+#else
+    UNUSED_PARAM(enable);
+#endif
+}
+
+bool WebRuntimeFeatures::isCustomDOMElementsEnabled()
+{
+#if ENABLE(CUSTOM_ELEMENTS)
+    return RuntimeEnabledFeatures::customDOMElementsEnabled();
+#else
+    return false;
+#endif
+}
+
+
 void WebRuntimeFeatures::enableStyleScoped(bool enable)
 {
 #if ENABLE(STYLE_SCOPED)
index 212ac0a..223a6c2 100644 (file)
@@ -1,3 +1,15 @@
+2013-02-24  Hajime Morrita  <morrita@google.com>
+
+        [Custom Elements] Implement bare-bone document.register()
+        https://bugs.webkit.org/show_bug.cgi?id=100229
+
+        Reviewed by Adam Barth.
+
+        Added enableCustomDOMElements flag.
+
+        * DumpRenderTree/chromium/TestShell.cpp:
+        (TestShell::TestShell):
+
 2013-02-23  Mark Pilgrim  <pilgrim@chromium.org>
 
         [Chromium] DumpRenderTree TestShell::initialize should take Platform* now that WebKitPlatformSupport is empty
index 947f980..d07b63c 100644 (file)
@@ -145,6 +145,7 @@ TestShell::TestShell()
     WebRuntimeFeatures::enableVideoTrack(true);
     WebRuntimeFeatures::enableGamepad(true);
     WebRuntimeFeatures::enableShadowDOM(true);
+    WebRuntimeFeatures::enableCustomDOMElements(true);
     WebRuntimeFeatures::enableStyleScoped(true);
     WebRuntimeFeatures::enableScriptedSpeech(true);
     WebRuntimeFeatures::enableRequestAutocomplete(true);