#include "config.h"
#include "JSNode.h"
#include "Attr.h"
#include "CDATASection.h"
#include "Comment.h"
#include "Document.h"
#include "DocumentFragment.h"
#include "DocumentType.h"
#include "ExceptionCode.h"
#include "HTMLAudioElement.h"
#include "HTMLCanvasElement.h"
#include "HTMLElement.h"
#include "HTMLFrameElementBase.h"
#include "HTMLImageElement.h"
#include "HTMLLinkElement.h"
#include "HTMLNames.h"
#include "HTMLScriptElement.h"
#include "HTMLStyleElement.h"
#include "JSAttr.h"
#include "JSCDATASection.h"
#include "JSComment.h"
#include "JSDOMBinding.h"
#include "JSDocument.h"
#include "JSDocumentFragment.h"
#include "JSDocumentType.h"
#include "JSEventListener.h"
#include "JSHTMLElement.h"
#include "JSHTMLElementWrapperFactory.h"
#include "JSProcessingInstruction.h"
#include "JSSVGElementWrapperFactory.h"
#include "JSShadowRoot.h"
#include "JSText.h"
#include "Node.h"
#include "ProcessingInstruction.h"
#include "RegisteredEventListener.h"
#include "SVGElement.h"
#include "ScriptState.h"
#include "ShadowRoot.h"
#include "StyleSheet.h"
#include "StyledElement.h"
#include "Text.h"
using namespace JSC;
namespace WebCore {
using namespace HTMLNames;
static inline bool isReachableFromDOM(Node* node, SlotVisitor& visitor)
{
if (!node->inDocument()) {
if (is<Element>(*node)) {
auto& element = downcast<Element>(*node);
if (is<HTMLImageElement>(element)) {
if (downcast<HTMLImageElement>(element).hasPendingActivity())
return true;
}
#if ENABLE(VIDEO)
else if (is<HTMLAudioElement>(element)) {
if (!downcast<HTMLAudioElement>(element).paused())
return true;
}
#endif
}
if (node->isFiringEventListeners())
return true;
}
return visitor.containsOpaqueRoot(root(node));
}
bool JSNodeOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, SlotVisitor& visitor)
{
JSNode* jsNode = jsCast<JSNode*>(handle.slot()->asCell());
return isReachableFromDOM(&jsNode->wrapped(), visitor);
}
JSValue JSNode::insertBefore(ExecState& state)
{
VM& vm = state.vm();
auto scope = DECLARE_THROW_SCOPE(vm);
if (UNLIKELY(state.argumentCount() < 2))
return throwException(&state, scope, createNotEnoughArgumentsError(&state));
JSValue newChildValue = state.uncheckedArgument(0);
auto* newChild = JSNode::toWrapped(newChildValue);
if (UNLIKELY(!newChild))
return JSValue::decode(throwArgumentTypeError(state, scope, 0, "node", "Node", "insertBefore", "Node"));
propagateException(state, scope, wrapped().insertBefore(*newChild, JSNode::toWrapped(state.uncheckedArgument(1))));
return newChildValue;
}
JSValue JSNode::replaceChild(ExecState& state)
{
VM& vm = state.vm();
auto scope = DECLARE_THROW_SCOPE(vm);
if (UNLIKELY(state.argumentCount() < 2))
return throwException(&state, scope, createNotEnoughArgumentsError(&state));
auto* newChild = JSNode::toWrapped(state.uncheckedArgument(0));
JSValue oldChildValue = state.uncheckedArgument(1);
auto* oldChild = JSNode::toWrapped(oldChildValue);
if (UNLIKELY(!newChild || !oldChild)) {
if (!newChild)
return JSValue::decode(throwArgumentTypeError(state, scope, 0, "node", "Node", "replaceChild", "Node"));
return JSValue::decode(throwArgumentTypeError(state, scope, 1, "child", "Node", "replaceChild", "Node"));
}
propagateException(state, scope, wrapped().replaceChild(*newChild, *oldChild));
return oldChildValue;
}
JSValue JSNode::removeChild(ExecState& state)
{
VM& vm = state.vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSValue childValue = state.argument(0);
auto* child = JSNode::toWrapped(childValue);
if (UNLIKELY(!child))
return JSValue::decode(throwArgumentTypeError(state, scope, 0, "child", "Node", "removeChild", "Node"));
propagateException(state, scope, wrapped().removeChild(*child));
return childValue;
}
JSValue JSNode::appendChild(ExecState& state)
{
VM& vm = state.vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSValue newChildValue = state.argument(0);
auto newChild = JSNode::toWrapped(newChildValue);
if (UNLIKELY(!newChild))
return JSValue::decode(throwArgumentTypeError(state, scope, 0, "node", "Node", "appendChild", "Node"));
propagateException(state, scope, wrapped().appendChild(*newChild));
return newChildValue;
}
JSScope* JSNode::pushEventHandlerScope(ExecState* exec, JSScope* node) const
{
if (inherits(JSHTMLElement::info()))
return jsCast<const JSHTMLElement*>(this)->pushEventHandlerScope(exec, node);
return node;
}
void JSNode::visitAdditionalChildren(SlotVisitor& visitor)
{
visitor.addOpaqueRoot(root(wrapped()));
}
static ALWAYS_INLINE JSValue createWrapperInline(ExecState* exec, JSDOMGlobalObject* globalObject, Ref<Node>&& node)
{
ASSERT(!getCachedWrapper(globalObject->world(), node));
JSDOMObject* wrapper;
switch (node->nodeType()) {
case Node::ELEMENT_NODE:
if (is<HTMLElement>(node.get()))
wrapper = createJSHTMLWrapper(globalObject, static_reference_cast<HTMLElement>(WTFMove(node)));
else if (is<SVGElement>(node.get()))
wrapper = createJSSVGWrapper(globalObject, static_reference_cast<SVGElement>(WTFMove(node)));
else
wrapper = createWrapper<Element>(globalObject, WTFMove(node));
break;
case Node::ATTRIBUTE_NODE:
wrapper = createWrapper<Attr>(globalObject, WTFMove(node));
break;
case Node::TEXT_NODE:
wrapper = createWrapper<Text>(globalObject, WTFMove(node));
break;
case Node::CDATA_SECTION_NODE:
wrapper = createWrapper<CDATASection>(globalObject, WTFMove(node));
break;
case Node::PROCESSING_INSTRUCTION_NODE:
wrapper = createWrapper<ProcessingInstruction>(globalObject, WTFMove(node));
break;
case Node::COMMENT_NODE:
wrapper = createWrapper<Comment>(globalObject, WTFMove(node));
break;
case Node::DOCUMENT_NODE:
return toJS(exec, globalObject, downcast<Document>(node.get()));
case Node::DOCUMENT_TYPE_NODE:
wrapper = createWrapper<DocumentType>(globalObject, WTFMove(node));
break;
case Node::DOCUMENT_FRAGMENT_NODE:
if (node->isShadowRoot())
wrapper = createWrapper<ShadowRoot>(globalObject, WTFMove(node));
else
wrapper = createWrapper<DocumentFragment>(globalObject, WTFMove(node));
break;
default:
wrapper = createWrapper<Node>(globalObject, WTFMove(node));
}
return wrapper;
}
JSValue createWrapper(ExecState* exec, JSDOMGlobalObject* globalObject, Ref<Node>&& node)
{
return createWrapperInline(exec, globalObject, WTFMove(node));
}
JSValue toJSNewlyCreated(ExecState* exec, JSDOMGlobalObject* globalObject, Ref<Node>&& node)
{
return createWrapperInline(exec, globalObject, WTFMove(node));
}
JSC::JSObject* getOutOfLineCachedWrapper(JSDOMGlobalObject* globalObject, Node& node)
{
ASSERT(!globalObject->world().isNormal());
return globalObject->world().m_wrappers.get(&node);
}
void willCreatePossiblyOrphanedTreeByRemovalSlowCase(Node* root)
{
JSC::ExecState* scriptState = mainWorldExecState(root->document().frame());
if (!scriptState)
return;
JSLockHolder lock(scriptState);
toJS(scriptState, static_cast<JSDOMGlobalObject*>(scriptState->lexicalGlobalObject()), *root);
}
}