Add document.defineCustomElement
[WebKit.git] / Source / WebCore / bindings / js / JSDocumentCustom.cpp
1 /*
2  * Copyright (C) 2007, 2008, 2009, 2011 Apple Inc. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21 #include "JSDocument.h"
22
23 #include "CustomElementDefinitions.h"
24 #include "ExceptionCode.h"
25 #include "Frame.h"
26 #include "FrameLoader.h"
27 #include "HTMLDocument.h"
28 #include "JSCanvasRenderingContext2D.h"
29 #include "JSDOMWindowCustom.h"
30 #include "JSHTMLDocument.h"
31 #include "JSLocation.h"
32 #include "JSNodeOrString.h"
33 #include "JSSVGDocument.h"
34 #include "Location.h"
35 #include "NodeTraversal.h"
36 #include "SVGDocument.h"
37 #include "ScriptController.h"
38 #include "TouchList.h"
39 #include <wtf/GetPtr.h>
40
41 #if ENABLE(WEBGL)
42 #include "JSWebGLRenderingContextBase.h"
43 #endif
44
45 #if ENABLE(TOUCH_EVENTS)
46 #include "JSTouch.h"
47 #include "JSTouchList.h"
48 #endif
49
50 using namespace JSC;
51
52 namespace WebCore {
53
54 static inline JSValue createNewDocumentWrapper(ExecState& state, JSDOMGlobalObject& globalObject, Document& document)
55 {
56     JSObject* wrapper;
57     if (document.isHTMLDocument())
58         wrapper = CREATE_DOM_WRAPPER(&globalObject, HTMLDocument, &document);
59     else if (document.isSVGDocument())
60         wrapper = CREATE_DOM_WRAPPER(&globalObject, SVGDocument, &document);
61     else
62         wrapper = CREATE_DOM_WRAPPER(&globalObject, Document, &document);
63
64     // Make sure the document is kept around by the window object, and works right with the
65     // back/forward cache.
66     if (!document.frame()) {
67         size_t nodeCount = 0;
68         for (Node* n = &document; n; n = NodeTraversal::next(*n))
69             ++nodeCount;
70
71         // FIXME: Adopt reportExtraMemoryVisited, and switch to reportExtraMemoryAllocated.
72         // https://bugs.webkit.org/show_bug.cgi?id=142595
73         state.heap()->deprecatedReportExtraMemory(nodeCount * sizeof(Node));
74     }
75
76     return wrapper;
77 }
78
79 JSValue toJS(ExecState* state, JSDOMGlobalObject* globalObject, Document* document)
80 {
81     if (!document)
82         return jsNull();
83
84     JSObject* wrapper = getCachedWrapper(globalObject->world(), document);
85     if (wrapper)
86         return wrapper;
87
88     if (DOMWindow* domWindow = document->domWindow()) {
89         globalObject = toJSDOMWindow(toJS(state, domWindow));
90         // Creating a wrapper for domWindow might have created a wrapper for document as well.
91         wrapper = getCachedWrapper(globalObject->world(), document);
92         if (wrapper)
93             return wrapper;
94     }
95
96     return createNewDocumentWrapper(*state, *globalObject, *document);
97 }
98
99 JSValue toJSNewlyCreated(ExecState* state, JSDOMGlobalObject* globalObject, Document* document)
100 {
101     return document ? createNewDocumentWrapper(*state, *globalObject, *document) : jsNull();
102 }
103
104 JSValue JSDocument::prepend(ExecState& state)
105 {
106     ExceptionCode ec = 0;
107     wrapped().prepend(toNodeOrStringVector(state), ec);
108     setDOMException(&state, ec);
109
110     return jsUndefined();
111 }
112
113 JSValue JSDocument::append(ExecState& state)
114 {
115     ExceptionCode ec = 0;
116     wrapped().append(toNodeOrStringVector(state), ec);
117     setDOMException(&state, ec);
118
119     return jsUndefined();
120 }
121
122 #if ENABLE(TOUCH_EVENTS)
123 JSValue JSDocument::createTouchList(ExecState& state)
124 {
125     RefPtr<TouchList> touchList = TouchList::create();
126
127     for (size_t i = 0; i < state.argumentCount(); i++)
128         touchList->append(JSTouch::toWrapped(state.argument(i)));
129
130     return toJS(&state, globalObject(), touchList.release());
131 }
132 #endif
133
134 #if ENABLE(CUSTOM_ELEMENTS)
135 JSValue JSDocument::defineCustomElement(ExecState& state)
136 {
137     AtomicString tagName(state.argument(0).toString(&state)->toAtomicString(&state));
138     if (UNLIKELY(state.hadException()))
139         return jsUndefined();
140
141     JSObject* object = state.argument(1).getObject();
142     ConstructData callData;
143     if (!object || object->methodTable()->getConstructData(object, callData) == ConstructTypeNone)
144         return throwTypeError(&state, "The second argument must be a constructor");
145
146     Document& document = wrapped();
147     switch (CustomElementDefinitions::checkName(tagName)) {
148     case CustomElementDefinitions::NameStatus::Valid:
149         break;
150     case CustomElementDefinitions::NameStatus::ConflictsWithBuiltinNames:
151         return throwSyntaxError(&state, "Custom element name cannot be same as one of the builtin elements");
152     case CustomElementDefinitions::NameStatus::NoHyphen:
153         return throwSyntaxError(&state, "Custom element name must contain a hyphen");
154     case CustomElementDefinitions::NameStatus::ContainsUpperCase:
155         return throwSyntaxError(&state, "Custom element name cannot contain an upper case letter");
156     }
157
158     QualifiedName name(nullAtom, tagName, HTMLNames::xhtmlNamespaceURI);
159     auto& definitions = document.ensureCustomElementDefinitions();
160     if (definitions.findInterface(tagName)) {
161         ExceptionCodeWithMessage ec;
162         ec.code = NOT_SUPPORTED_ERR;
163         ec.message = "Cannot define multiple custom elements with the same tag name";
164         setDOMException(&state, ec);
165         return jsUndefined();
166     }
167     definitions.defineElement(name, JSCustomElementInterface::create(object, globalObject()));
168     PrivateName uniquePrivateName;
169     globalObject()->putDirect(globalObject()->vm(), uniquePrivateName, object);
170
171     return jsUndefined();
172 }
173 #endif
174
175 } // namespace WebCore