0d3c20f365689d783c9477589fc1da09e9abac46
[WebKit-https.git] / Source / WebCore / bindings / js / JSNodeCustom.cpp
1 /*
2  * Copyright (C) 2007, 2009, 2010 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "JSNode.h"
28
29 #include "Attr.h"
30 #include "CDATASection.h"
31 #include "Comment.h"
32 #include "Document.h"
33 #include "DocumentFragment.h"
34 #include "DocumentType.h"
35 #include "Entity.h"
36 #include "EntityReference.h"
37 #include "ExceptionCode.h"
38 #include "HTMLAudioElement.h"
39 #include "HTMLCanvasElement.h"
40 #include "HTMLElement.h"
41 #include "HTMLFrameElementBase.h"
42 #include "HTMLImageElement.h"
43 #include "HTMLLinkElement.h"
44 #include "HTMLNames.h"
45 #include "HTMLScriptElement.h"
46 #include "HTMLStyleElement.h"
47 #include "JSAttr.h"
48 #include "JSCDATASection.h"
49 #include "JSComment.h"
50 #include "JSDOMBinding.h"
51 #include "JSDocument.h"
52 #include "JSDocumentFragment.h"
53 #include "JSDocumentType.h"
54 #include "JSEntity.h"
55 #include "JSEntityReference.h"
56 #include "JSEventListener.h"
57 #include "JSHTMLElement.h"
58 #include "JSHTMLElementWrapperFactory.h"
59 #include "JSNotation.h"
60 #include "JSProcessingInstruction.h"
61 #include "JSText.h"
62 #include "Node.h"
63 #include "Notation.h"
64 #include "ProcessingInstruction.h"
65 #include "RegisteredEventListener.h"
66 #include "ShadowRoot.h"
67 #include "StyleSheet.h"
68 #include "StyledElement.h"
69 #include "Text.h"
70 #include <wtf/PassRefPtr.h>
71 #include <wtf/RefPtr.h>
72
73 #if ENABLE(SVG)
74 #include "JSSVGElementWrapperFactory.h"
75 #include "SVGElement.h"
76 #endif
77
78 using namespace JSC;
79
80 namespace WebCore {
81
82 using namespace HTMLNames;
83
84 static inline bool isObservable(JSNode* jsNode, Node* node)
85 {
86     // The root node keeps the tree intact.
87     if (!node->parentNode())
88         return true;
89
90     if (jsNode->hasCustomProperties())
91         return true;
92
93     // A node's JS wrapper is responsible for marking its JS event listeners.
94     if (node->hasEventListeners())
95         return true;
96
97     return false;
98 }
99
100 static inline bool isReachableFromDOM(JSNode* jsNode, Node* node, SlotVisitor& visitor)
101 {
102     if (!node->inDocument()) {
103         // If a wrapper is the last reference to an image element
104         // that is loading but not in the document, the wrapper is observable
105         // because it is the only thing keeping the image element alive, and if
106         // the element is destroyed, its load event will not fire.
107         // FIXME: The DOM should manage this issue without the help of JavaScript wrappers.
108         if (isHTMLImageElement(node)) {
109             if (toHTMLImageElement(node)->hasPendingActivity())
110                 return true;
111         }
112     #if ENABLE(VIDEO)
113         else if (isHTMLAudioElement(node)) {
114             if (!toHTMLAudioElement(node)->paused())
115                 return true;
116         }
117     #endif
118
119         // If a node is firing event listeners, its wrapper is observable because
120         // its wrapper is responsible for marking those event listeners.
121         if (node->isFiringEventListeners())
122             return true;
123     }
124
125     return isObservable(jsNode, node) && visitor.containsOpaqueRoot(root(node));
126 }
127
128 bool JSNodeOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, SlotVisitor& visitor)
129 {
130     JSNode* jsNode = jsCast<JSNode*>(handle.get().asCell());
131     return isReachableFromDOM(jsNode, jsNode->impl(), visitor);
132 }
133
134 void JSNodeOwner::finalize(JSC::Handle<JSC::Unknown> handle, void* context)
135 {
136     JSNode* jsNode = static_cast<JSNode*>(handle.get().asCell());
137     DOMWrapperWorld* world = static_cast<DOMWrapperWorld*>(context);
138     uncacheWrapper(world, jsNode->impl(), jsNode);
139     jsNode->releaseImpl();
140 }
141
142 JSValue JSNode::insertBefore(ExecState* exec)
143 {
144     Node* imp = static_cast<Node*>(impl());
145     ExceptionCode ec = 0;
146     bool ok = imp->insertBefore(toNode(exec->argument(0)), toNode(exec->argument(1)), ec, AttachLazily);
147     setDOMException(exec, ec);
148     if (ok)
149         return exec->argument(0);
150     return jsNull();
151 }
152
153 JSValue JSNode::replaceChild(ExecState* exec)
154 {
155     Node* imp = static_cast<Node*>(impl());
156     ExceptionCode ec = 0;
157     bool ok = imp->replaceChild(toNode(exec->argument(0)), toNode(exec->argument(1)), ec, AttachLazily);
158     setDOMException(exec, ec);
159     if (ok)
160         return exec->argument(1);
161     return jsNull();
162 }
163
164 JSValue JSNode::removeChild(ExecState* exec)
165 {
166     Node* imp = static_cast<Node*>(impl());
167     ExceptionCode ec = 0;
168     bool ok = imp->removeChild(toNode(exec->argument(0)), ec);
169     setDOMException(exec, ec);
170     if (ok)
171         return exec->argument(0);
172     return jsNull();
173 }
174
175 JSValue JSNode::appendChild(ExecState* exec)
176 {
177     Node* imp = static_cast<Node*>(impl());
178     ExceptionCode ec = 0;
179     bool ok = imp->appendChild(toNode(exec->argument(0)), ec, AttachLazily);
180     setDOMException(exec, ec);
181     if (ok)
182         return exec->argument(0);
183     return jsNull();
184 }
185
186 JSScope* JSNode::pushEventHandlerScope(ExecState* exec, JSScope* node) const
187 {
188     if (inherits(&JSHTMLElement::s_info))
189         return jsCast<const JSHTMLElement*>(this)->pushEventHandlerScope(exec, node);
190     return node;
191 }
192
193 void JSNode::visitChildren(JSCell* cell, SlotVisitor& visitor)
194 {
195     JSNode* thisObject = jsCast<JSNode*>(cell);
196     ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
197     COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
198     ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
199     Base::visitChildren(thisObject, visitor);
200
201     Node* node = thisObject->impl();
202     node->visitJSEventListeners(visitor);
203
204     visitor.addOpaqueRoot(root(node));
205 }
206
207 static ALWAYS_INLINE JSValue createWrapperInline(ExecState* exec, JSDOMGlobalObject* globalObject, Node* node)
208 {
209     ASSERT(node);
210     ASSERT(!getCachedWrapper(currentWorld(exec), node));
211     
212     JSDOMWrapper* wrapper;    
213     switch (node->nodeType()) {
214         case Node::ELEMENT_NODE:
215             if (node->isHTMLElement())
216                 wrapper = createJSHTMLWrapper(exec, globalObject, toHTMLElement(node));
217 #if ENABLE(SVG)
218             else if (node->isSVGElement())
219                 wrapper = createJSSVGWrapper(exec, globalObject, toSVGElement(node));
220 #endif
221             else
222                 wrapper = CREATE_DOM_WRAPPER(exec, globalObject, Element, node);
223             break;
224         case Node::ATTRIBUTE_NODE:
225             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, Attr, node);
226             break;
227         case Node::TEXT_NODE:
228             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, Text, node);
229             break;
230         case Node::CDATA_SECTION_NODE:
231             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, CDATASection, node);
232             break;
233         case Node::ENTITY_NODE:
234             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, Entity, node);
235             break;
236         case Node::PROCESSING_INSTRUCTION_NODE:
237             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, ProcessingInstruction, node);
238             break;
239         case Node::COMMENT_NODE:
240             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, Comment, node);
241             break;
242         case Node::DOCUMENT_NODE:
243             // we don't want to cache the document itself in the per-document dictionary
244             return toJS(exec, globalObject, toDocument(node));
245         case Node::DOCUMENT_TYPE_NODE:
246             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, DocumentType, node);
247             break;
248         case Node::NOTATION_NODE:
249             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, Notation, node);
250             break;
251         case Node::DOCUMENT_FRAGMENT_NODE:
252             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, DocumentFragment, node);
253             break;
254         case Node::ENTITY_REFERENCE_NODE:
255             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, EntityReference, node);
256             break;
257         default:
258             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, Node, node);
259     }
260
261     return wrapper;    
262 }
263
264 JSValue createWrapper(ExecState* exec, JSDOMGlobalObject* globalObject, Node* node)
265 {
266     return createWrapperInline(exec, globalObject, node);
267 }
268     
269 JSValue toJSNewlyCreated(ExecState* exec, JSDOMGlobalObject* globalObject, Node* node)
270 {
271     if (!node)
272         return jsNull();
273     
274     return createWrapperInline(exec, globalObject, node);
275 }
276
277 void willCreatePossiblyOrphanedTreeByRemovalSlowCase(Node* root)
278 {
279     ScriptState* scriptState = mainWorldScriptState(root->document()->frame());
280     if (!scriptState)
281         return;
282
283     JSLockHolder lock(scriptState);
284     toJS(scriptState, static_cast<JSDOMGlobalObject*>(scriptState->lexicalGlobalObject()), root);
285 }
286
287 } // namespace WebCore