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