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
78using namespace JSC;
79
80namespace WebCore {
81
82using namespace HTMLNames;
83
84static 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
100static 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 (node->hasTagName(imgTag)) {
109            if (static_cast<HTMLImageElement*>(node)->hasPendingActivity())
110                return true;
111        }
112    #if ENABLE(VIDEO)
113        else if (node->hasTagName(audioTag)) {
114            if (!static_cast<HTMLAudioElement*>(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
128bool 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
134void 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
142JSValue 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
153JSValue 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
164JSValue 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
175JSValue 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
186JSScope* 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
193void 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
207static 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
264JSValue createWrapper(ExecState* exec, JSDOMGlobalObject* globalObject, Node* node)
265{
266    return createWrapperInline(exec, globalObject, node);
267}
268
269JSValue toJSNewlyCreated(ExecState* exec, JSDOMGlobalObject* globalObject, Node* node)
270{
271    if (!node)
272        return jsNull();
273
274    return createWrapperInline(exec, globalObject, node);
275}
276
277void 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
288