515f0b1d69eee16d124ea48df908b36514a2bcf2
[WebKit-https.git] / WebCore / bindings / js / JSNodeCustom.cpp
1 /*
2  * Copyright (C) 2007 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 "HTMLElement.h"
38 #include "JSAttr.h"
39 #include "JSCDATASection.h"
40 #include "JSComment.h"
41 #include "JSDocument.h"
42 #include "JSDocumentFragment.h"
43 #include "JSDocumentType.h"
44 #include "JSEntity.h"
45 #include "JSEntityReference.h"
46 #include "JSHTMLElement.h"
47 #include "JSHTMLElementWrapperFactory.h"
48 #include "JSNotation.h"
49 #include "JSProcessingInstruction.h"
50 #include "JSText.h"
51 #include "Node.h"
52 #include "Notation.h"
53 #include "ProcessingInstruction.h"
54 #include "Text.h"
55 #include <wtf/PassRefPtr.h>
56 #include <wtf/RefPtr.h>
57
58 #if ENABLE(SVG)
59 #include "JSSVGElementWrapperFactory.h"
60 #include "SVGElement.h"
61 #endif
62
63 using namespace KJS;
64
65 namespace WebCore {
66
67 typedef int ExpectionCode;
68
69 JSValue* JSNode::insertBefore(ExecState* exec, const ArgList& args)
70 {
71     ExceptionCode ec = 0;
72     bool ok = impl()->insertBefore(toNode(args[0]), toNode(args[1]), ec, true);
73     setDOMException(exec, ec);
74     if (ok)
75         return args[0];
76     return jsNull();
77 }
78
79 JSValue* JSNode::replaceChild(ExecState* exec, const ArgList& args)
80 {
81     ExceptionCode ec = 0;
82     bool ok = impl()->replaceChild(toNode(args[0]), toNode(args[1]), ec, true);
83     setDOMException(exec, ec);
84     if (ok)
85         return args[1];
86     return jsNull();
87 }
88
89 JSValue* JSNode::removeChild(ExecState* exec, const ArgList& args)
90 {
91     ExceptionCode ec = 0;
92     bool ok = impl()->removeChild(toNode(args[0]), ec);
93     setDOMException(exec, ec);
94     if (ok)
95         return args[0];
96     return jsNull();
97 }
98
99 JSValue* JSNode::appendChild(ExecState* exec, const ArgList& args)
100 {
101     ExceptionCode ec = 0;
102     bool ok = impl()->appendChild(toNode(args[0]), ec, true);
103     setDOMException(exec, ec);
104     if (ok)
105         return args[0];
106     return jsNull();
107 }
108
109 void JSNode::mark()
110 {
111     ASSERT(!marked());
112
113     Node* node = m_impl.get();
114
115     // Nodes in the document are kept alive by ScriptInterpreter::mark,
116     // so we have no special responsibilities and can just call the base class here.
117     if (node->inDocument()) {
118         // But if the document isn't marked we have to mark it to ensure that
119         // nodes reachable from this one are also marked
120         if (Document* doc = node->ownerDocument())
121             if (DOMObject* docWrapper = ScriptInterpreter::getDOMObject(doc))
122                 if (!docWrapper->marked())
123                     docWrapper->mark();
124         DOMObject::mark();
125         return;
126     }
127
128     // This is a node outside the document, so find the root of the tree it is in,
129     // and start marking from there.
130     Node* root = node;
131     for (Node* current = m_impl.get(); current; current = current->parentNode())
132         root = current;
133
134     // If we're already marking this tree, then we can simply mark this wrapper
135     // by calling the base class; our caller is iterating the tree.
136     if (root->m_inSubtreeMark) {
137         DOMObject::mark();
138         return;
139     }
140
141     // Mark the whole tree; use the global set of roots to avoid reentering.
142     root->m_inSubtreeMark = true;
143     for (Node* nodeToMark = root; nodeToMark; nodeToMark = nodeToMark->traverseNextNode()) {
144         JSNode* wrapper = ScriptInterpreter::getDOMNodeForDocument(m_impl->document(), nodeToMark);
145         if (wrapper) {
146             if (!wrapper->marked())
147                 wrapper->mark();
148         } else if (nodeToMark == node) {
149             // This is the case where the map from the document to wrappers has
150             // been cleared out, but a wrapper is being marked. For now, we'll
151             // let the rest of the tree of wrappers get collected, because we have
152             // no good way of finding them. Later we should test behavior of other
153             // browsers and see if we need to preserve other wrappers in this case.
154             if (!marked())
155                 mark();
156         }
157     }
158     root->m_inSubtreeMark = false;
159
160     // Double check that we actually ended up marked. This assert caught problems in the past.
161     ASSERT(marked());
162 }
163
164 static ALWAYS_INLINE JSValue* createWrapper(ExecState* exec, Node* node)
165 {
166     ASSERT(node);
167     ASSERT(!ScriptInterpreter::getDOMNodeForDocument(node->document(), node));
168     
169     Document* doc = node->document();
170     JSNode* ret = 0;
171     
172     switch (node->nodeType()) {
173         case Node::ELEMENT_NODE:
174             if (node->isHTMLElement())
175                 ret = createJSHTMLWrapper(exec, static_cast<HTMLElement*>(node));
176 #if ENABLE(SVG)
177             else if (node->isSVGElement())
178                 ret = createJSSVGWrapper(exec, static_cast<SVGElement*>(node));
179 #endif
180             else
181                 ret = new (exec) JSElement(JSElementPrototype::self(exec), static_cast<Element*>(node));
182             break;
183         case Node::ATTRIBUTE_NODE:
184             ret = new (exec) JSAttr(JSAttrPrototype::self(exec), static_cast<Attr*>(node));
185             break;
186         case Node::TEXT_NODE:
187             ret = new (exec) JSText(JSTextPrototype::self(exec), static_cast<Text*>(node));
188             break;
189         case Node::CDATA_SECTION_NODE:
190             ret = new (exec) JSCDATASection(JSCDATASectionPrototype::self(exec), static_cast<CDATASection*>(node));
191             break;
192         case Node::ENTITY_NODE:
193             ret = new (exec) JSEntity(JSEntityPrototype::self(exec), static_cast<Entity*>(node));
194             break;
195         case Node::PROCESSING_INSTRUCTION_NODE:
196             ret = new (exec) JSProcessingInstruction(JSProcessingInstructionPrototype::self(exec), static_cast<ProcessingInstruction*>(node));
197             break;
198         case Node::COMMENT_NODE:
199             ret = new (exec) JSComment(JSCommentPrototype::self(exec), static_cast<Comment*>(node));
200             break;
201         case Node::DOCUMENT_NODE:
202             // we don't want to cache the document itself in the per-document dictionary
203             return toJS(exec, static_cast<Document*>(node));
204         case Node::DOCUMENT_TYPE_NODE:
205             ret = new (exec) JSDocumentType(JSDocumentTypePrototype::self(exec), static_cast<DocumentType*>(node));
206             break;
207         case Node::NOTATION_NODE:
208             ret = new (exec) JSNotation(JSNotationPrototype::self(exec), static_cast<Notation*>(node));
209             break;
210         case Node::DOCUMENT_FRAGMENT_NODE:
211             ret = new (exec) JSDocumentFragment(JSDocumentFragmentPrototype::self(exec), static_cast<DocumentFragment*>(node));
212             break;
213         case Node::ENTITY_REFERENCE_NODE:
214             ret = new (exec) JSEntityReference(JSEntityReferencePrototype::self(exec), static_cast<EntityReference*>(node));
215             break;
216         default:
217             ret = new (exec) JSNode(JSNodePrototype::self(exec), node);
218     }
219
220     ScriptInterpreter::putDOMNodeForDocument(doc, node, ret);
221
222     return ret;    
223 }
224     
225 JSValue* toJSNewlyCreated(ExecState* exec, Node* node)
226 {
227     if (!node)
228         return jsNull();
229     
230     return createWrapper(exec, node);
231 }
232     
233 JSValue* toJS(ExecState* exec, Node* node)
234 {
235     if (!node)
236         return jsNull();
237
238     Document* doc = node->document();
239     JSNode* ret = ScriptInterpreter::getDOMNodeForDocument(doc, node);
240     if (ret)
241         return ret;
242
243     return createWrapper(exec, node);
244 }
245
246 } // namespace WebCore