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 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 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 "JSSVGElementWrapperFactory.h"
62#include "JSText.h"
63#include "Node.h"
64#include "Notation.h"
65#include "ProcessingInstruction.h"
66#include "RegisteredEventListener.h"
67#include "SVGElement.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
75using namespace JSC;
76
77namespace WebCore {
78
79using namespace HTMLNames;
80
81static inline bool isObservable(JSNode* jsNode, Node* node)
82{
83    // The root node keeps the tree intact.
84    if (!node->parentNode())
85        return true;
86
87    if (jsNode->hasCustomProperties())
88        return true;
89
90    // A node's JS wrapper is responsible for marking its JS event listeners.
91    if (node->hasEventListeners())
92        return true;
93
94    return false;
95}
96
97static inline bool isReachableFromDOM(JSNode* jsNode, Node* node, SlotVisitor& visitor)
98{
99    if (!node->inDocument()) {
100        if (node->isElementNode()) {
101            auto& element = toElement(*node);
102
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(element)) {
109                if (toHTMLImageElement(element).hasPendingActivity())
110                    return true;
111            }
112#if ENABLE(VIDEO)
113            else if (isHTMLAudioElement(element)) {
114                if (!toHTMLAudioElement(element).paused())
115                    return true;
116            }
117#endif
118        }
119
120        // If a node is firing event listeners, its wrapper is observable because
121        // its wrapper is responsible for marking those event listeners.
122        if (node->isFiringEventListeners())
123            return true;
124    }
125
126    return isObservable(jsNode, node) && visitor.containsOpaqueRoot(root(node));
127}
128
129bool JSNodeOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, SlotVisitor& visitor)
130{
131    JSNode* jsNode = jsCast<JSNode*>(handle.slot()->asCell());
132    return isReachableFromDOM(jsNode, &jsNode->impl(), visitor);
133}
134
135JSValue JSNode::insertBefore(ExecState* exec)
136{
137    ExceptionCode ec = 0;
138    bool ok = impl().insertBefore(toNode(exec->argument(0)), toNode(exec->argument(1)), ec);
139    setDOMException(exec, ec);
140    if (ok)
141        return exec->argument(0);
142    return jsNull();
143}
144
145JSValue JSNode::replaceChild(ExecState* exec)
146{
147    ExceptionCode ec = 0;
148    bool ok = impl().replaceChild(toNode(exec->argument(0)), toNode(exec->argument(1)), ec);
149    setDOMException(exec, ec);
150    if (ok)
151        return exec->argument(1);
152    return jsNull();
153}
154
155JSValue JSNode::removeChild(ExecState* exec)
156{
157    ExceptionCode ec = 0;
158    bool ok = impl().removeChild(toNode(exec->argument(0)), ec);
159    setDOMException(exec, ec);
160    if (ok)
161        return exec->argument(0);
162    return jsNull();
163}
164
165JSValue JSNode::appendChild(ExecState* exec)
166{
167    ExceptionCode ec = 0;
168    bool ok = impl().appendChild(toNode(exec->argument(0)), ec);
169    setDOMException(exec, ec);
170    if (ok)
171        return exec->argument(0);
172    return jsNull();
173}
174
175JSScope* JSNode::pushEventHandlerScope(ExecState* exec, JSScope* node) const
176{
177    if (inherits(JSHTMLElement::info()))
178        return jsCast<const JSHTMLElement*>(this)->pushEventHandlerScope(exec, node);
179    return node;
180}
181
182void JSNode::visitAdditionalChildren(SlotVisitor& visitor)
183{
184    visitor.addOpaqueRoot(root(impl()));
185}
186
187static ALWAYS_INLINE JSValue createWrapperInline(ExecState* exec, JSDOMGlobalObject* globalObject, Node* node)
188{
189    ASSERT(node);
190    ASSERT(!getCachedWrapper(globalObject->world(), node));
191
192    JSDOMWrapper* wrapper;
193    switch (node->nodeType()) {
194        case Node::ELEMENT_NODE:
195            if (node->isHTMLElement())
196                wrapper = createJSHTMLWrapper(globalObject, toHTMLElement(node));
197            else if (node->isSVGElement())
198                wrapper = createJSSVGWrapper(globalObject, toSVGElement(node));
199            else
200                wrapper = CREATE_DOM_WRAPPER(globalObject, Element, node);
201            break;
202        case Node::ATTRIBUTE_NODE:
203            wrapper = CREATE_DOM_WRAPPER(globalObject, Attr, node);
204            break;
205        case Node::TEXT_NODE:
206            wrapper = CREATE_DOM_WRAPPER(globalObject, Text, node);
207            break;
208        case Node::CDATA_SECTION_NODE:
209            wrapper = CREATE_DOM_WRAPPER(globalObject, CDATASection, node);
210            break;
211        case Node::ENTITY_NODE:
212            wrapper = CREATE_DOM_WRAPPER(globalObject, Entity, node);
213            break;
214        case Node::PROCESSING_INSTRUCTION_NODE:
215            wrapper = CREATE_DOM_WRAPPER(globalObject, ProcessingInstruction, node);
216            break;
217        case Node::COMMENT_NODE:
218            wrapper = CREATE_DOM_WRAPPER(globalObject, Comment, node);
219            break;
220        case Node::DOCUMENT_NODE:
221            // we don't want to cache the document itself in the per-document dictionary
222            return toJS(exec, globalObject, toDocument(node));
223        case Node::DOCUMENT_TYPE_NODE:
224            wrapper = CREATE_DOM_WRAPPER(globalObject, DocumentType, node);
225            break;
226        case Node::NOTATION_NODE:
227            wrapper = CREATE_DOM_WRAPPER(globalObject, Notation, node);
228            break;
229        case Node::DOCUMENT_FRAGMENT_NODE:
230            wrapper = CREATE_DOM_WRAPPER(globalObject, DocumentFragment, node);
231            break;
232        case Node::ENTITY_REFERENCE_NODE:
233            wrapper = CREATE_DOM_WRAPPER(globalObject, EntityReference, node);
234            break;
235        default:
236            wrapper = CREATE_DOM_WRAPPER(globalObject, Node, node);
237    }
238
239    return wrapper;
240}
241
242JSValue createWrapper(ExecState* exec, JSDOMGlobalObject* globalObject, Node* node)
243{
244    return createWrapperInline(exec, globalObject, node);
245}
246
247JSValue toJSNewlyCreated(ExecState* exec, JSDOMGlobalObject* globalObject, Node* node)
248{
249    if (!node)
250        return jsNull();
251
252    return createWrapperInline(exec, globalObject, node);
253}
254
255JSC::JSObject* getOutOfLineCachedWrapper(JSDOMGlobalObject* globalObject, Node* node)
256{
257    ASSERT(!globalObject->world().isNormal());
258    return globalObject->world().m_wrappers.get(node);
259}
260
261void willCreatePossiblyOrphanedTreeByRemovalSlowCase(Node* root)
262{
263    JSC::ExecState* scriptState = mainWorldExecState(root->document().frame());
264    if (!scriptState)
265        return;
266
267    JSLockHolder lock(scriptState);
268    toJS(scriptState, static_cast<JSDOMGlobalObject*>(scriptState->lexicalGlobalObject()), root);
269}
270
271} // namespace WebCore
272