99f2d5522bcf90a049d024b452b6f3e0c9005b44
[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 "CachedImage.h"
31 #include "CachedScript.h"
32 #include "CDATASection.h"
33 #include "Comment.h"
34 #include "Document.h"
35 #include "DocumentFragment.h"
36 #include "DocumentType.h"
37 #include "Entity.h"
38 #include "EntityReference.h"
39 #include "ExceptionCode.h"
40 #include "HTMLAudioElement.h"
41 #include "HTMLCanvasElement.h"
42 #include "HTMLElement.h"
43 #include "HTMLFrameElementBase.h"
44 #include "HTMLImageElement.h"
45 #include "HTMLLinkElement.h"
46 #include "HTMLNames.h"
47 #include "HTMLScriptElement.h"
48 #include "HTMLStyleElement.h"
49 #include "JSAttr.h"
50 #include "JSCDATASection.h"
51 #include "JSComment.h"
52 #include "JSDOMBinding.h"
53 #include "JSDocument.h"
54 #include "JSDocumentFragment.h"
55 #include "JSDocumentType.h"
56 #include "JSEntity.h"
57 #include "JSEntityReference.h"
58 #include "JSEventListener.h"
59 #include "JSHTMLElement.h"
60 #include "JSHTMLElementWrapperFactory.h"
61 #include "JSNotation.h"
62 #include "JSProcessingInstruction.h"
63 #include "JSText.h"
64 #include "Node.h"
65 #include "Notation.h"
66 #include "ProcessingInstruction.h"
67 #include "RegisteredEventListener.h"
68 #include "ShadowRoot.h"
69 #include "StyleSheet.h"
70 #include "StyledElement.h"
71 #include "Text.h"
72 #include <wtf/PassRefPtr.h>
73 #include <wtf/RefPtr.h>
74
75 #if ENABLE(SVG)
76 #include "JSSVGElementWrapperFactory.h"
77 #include "SVGElement.h"
78 #endif
79
80 using namespace JSC;
81
82 namespace WebCore {
83
84 using namespace HTMLNames;
85
86 static inline bool isObservable(JSNode* jsNode, Node* node)
87 {
88     // The root node keeps the tree intact.
89     if (!node->parentNode())
90         return true;
91
92     if (jsNode->hasCustomProperties())
93         return true;
94
95     // A node's JS wrapper is responsible for marking its JS event listeners.
96     if (node->hasEventListeners())
97         return true;
98
99     return false;
100 }
101
102 static inline bool isReachableFromDOM(JSNode* jsNode, Node* node, SlotVisitor& visitor)
103 {
104     if (!node->inDocument()) {
105         // If a wrapper is the last reference to an image element
106         // that is loading but not in the document, the wrapper is observable
107         // because it is the only thing keeping the image element alive, and if
108         // the element is destroyed, its load event will not fire.
109         // FIXME: The DOM should manage this issue without the help of JavaScript wrappers.
110         if (node->hasTagName(imgTag)) {
111             if (static_cast<HTMLImageElement*>(node)->hasPendingActivity())
112                 return true;
113         }
114     #if ENABLE(VIDEO)
115         else if (node->hasTagName(audioTag)) {
116             if (!static_cast<HTMLAudioElement*>(node)->paused())
117                 return true;
118         }
119     #endif
120
121         // If a node is firing event listeners, its wrapper is observable because
122         // its wrapper is responsible for marking those event listeners.
123         if (node->isFiringEventListeners())
124             return true;
125     }
126
127     return isObservable(jsNode, node) && visitor.containsOpaqueRoot(root(node));
128 }
129
130 bool JSNodeOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, SlotVisitor& visitor)
131 {
132     JSNode* jsNode = jsCast<JSNode*>(handle.get().asCell());
133     return isReachableFromDOM(jsNode, jsNode->impl(), visitor);
134 }
135
136 void JSNodeOwner::finalize(JSC::Handle<JSC::Unknown> handle, void* context)
137 {
138     JSNode* jsNode = static_cast<JSNode*>(handle.get().asCell());
139     DOMWrapperWorld* world = static_cast<DOMWrapperWorld*>(context);
140     uncacheWrapper(world, jsNode->impl(), jsNode);
141     jsNode->releaseImpl();
142 }
143
144 JSValue JSNode::insertBefore(ExecState* exec)
145 {
146     Node* imp = static_cast<Node*>(impl());
147     ExceptionCode ec = 0;
148     bool ok = imp->insertBefore(toNode(exec->argument(0)), toNode(exec->argument(1)), ec, AttachLazily);
149     setDOMException(exec, ec);
150     if (ok)
151         return exec->argument(0);
152     return jsNull();
153 }
154
155 JSValue JSNode::replaceChild(ExecState* exec)
156 {
157     Node* imp = static_cast<Node*>(impl());
158     ExceptionCode ec = 0;
159     bool ok = imp->replaceChild(toNode(exec->argument(0)), toNode(exec->argument(1)), ec, AttachLazily);
160     setDOMException(exec, ec);
161     if (ok)
162         return exec->argument(1);
163     return jsNull();
164 }
165
166 JSValue JSNode::removeChild(ExecState* exec)
167 {
168     Node* imp = static_cast<Node*>(impl());
169     ExceptionCode ec = 0;
170     bool ok = imp->removeChild(toNode(exec->argument(0)), ec);
171     setDOMException(exec, ec);
172     if (ok)
173         return exec->argument(0);
174     return jsNull();
175 }
176
177 JSValue JSNode::appendChild(ExecState* exec)
178 {
179     Node* imp = static_cast<Node*>(impl());
180     ExceptionCode ec = 0;
181     bool ok = imp->appendChild(toNode(exec->argument(0)), ec, AttachLazily);
182     setDOMException(exec, ec);
183     if (ok)
184         return exec->argument(0);
185     return jsNull();
186 }
187
188 JSScope* JSNode::pushEventHandlerScope(ExecState* exec, JSScope* node) const
189 {
190     if (inherits(&JSHTMLElement::s_info))
191         return jsCast<const JSHTMLElement*>(this)->pushEventHandlerScope(exec, node);
192     return node;
193 }
194
195 void JSNode::visitChildren(JSCell* cell, SlotVisitor& visitor)
196 {
197     JSNode* thisObject = jsCast<JSNode*>(cell);
198     ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
199     COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
200     ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
201     Base::visitChildren(thisObject, visitor);
202
203     Node* node = thisObject->impl();
204     node->visitJSEventListeners(visitor);
205
206     visitor.addOpaqueRoot(root(node));
207 }
208
209 static ALWAYS_INLINE JSValue createWrapperInline(ExecState* exec, JSDOMGlobalObject* globalObject, Node* node)
210 {
211     ASSERT(node);
212     ASSERT(!getCachedWrapper(currentWorld(exec), node));
213     
214     JSDOMWrapper* wrapper;    
215     switch (node->nodeType()) {
216         case Node::ELEMENT_NODE:
217             if (node->isHTMLElement())
218                 wrapper = createJSHTMLWrapper(exec, globalObject, toHTMLElement(node));
219 #if ENABLE(SVG)
220             else if (node->isSVGElement())
221                 wrapper = createJSSVGWrapper(exec, globalObject, static_cast<SVGElement*>(node));
222 #endif
223             else
224                 wrapper = CREATE_DOM_WRAPPER(exec, globalObject, Element, node);
225             break;
226         case Node::ATTRIBUTE_NODE:
227             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, Attr, node);
228             break;
229         case Node::TEXT_NODE:
230             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, Text, node);
231             break;
232         case Node::CDATA_SECTION_NODE:
233             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, CDATASection, node);
234             break;
235         case Node::ENTITY_NODE:
236             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, Entity, node);
237             break;
238         case Node::PROCESSING_INSTRUCTION_NODE:
239             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, ProcessingInstruction, node);
240             break;
241         case Node::COMMENT_NODE:
242             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, Comment, node);
243             break;
244         case Node::DOCUMENT_NODE:
245             // we don't want to cache the document itself in the per-document dictionary
246             return toJS(exec, globalObject, static_cast<Document*>(node));
247         case Node::DOCUMENT_TYPE_NODE:
248             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, DocumentType, node);
249             break;
250         case Node::NOTATION_NODE:
251             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, Notation, node);
252             break;
253         case Node::DOCUMENT_FRAGMENT_NODE:
254             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, DocumentFragment, node);
255             break;
256         case Node::ENTITY_REFERENCE_NODE:
257             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, EntityReference, node);
258             break;
259         default:
260             wrapper = CREATE_DOM_WRAPPER(exec, globalObject, Node, node);
261     }
262
263     return wrapper;    
264 }
265
266 JSValue createWrapper(ExecState* exec, JSDOMGlobalObject* globalObject, Node* node)
267 {
268     return createWrapperInline(exec, globalObject, node);
269 }
270     
271 JSValue toJSNewlyCreated(ExecState* exec, JSDOMGlobalObject* globalObject, Node* node)
272 {
273     if (!node)
274         return jsNull();
275     
276     return createWrapperInline(exec, globalObject, node);
277 }
278
279 void willCreatePossiblyOrphanedTreeByRemovalSlowCase(Node* root)
280 {
281     ScriptState* scriptState = mainWorldScriptState(root->document()->frame());
282     if (!scriptState)
283         return;
284
285     JSLockHolder lock(scriptState);
286     toJS(scriptState, static_cast<JSDOMGlobalObject*>(scriptState->lexicalGlobalObject()), root);
287 }
288
289 } // namespace WebCore