1/*
2 * Copyright (C) 2007, 2009-2010, 2016 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 "HTMLAudioElement.h"
36#include "HTMLCanvasElement.h"
37#include "HTMLElement.h"
38#include "HTMLFrameElementBase.h"
39#include "HTMLImageElement.h"
40#include "HTMLLinkElement.h"
41#include "HTMLNames.h"
42#include "HTMLScriptElement.h"
43#include "HTMLStyleElement.h"
44#include "JSAttr.h"
45#include "JSCDATASection.h"
46#include "JSComment.h"
47#include "JSDOMBinding.h"
48#include "JSDocument.h"
49#include "JSDocumentFragment.h"
50#include "JSDocumentType.h"
51#include "JSEventListener.h"
52#include "JSHTMLElement.h"
53#include "JSHTMLElementWrapperFactory.h"
54#include "JSProcessingInstruction.h"
55#include "JSSVGElementWrapperFactory.h"
56#include "JSShadowRoot.h"
57#include "JSText.h"
58#include "Node.h"
59#include "ProcessingInstruction.h"
60#include "RegisteredEventListener.h"
61#include "SVGElement.h"
62#include "ScriptState.h"
63#include "ShadowRoot.h"
64#include "GCReachableRef.h"
65#include "StyleSheet.h"
66#include "StyledElement.h"
67#include "Text.h"
68
69
70namespace WebCore {
71using namespace JSC;
72
73using namespace HTMLNames;
74
75static inline bool isReachableFromDOM(Node* node, SlotVisitor& visitor, const char** reason)
76{
77 if (!node->isConnected()) {
78 if (is<Element>(*node)) {
79 auto& element = downcast<Element>(*node);
80
81 // If a wrapper is the last reference to an image element
82 // that is loading but not in the document, the wrapper is observable
83 // because it is the only thing keeping the image element alive, and if
84 // the element is destroyed, its load event will not fire.
85 // FIXME: The DOM should manage this issue without the help of JavaScript wrappers.
86 if (is<HTMLImageElement>(element)) {
87 if (downcast<HTMLImageElement>(element).hasPendingActivity()) {
88 if (UNLIKELY(reason))
89 *reason = "Image element with pending activity";
90 return true;
91 }
92 }
93#if ENABLE(VIDEO)
94 else if (is<HTMLAudioElement>(element)) {
95 if (!downcast<HTMLAudioElement>(element).paused()) {
96 if (UNLIKELY(reason))
97 *reason = "Audio element which is not paused";
98 return true;
99 }
100 }
101#endif
102 }
103
104 // If a node is firing event listeners, its wrapper is observable because
105 // its wrapper is responsible for marking those event listeners.
106 if (node->isFiringEventListeners()) {
107 if (UNLIKELY(reason))
108 *reason = "Node which is firing event listeners";
109 return true;
110 }
111 if (GCReachableRefMap::contains(*node)) {
112 if (UNLIKELY(reason))
113 *reason = "Node is scheduled to be used in an async script invocation)";
114 return true;
115 }
116 }
117
118 if (UNLIKELY(reason))
119 *reason = "Connected node";
120
121 return visitor.containsOpaqueRoot(root(node));
122}
123
124bool JSNodeOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, SlotVisitor& visitor, const char** reason)
125{
126 JSNode* jsNode = jsCast<JSNode*>(handle.slot()->asCell());
127 return isReachableFromDOM(&jsNode->wrapped(), visitor, reason);
128}
129
130JSScope* JSNode::pushEventHandlerScope(ExecState* exec, JSScope* node) const
131{
132 if (inherits<JSHTMLElement>(exec->vm()))
133 return jsCast<const JSHTMLElement*>(this)->pushEventHandlerScope(exec, node);
134 return node;
135}
136
137void JSNode::visitAdditionalChildren(SlotVisitor& visitor)
138{
139 visitor.addOpaqueRoot(root(wrapped()));
140}
141
142static ALWAYS_INLINE JSValue createWrapperInline(ExecState* exec, JSDOMGlobalObject* globalObject, Ref<Node>&& node)
143{
144 ASSERT(!getCachedWrapper(globalObject->world(), node));
145
146 JSDOMObject* wrapper;
147 switch (node->nodeType()) {
148 case Node::ELEMENT_NODE:
149 if (is<HTMLElement>(node))
150 wrapper = createJSHTMLWrapper(globalObject, static_reference_cast<HTMLElement>(WTFMove(node)));
151 else if (is<SVGElement>(node))
152 wrapper = createJSSVGWrapper(globalObject, static_reference_cast<SVGElement>(WTFMove(node)));
153 else
154 wrapper = createWrapper<Element>(globalObject, WTFMove(node));
155 break;
156 case Node::ATTRIBUTE_NODE:
157 wrapper = createWrapper<Attr>(globalObject, WTFMove(node));
158 break;
159 case Node::TEXT_NODE:
160 wrapper = createWrapper<Text>(globalObject, WTFMove(node));
161 break;
162 case Node::CDATA_SECTION_NODE:
163 wrapper = createWrapper<CDATASection>(globalObject, WTFMove(node));
164 break;
165 case Node::PROCESSING_INSTRUCTION_NODE:
166 wrapper = createWrapper<ProcessingInstruction>(globalObject, WTFMove(node));
167 break;
168 case Node::COMMENT_NODE:
169 wrapper = createWrapper<Comment>(globalObject, WTFMove(node));
170 break;
171 case Node::DOCUMENT_NODE:
172 // we don't want to cache the document itself in the per-document dictionary
173 return toJS(exec, globalObject, downcast<Document>(node.get()));
174 case Node::DOCUMENT_TYPE_NODE:
175 wrapper = createWrapper<DocumentType>(globalObject, WTFMove(node));
176 break;
177 case Node::DOCUMENT_FRAGMENT_NODE:
178 if (node->isShadowRoot())
179 wrapper = createWrapper<ShadowRoot>(globalObject, WTFMove(node));
180 else
181 wrapper = createWrapper<DocumentFragment>(globalObject, WTFMove(node));
182 break;
183 default:
184 wrapper = createWrapper<Node>(globalObject, WTFMove(node));
185 }
186
187 return wrapper;
188}
189
190JSValue createWrapper(ExecState* exec, JSDOMGlobalObject* globalObject, Ref<Node>&& node)
191{
192 return createWrapperInline(exec, globalObject, WTFMove(node));
193}
194
195JSValue toJSNewlyCreated(ExecState* exec, JSDOMGlobalObject* globalObject, Ref<Node>&& node)
196{
197 return createWrapperInline(exec, globalObject, WTFMove(node));
198}
199
200JSC::JSObject* getOutOfLineCachedWrapper(JSDOMGlobalObject* globalObject, Node& node)
201{
202 ASSERT(!globalObject->world().isNormal());
203 return globalObject->world().wrappers().get(&node);
204}
205
206void willCreatePossiblyOrphanedTreeByRemovalSlowCase(Node* root)
207{
208 JSC::ExecState* scriptState = mainWorldExecState(root->document().frame());
209 if (!scriptState)
210 return;
211
212 JSLockHolder lock(scriptState);
213 toJS(scriptState, static_cast<JSDOMGlobalObject*>(scriptState->lexicalGlobalObject()), *root);
214}
215
216} // namespace WebCore
217