document.createElement should be able to create a custom element
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 25 Jan 2016 17:23:42 +0000 (17:23 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 25 Jan 2016 17:23:42 +0000 (17:23 +0000)
https://bugs.webkit.org/show_bug.cgi?id=153173

Reviewed by Darin Adler.

Source/WebCore:

Added the support for constructing a custom element via document.createElement.

Extracted HTMLElementFactory::createKnownElement, which returns nullptr when the specified name doesn't match
any builtin element instead of out of HTMLUnknownElement, out of HTMLElementFactory::createElement.

Test: fast/custom-elements/Document-createElement.html

* bindings/js/JSCustomElementInterface.cpp:
(WebCore::JSCustomElementInterface::constructHTMLElement): Added. Constructs a custom element by invoking its
constructor. We allow exceptions to be thrown by the constructor so the caller is responsible for checking
any exceptions in the ExecState before preceeding if the returned value is null.

* bindings/js/JSCustomElementInterface.h:
(WebCore::JSCustomElementInterface::constructSVGElement): Added.
* bindings/js/JSElementCustom.cpp:
(WebCore::toJSNewlyCreated): Exit early if the element is a custom element as the wrapper had already been
created by super() call inside the custom element'c constructor.

* bindings/js/JSMainThreadExecState.h:
(WebCore::JSMainThreadExecState):
* bindings/js/JSMainThreadExecStateInstrumentation.h:
(WebCore::JSMainThreadExecState::instrumentFunctionInternal): Generalized from instrumentFunctionCall so that
we can use it for both call and construct.
(WebCore::JSMainThreadExecState::instrumentFunctionCall): Specialized the above function for call.
(WebCore::JSMainThreadExecState::instrumentFunctionConstruct): Ditto for construct.

* dom/CustomElementDefinitions.cpp:
(WebCore::CustomElementDefinitions::findInterface): Added.
* dom/CustomElementDefinitions.h:

* dom/Document.cpp:
(WebCore::createHTMLElementWithNameValidation): Extracted from createElement.
(WebCore::Document::createElementForBindings): Renamed from createElement. Specifies
ShouldCreateCustomElement::Create to create a custom element before using fallback elements.
* dom/Document.h:
* dom/Document.idl:

* dom/Node.h:
(WebCore::Node::isCustomElement): Added. This flag is used to identify a custom element.
(WebCore::Node::setIsCustomElement): Added.

* dom/make_names.pl: Extracted createKnownElement from createElement for createHTMLElementWithNameValidation.

* inspector/InspectorCSSAgent.cpp:
(WebCore::InspectorCSSAgent::createInspectorStyleSheetForDocument): Use qualified name object to instantiate
a style element and set type content attribute.
* inspector/InspectorDOMAgent.cpp:
(WebCore::InspectorDOMAgent::setNodeName): Use createElementForBindings here since we might be creating an
arbitrary element here. Also use RefPtr instead of raw pointers while mutating DOM for safety.

Source/WebKit/win:

Use createElementForBindings here since this is a C++ binding for Windows.

* DOMCoreClasses.cpp:
(DOMDocument::createElement):

Source/WebKit2:

Use createElementForBindings here since this is for SPI.

* WebProcess/InjectedBundle/API/mac/WKDOMDocument.mm:
(-[WKDOMDocument createElement:]):
(-[WKDOMDocument createTextNode:]):

LayoutTests:

Add a test for creating a custom elemnet via document.createElement.
The behavior is to be documented later.

* fast/custom-elements/Document-createElement-expected.txt: Added.
* fast/custom-elements/Document-createElement.html: Added.

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

22 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/custom-elements/Document-createElement-expected.txt [new file with mode: 0644]
LayoutTests/fast/custom-elements/Document-createElement.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/JSCustomElementInterface.cpp
Source/WebCore/bindings/js/JSCustomElementInterface.h
Source/WebCore/bindings/js/JSElementCustom.cpp
Source/WebCore/bindings/js/JSMainThreadExecState.h
Source/WebCore/bindings/js/JSMainThreadExecStateInstrumentation.h
Source/WebCore/dom/CustomElementDefinitions.cpp
Source/WebCore/dom/CustomElementDefinitions.h
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/dom/Document.idl
Source/WebCore/dom/Node.h
Source/WebCore/dom/make_names.pl
Source/WebCore/inspector/InspectorCSSAgent.cpp
Source/WebCore/inspector/InspectorDOMAgent.cpp
Source/WebKit/win/ChangeLog
Source/WebKit/win/DOMCoreClasses.cpp
Source/WebKit2/ChangeLog
Source/WebKit2/WebProcess/InjectedBundle/API/mac/WKDOMDocument.mm

index aaf25bc..160dcee 100644 (file)
@@ -1,3 +1,16 @@
+2016-01-22  Ryosuke Niwa  <rniwa@webkit.org>
+
+        document.createElement should be able to create a custom element
+        https://bugs.webkit.org/show_bug.cgi?id=153173
+
+        Reviewed by Darin Adler.
+
+        Add a test for creating a custom elemnet via document.createElement.
+        The behavior is to be documented later.
+
+        * fast/custom-elements/Document-createElement-expected.txt: Added.
+        * fast/custom-elements/Document-createElement.html: Added.
+
 2016-01-25  Youenn Fablet  <youenn.fablet@crf.canon.fr>
 
         [Fetch API] Implement Fetch API Headers
diff --git a/LayoutTests/fast/custom-elements/Document-createElement-expected.txt b/LayoutTests/fast/custom-elements/Document-createElement-expected.txt
new file mode 100644 (file)
index 0000000..fc690db
--- /dev/null
@@ -0,0 +1,7 @@
+
+PASS document.createElement must create an instance of custom elements 
+PASS document.createElement must return null when a custom element constructor returns an object that is not an instance of Node 
+PASS document.createElement must return null when a custom element constructor returns a Text node 
+PASS document.createElement must return an element returned by a custom element constructor 
+PASS document.createElement must re-throw an exception thrown by a custom element constructor 
+
diff --git a/LayoutTests/fast/custom-elements/Document-createElement.html b/LayoutTests/fast/custom-elements/Document-createElement.html
new file mode 100644 (file)
index 0000000..ccbd099
--- /dev/null
@@ -0,0 +1,105 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: Extensions to Document interface</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="document.createElement should instantiate a custom element">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<link rel='stylesheet' href='../../resources/testharness.css'>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+test(function () {
+    class MyCustomElement extends HTMLElement {};
+
+    assert_true(document.createElement('my-custom-element') instanceof HTMLElement);
+    assert_false(document.createElement('my-custom-element') instanceof MyCustomElement);
+
+    document.defineCustomElement('my-custom-element', MyCustomElement);
+    var instance = document.createElement('my-custom-element');
+    assert_true(instance instanceof MyCustomElement);
+    assert_equals(instance.localName, 'my-custom-element');
+    assert_equals(instance.namespaceURI, 'http://www.w3.org/1999/xhtml', 'A custom element HTML must use HTML namespace');
+
+}, 'document.createElement must create an instance of custom elements');
+
+test(function () {
+    class ObjectCustomElement extends HTMLElement {
+        constructor()
+        {
+            return {foo: 'bar'};
+        }
+    };
+    document.defineCustomElement('object-custom-element', ObjectCustomElement);
+
+    var instance = new ObjectCustomElement;
+    assert_true(instance instanceof Object);
+    assert_equals(instance.foo, 'bar');
+
+    assert_equals(document.createElement('object-custom-element'), null);
+}, 'document.createElement must return null when a custom element constructor returns an object that is not an instance of Node');
+
+test(function () {
+    class TextCustomElement extends HTMLElement {
+        constructor()
+        {
+            return document.createTextNode('hello');
+        }
+    };
+    document.defineCustomElement('text-custom-element', TextCustomElement);
+    assert_true(new TextCustomElement instanceof Text);
+    assert_equals(document.createElement('object-custom-element'), null);
+}, 'document.createElement must return null when a custom element constructor returns a Text node');
+
+test(function () {
+    var createdElement = null;
+    class DivCustomElement extends HTMLElement {
+        constructor()
+        {
+            super();
+            createdElement = document.createElement('div');
+            return createdElement;
+        }
+    };
+    document.defineCustomElement('div-custom-element', DivCustomElement);
+    assert_true(new DivCustomElement instanceof HTMLDivElement);
+
+    var instance = document.createElement('div-custom-element');
+    assert_true(instance instanceof HTMLDivElement);
+    assert_equals(instance, createdElement);
+}, 'document.createElement must return an element returned by a custom element constructor');
+
+test(function () {
+    var exceptionToThrow = {message: 'exception thrown by a custom constructor'};
+    class ThrowCustomElement extends HTMLElement {
+        constructor()
+        {
+            super();
+            if (exceptionToThrow)
+                throw exceptionToThrow;
+        }
+    };
+    document.defineCustomElement('throw-custom-element', ThrowCustomElement);
+
+    assert_throws(null, function () { new ThrowCustomElement; });
+
+    try {
+        document.createElement('throw-custom-element');
+        assert(false, 'document.createElement must throw when a custom element constructor throws');
+    } catch (exception) {
+        assert_equals(exception, exceptionToThrow, 'document.createElement must throw the same exception custom element constructor throws');
+    }
+
+    exceptionToThrow = false;
+    var instance = document.createElement('throw-custom-element');
+    assert_true(instance instanceof ThrowCustomElement);
+    assert_equals(instance.localName, 'throw-custom-element');
+
+}, 'document.createElement must re-throw an exception thrown by a custom element constructor');
+
+</script>
+</body>
+</html>
index 1595d08..ef304f9 100644 (file)
@@ -1,3 +1,60 @@
+2016-01-22  Ryosuke Niwa  <rniwa@webkit.org>
+
+        document.createElement should be able to create a custom element
+        https://bugs.webkit.org/show_bug.cgi?id=153173
+
+        Reviewed by Darin Adler.
+
+        Added the support for constructing a custom element via document.createElement.
+
+        Extracted HTMLElementFactory::createKnownElement, which returns nullptr when the specified name doesn't match
+        any builtin element instead of out of HTMLUnknownElement, out of HTMLElementFactory::createElement.
+
+        Test: fast/custom-elements/Document-createElement.html
+
+        * bindings/js/JSCustomElementInterface.cpp:
+        (WebCore::JSCustomElementInterface::constructHTMLElement): Added. Constructs a custom element by invoking its
+        constructor. We allow exceptions to be thrown by the constructor so the caller is responsible for checking
+        any exceptions in the ExecState before preceeding if the returned value is null.
+
+        * bindings/js/JSCustomElementInterface.h:
+        (WebCore::JSCustomElementInterface::constructSVGElement): Added.
+        * bindings/js/JSElementCustom.cpp:
+        (WebCore::toJSNewlyCreated): Exit early if the element is a custom element as the wrapper had already been
+        created by super() call inside the custom element'c constructor.
+
+        * bindings/js/JSMainThreadExecState.h:
+        (WebCore::JSMainThreadExecState):
+        * bindings/js/JSMainThreadExecStateInstrumentation.h:
+        (WebCore::JSMainThreadExecState::instrumentFunctionInternal): Generalized from instrumentFunctionCall so that
+        we can use it for both call and construct.
+        (WebCore::JSMainThreadExecState::instrumentFunctionCall): Specialized the above function for call.
+        (WebCore::JSMainThreadExecState::instrumentFunctionConstruct): Ditto for construct.
+
+        * dom/CustomElementDefinitions.cpp:
+        (WebCore::CustomElementDefinitions::findInterface): Added.
+        * dom/CustomElementDefinitions.h:
+
+        * dom/Document.cpp:
+        (WebCore::createHTMLElementWithNameValidation): Extracted from createElement.
+        (WebCore::Document::createElementForBindings): Renamed from createElement. Specifies
+        ShouldCreateCustomElement::Create to create a custom element before using fallback elements.
+        * dom/Document.h:
+        * dom/Document.idl:
+
+        * dom/Node.h:
+        (WebCore::Node::isCustomElement): Added. This flag is used to identify a custom element.
+        (WebCore::Node::setIsCustomElement): Added.
+
+        * dom/make_names.pl: Extracted createKnownElement from createElement for createHTMLElementWithNameValidation.
+
+        * inspector/InspectorCSSAgent.cpp:
+        (WebCore::InspectorCSSAgent::createInspectorStyleSheetForDocument): Use qualified name object to instantiate
+        a style element and set type content attribute.
+        * inspector/InspectorDOMAgent.cpp:
+        (WebCore::InspectorDOMAgent::setNodeName): Use createElementForBindings here since we might be creating an
+        arbitrary element here. Also use RefPtr instead of raw pointers while mutating DOM for safety.
+
 2016-01-25  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         REGRESSION(r192773): [GTK] maps.google.com unresponsive/stalls since r192773
index dcef342..3a0013b 100644 (file)
@@ -31,9 +31,9 @@
 #if ENABLE(CUSTOM_ELEMENTS)
 
 #include "DOMWrapperWorld.h"
-#include "Element.h"
 #include "JSDOMGlobalObject.h"
 #include "JSElement.h"
+#include "JSHTMLElement.h"
 #include "JSMainThreadExecState.h"
 #include "JSMainThreadExecStateInstrumentation.h"
 #include "ScriptExecutionContext.h"
@@ -55,6 +55,49 @@ JSCustomElementInterface::~JSCustomElementInterface()
 {
 }
 
+RefPtr<Element> JSCustomElementInterface::constructElement(const AtomicString& tagName)
+{
+    if (!canInvokeCallback())
+        return nullptr;
+
+    Ref<JSCustomElementInterface> protect(*this);
+
+    JSLockHolder lock(m_isolatedWorld->vm());
+
+    if (!m_constructor)
+        return nullptr;
+
+    ScriptExecutionContext* context = scriptExecutionContext();
+    if (!context)
+        return nullptr;
+    ASSERT(context->isDocument());
+    JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(context, *m_isolatedWorld);
+    ExecState* state = globalObject->globalExec();
+
+    ConstructData constructData;
+    ConstructType constructType = m_constructor->methodTable()->getConstructData(m_constructor.get(), constructData);
+    if (constructType == ConstructTypeNone) {
+        ASSERT_NOT_REACHED();
+        return nullptr;
+    }
+
+    MarkedArgumentBuffer args;
+    args.append(jsStringWithCache(state, tagName));
+
+    InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionConstruct(context, constructType, constructData);
+    JSValue newElement = construct(state, m_constructor.get(), constructType, constructData, args);
+    InspectorInstrumentation::didCallFunction(cookie, context);
+
+    if (newElement.isEmpty())
+        return nullptr;
+
+    Element* wrappedElement = JSElement::toWrapped(newElement);
+    if (!wrappedElement)
+        return nullptr;
+    wrappedElement->setIsCustomElement();
+    return wrappedElement;
+}
+
 } // namespace WebCore
 
 #endif
index 96df1d5..6e4c9ed 100644 (file)
@@ -49,6 +49,8 @@ namespace WebCore {
 class DOMWrapperWorld;
 class Element;
 class JSDOMGlobalObject;
+class MathMLElement;
+class SVGElement;
 
 class JSCustomElementInterface : public RefCounted<JSCustomElementInterface>, public ActiveDOMCallback {
 public:
@@ -57,6 +59,8 @@ public:
         return adoptRef(*new JSCustomElementInterface(callback, globalObject));
     }
 
+    RefPtr<Element> constructElement(const AtomicString&);
+
     ScriptExecutionContext* scriptExecutionContext() const { return ContextDestructionObserver::scriptExecutionContext(); }
     JSC::JSObject* constructor() { return m_constructor.get(); }
 
index 1d65242..dd09e07 100644 (file)
@@ -54,6 +54,10 @@ JSValue toJSNewlyCreated(ExecState*, JSDOMGlobalObject* globalObject, Element* e
     if (!element)
         return jsNull();
 
+#if ENABLE(CUSTOM_ELEMENTS)
+    if (element->isCustomElement())
+        return getCachedWrapper(globalObject->world(), element);
+#endif
     ASSERT(!getCachedWrapper(globalObject->world(), element));
 
     JSDOMObject* wrapper;        
index 82828c8..dffc8f1 100644 (file)
@@ -93,6 +93,7 @@ public:
     }
 
     static InspectorInstrumentationCookie instrumentFunctionCall(ScriptExecutionContext*, JSC::CallType, const JSC::CallData&);
+    static InspectorInstrumentationCookie instrumentFunctionConstruct(ScriptExecutionContext*, JSC::ConstructType, const JSC::ConstructData&);
 
 private:
     explicit JSMainThreadExecState(JSC::ExecState* exec)
@@ -116,6 +117,8 @@ private:
             didLeaveScriptContext();
     }
 
+    template<typename Type, Type jsType, typename DataType> static InspectorInstrumentationCookie instrumentFunctionInternal(ScriptExecutionContext*, Type, const DataType&);
+
     static JSC::ExecState* s_mainThreadState;
     JSC::ExecState* m_previousState;
     JSC::JSLockHolder m_lock;
index 95477c2..4b12b45 100644 (file)
 
 namespace WebCore {
 
-inline InspectorInstrumentationCookie JSMainThreadExecState::instrumentFunctionCall(ScriptExecutionContext* context, JSC::CallType callType, const JSC::CallData& callData)
+template<typename Type, Type jsType, class DataType>
+inline InspectorInstrumentationCookie JSMainThreadExecState::instrumentFunctionInternal(ScriptExecutionContext* context, Type callType, const DataType& callData)
 {
     if (!InspectorInstrumentation::timelineAgentEnabled(context))
         return InspectorInstrumentationCookie();
     String resourceName;
     int lineNumber = 1;
-    if (callType == JSC::CallTypeJS) {
+    if (callType == jsType) {
         resourceName = callData.js.functionExecutable->sourceURL();
         lineNumber = callData.js.functionExecutable->firstLine();
     } else
@@ -47,6 +48,16 @@ inline InspectorInstrumentationCookie JSMainThreadExecState::instrumentFunctionC
     return InspectorInstrumentation::willCallFunction(context, resourceName, lineNumber);
 }
 
+inline InspectorInstrumentationCookie JSMainThreadExecState::instrumentFunctionCall(ScriptExecutionContext* context, JSC::CallType type, const JSC::CallData& data)
+{
+    return instrumentFunctionInternal<JSC::CallType, JSC::CallTypeJS, JSC::CallData>(context, type, data);
+}
+
+inline InspectorInstrumentationCookie JSMainThreadExecState::instrumentFunctionConstruct(ScriptExecutionContext* context, JSC::ConstructType type, const JSC::ConstructData& data)
+{
+    return instrumentFunctionInternal<JSC::ConstructType, JSC::ConstructTypeJS, JSC::ConstructData>(context, type, data);
+}
+
 } // namespace WebCore
 
 #endif // JSMainThreadExecStateInstrumentation_h
index 96f6196..9dae68f 100644 (file)
@@ -82,6 +82,12 @@ bool CustomElementDefinitions::defineElement(const QualifiedName& fullName, Ref<
     return true;
 }
 
+JSCustomElementInterface* CustomElementDefinitions::findInterface(const QualifiedName& name) const
+{
+    auto it = m_nameMap.find(name.localName());
+    return it == m_nameMap.end() || it->value.fullName != name ? nullptr : it->value.interface.get();
+}
+
 JSCustomElementInterface* CustomElementDefinitions::findInterface(const AtomicString& name) const
 {
     auto it = m_nameMap.find(name);
index 88459f7..e4dc2a6 100644 (file)
@@ -50,6 +50,7 @@ class CustomElementDefinitions {
 public:
     bool defineElement(const QualifiedName&, Ref<JSCustomElementInterface>&&);
 
+    JSCustomElementInterface* findInterface(const QualifiedName&) const;
     JSCustomElementInterface* findInterface(const AtomicString&) const;
     const QualifiedName& findName(const JSC::JSObject*) const;
 
index 756d892..21f9688 100644 (file)
@@ -90,6 +90,7 @@
 #include "HTMLScriptElement.h"
 #include "HTMLStyleElement.h"
 #include "HTMLTitleElement.h"
+#include "HTMLUnknownElement.h"
 #include "HTTPHeaderNames.h"
 #include "HTTPParsers.h"
 #include "HashChangeEvent.h"
@@ -882,18 +883,40 @@ void Document::childrenChanged(const ChildChange& change)
     clearStyleResolver();
 }
 
-RefPtr<Element> Document::createElement(const AtomicString& name, ExceptionCode& ec)
+static RefPtr<Element> createHTMLElementWithNameValidation(Document& document, const QualifiedName qualifiedName, ExceptionCode& ec)
 {
-    if (!isValidName(name)) {
+    RefPtr<HTMLElement> element = HTMLElementFactory::createKnownElement(qualifiedName, document);
+    if (LIKELY(element))
+        return element;
+
+#if ENABLE(CUSTOM_ELEMENTS)
+    auto* definitions = document.customElementDefinitions();
+    if (UNLIKELY(definitions)) {
+        if (auto* interface = definitions->findInterface(qualifiedName))
+            return interface->constructElement(qualifiedName.localName());
+    }
+#endif
+
+    if (UNLIKELY(!Document::isValidName(qualifiedName.localName()))) {
         ec = INVALID_CHARACTER_ERR;
         return nullptr;
     }
 
+    return HTMLUnknownElement::create(qualifiedName, document);
+}
+
+RefPtr<Element> Document::createElementForBindings(const AtomicString& name, ExceptionCode& ec)
+{
     if (isHTMLDocument())
-        return HTMLElementFactory::createElement(QualifiedName(nullAtom, name.convertToASCIILowercase(), xhtmlNamespaceURI), *this);
+        return createHTMLElementWithNameValidation(*this, QualifiedName(nullAtom, name.convertToASCIILowercase(), xhtmlNamespaceURI), ec);
 
     if (isXHTMLDocument())
-        return HTMLElementFactory::createElement(QualifiedName(nullAtom, name, xhtmlNamespaceURI), *this);
+        return createHTMLElementWithNameValidation(*this, QualifiedName(nullAtom, name, xhtmlNamespaceURI), ec);
+
+    if (!isValidName(name)) {
+        ec = INVALID_CHARACTER_ERR;
+        return nullptr;
+    }
 
     return createElement(QualifiedName(nullAtom, name, nullAtom), false);
 }
index 3d1c794..1ed994d 100644 (file)
@@ -383,7 +383,7 @@ public:
 
     bool hasManifest() const;
     
-    WEBCORE_EXPORT RefPtr<Element> createElement(const AtomicString& tagName, ExceptionCode&);
+    WEBCORE_EXPORT RefPtr<Element> createElementForBindings(const AtomicString& tagName, ExceptionCode&);
     WEBCORE_EXPORT Ref<DocumentFragment> createDocumentFragment();
     WEBCORE_EXPORT Ref<Text> createTextNode(const String& data);
     Ref<Comment> createComment(const String& data);
index aeb6012..5e60948 100644 (file)
@@ -31,7 +31,7 @@
     readonly attribute DOMImplementation implementation;
     readonly attribute Element documentElement;
 
-    [NewObject, RaisesException] Element createElement(DOMString tagName);
+    [NewObject, RaisesException, ImplementedAs=createElementForBindings] Element createElement(DOMString tagName);
     [NewObject] DocumentFragment   createDocumentFragment();
     [NewObject] Text createTextNode([Default=Undefined] optional DOMString data);
     [NewObject] Comment createComment([Default=Undefined] optional DOMString data);
index 8d32ffb..a9a716d 100644 (file)
@@ -286,6 +286,11 @@ public:
     HTMLSlotElement* assignedSlot() const;
 #endif
 
+#if ENABLE(CUSTOM_ELEMENTS)
+    bool isCustomElement() const { return getFlag(IsCustomElement); }
+    void setIsCustomElement() { return setFlag(IsCustomElement); }
+#endif
+
     // Returns null, a child of ShadowRoot, or a legacy shadow root.
     Node* nonBoundaryShadowTreeRootNode();
 
@@ -617,7 +622,7 @@ protected:
         HasSyntheticAttrChildNodesFlag = 1 << 19,
         HasCustomStyleResolveCallbacksFlag = 1 << 20,
         HasEventTargetDataFlag = 1 << 21,
-        // HeyItIsAFreeBit = 1 << 22,
+        IsCustomElement = 1 << 22,
         IsInShadowTreeFlag = 1 << 23,
         IsMathMLFlag = 1 << 24,
 
index d52d3c9..8c2a2d2 100755 (executable)
@@ -978,6 +978,13 @@ END
     ;
 
     my %tagConstructorMap = buildConstructorMap();
+    my $argumentList;
+
+    if ($parameters{namespace} eq "HTML") {
+        $argumentList = "name, document, formElement, createdByParser";
+    } else {
+        $argumentList = "name, document, createdByParser";
+    }
 
     printConstructors($F, \%tagConstructorMap);
 
@@ -1002,22 +1009,22 @@ END
         map.add(table[i].name.localName().impl(), table[i].function);
 }
 
-Ref<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createElement(const QualifiedName& name, Document& document$formElementArgumentForDefinition, bool createdByParser)
+RefPtr<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createKnownElement(const QualifiedName& name, Document& document$formElementArgumentForDefinition, bool createdByParser)
 {
     static NeverDestroyed<HashMap<AtomicStringImpl*, $parameters{namespace}ConstructorFunction>> functions;
     if (functions.get().isEmpty())
         populate$parameters{namespace}FactoryMap(functions);
-    if ($parameters{namespace}ConstructorFunction function = functions.get().get(name.localName().impl()))
-END
-    ;
-
-    if ($parameters{namespace} eq "HTML") {
-        print F "        return function(name, document, formElement, createdByParser);\n";
-    } else {
-        print F "        return function(name, document, createdByParser);\n";
-    }
+    $parameters{namespace}ConstructorFunction function = functions.get().get(name.localName().impl());
+    if (LIKELY(function))
+        return function($argumentList);
+    return nullptr;
+}
 
-    print F <<END
+Ref<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createElement(const QualifiedName& name, Document& document$formElementArgumentForDefinition, bool createdByParser)
+{
+    RefPtr<$parameters{namespace}Element> element = $parameters{namespace}ElementFactory::createKnownElement($argumentList);
+    if (LIKELY(element))
+        return element.releaseNonNull();
     return $parameters{fallbackInterfaceName}::create(name, document);
 }
 
@@ -1058,6 +1065,9 @@ namespace WebCore {
 END
 ;
 
+print F "        static RefPtr<$parameters{namespace}Element> createKnownElement(const QualifiedName&, Document&";
+print F ", HTMLFormElement* = nullptr" if $parameters{namespace} eq "HTML";
+print F ", bool createdByParser = false);\n\n";
 print F "        static Ref<$parameters{namespace}Element> createElement(const QualifiedName&, Document&";
 print F ", HTMLFormElement* = nullptr" if $parameters{namespace} eq "HTML";
 print F ", bool createdByParser = false);\n";
index 627814c..9ffea27 100644 (file)
@@ -776,14 +776,8 @@ InspectorStyleSheet* InspectorCSSAgent::createInspectorStyleSheetForDocument(Doc
     if (!document.isHTMLDocument() && !document.isSVGDocument())
         return nullptr;
 
-    ExceptionCode ec = 0;
-    RefPtr<Element> styleElement = document.createElement("style", ec);
-    if (ec)
-        return nullptr;
-
-    styleElement->setAttribute("type", "text/css", ec);
-    if (ec)
-        return nullptr;
+    Ref<Element> styleElement = document.createElement(HTMLNames::styleTag, false);
+    styleElement->setAttribute(HTMLNames::typeAttr, "text/css");
 
     ContainerNode* targetNode;
     // HEAD is absent in ImageDocuments, for example.
@@ -799,7 +793,8 @@ InspectorStyleSheet* InspectorCSSAgent::createInspectorStyleSheetForDocument(Doc
     // Set this flag, so when we create it, we put it into the via inspector map.
     m_creatingViaInspectorStyleSheet = true;
     InlineStyleOverrideScope overrideScope(document);
-    targetNode->appendChild(styleElement.releaseNonNull(), ec);
+    ExceptionCode ec = 0;
+    targetNode->appendChild(WTFMove(styleElement), ec);
     m_creatingViaInspectorStyleSheet = false;
     if (ec)
         return nullptr;
index 4fa946e..af9b101 100644 (file)
@@ -714,33 +714,33 @@ void InspectorDOMAgent::setNodeName(ErrorString& errorString, int nodeId, const
 {
     *newId = 0;
 
-    Node* oldNode = nodeForId(nodeId);
-    if (!is<Element>(oldNode))
+    RefPtr<Node> oldNode = nodeForId(nodeId);
+    if (!is<Element>(oldNode.get()))
         return;
 
     ExceptionCode ec = 0;
-    RefPtr<Element> newElem = oldNode->document().createElement(tagName, ec);
+    RefPtr<Element> newElement = oldNode->document().createElementForBindings(tagName, ec);
     if (ec)
         return;
 
     // Copy over the original node's attributes.
-    newElem->cloneAttributesFromElement(*downcast<Element>(oldNode));
+    newElement->cloneAttributesFromElement(downcast<Element>(*oldNode));
 
     // Copy over the original node's children.
-    Node* child;
+    RefPtr<Node> child;
     while ((child = oldNode->firstChild())) {
-        if (!m_domEditor->insertBefore(newElem.get(), child, 0, errorString))
+        if (!m_domEditor->insertBefore(newElement.get(), child.get(), 0, errorString))
             return;
     }
 
     // Replace the old node with the new node
-    ContainerNode* parent = oldNode->parentNode();
-    if (!m_domEditor->insertBefore(parent, newElem.get(), oldNode->nextSibling(), errorString))
+    RefPtr<ContainerNode> parent = oldNode->parentNode();
+    if (!m_domEditor->insertBefore(parent.get(), newElement.get(), oldNode->nextSibling(), errorString))
         return;
-    if (!m_domEditor->removeChild(parent, oldNode, errorString))
+    if (!m_domEditor->removeChild(parent.get(), oldNode.get(), errorString))
         return;
 
-    *newId = pushNodePathToFrontend(newElem.get());
+    *newId = pushNodePathToFrontend(newElement.get());
     if (m_childrenRequested.contains(nodeId))
         pushChildNodesToFrontend(*newId);
 }
index 8178f27..025bdf2 100644 (file)
@@ -1,3 +1,15 @@
+2016-01-22  Ryosuke Niwa  <rniwa@webkit.org>
+
+        document.createElement should be able to create a custom element
+        https://bugs.webkit.org/show_bug.cgi?id=153173
+
+        Reviewed by Darin Adler.
+
+        Use createElementForBindings here since this is a C++ binding for Windows.
+
+        * DOMCoreClasses.cpp:
+        (DOMDocument::createElement):
+
 2016-01-24  Gyuyoung Kim  <gyuyoung.kim@webkit.org>
 
         Reduce PassRefPtr uses in dom - 4
index 8298a22..7c15db7 100644 (file)
@@ -635,7 +635,7 @@ HRESULT DOMDocument::createElement(_In_ BSTR tagName, _COM_Outptr_opt_ IDOMEleme
 
     String tagNameString(tagName);
     ExceptionCode ec;
-    *result = DOMElement::createInstance(m_document->createElement(tagNameString, ec).get());
+    *result = DOMElement::createInstance(m_document->createElementForBindings(tagNameString, ec).get());
     return *result ? S_OK : E_FAIL;
 }
 
index 2b65463..97bff53 100644 (file)
@@ -1,3 +1,16 @@
+2016-01-22  Ryosuke Niwa  <rniwa@webkit.org>
+
+        document.createElement should be able to create a custom element
+        https://bugs.webkit.org/show_bug.cgi?id=153173
+
+        Reviewed by Darin Adler.
+
+        Use createElementForBindings here since this is for SPI.
+
+        * WebProcess/InjectedBundle/API/mac/WKDOMDocument.mm:
+        (-[WKDOMDocument createElement:]):
+        (-[WKDOMDocument createTextNode:]):
+
 2016-01-24  Alex Christensen  <achristensen@webkit.org>
 
         Report upload progress to NetworkLoadClient when using NetworkSession
index 1507091..6396ed2 100644 (file)
@@ -39,7 +39,7 @@
 {
     // FIXME: Do something about the exception.
     WebCore::ExceptionCode ec = 0;
-    return WebKit::toWKDOMElement(downcast<WebCore::Document>(*_impl).createElement(tagName, ec).get());
+    return WebKit::toWKDOMElement(downcast<WebCore::Document>(*_impl).createElementForBindings(tagName, ec).get());
 }
 
 - (WKDOMText *)createTextNode:(NSString *)data