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