summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/bindings/js/JSNodeCustom.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/bindings/js/JSNodeCustom.cpp')
-rw-r--r--Source/WebCore/bindings/js/JSNodeCustom.cpp156
1 files changed, 120 insertions, 36 deletions
diff --git a/Source/WebCore/bindings/js/JSNodeCustom.cpp b/Source/WebCore/bindings/js/JSNodeCustom.cpp
index 17f57f4..d305be7 100644
--- a/Source/WebCore/bindings/js/JSNodeCustom.cpp
+++ b/Source/WebCore/bindings/js/JSNodeCustom.cpp
@@ -35,7 +35,15 @@
#include "Entity.h"
#include "EntityReference.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"
@@ -55,6 +63,8 @@
#include "Notation.h"
#include "ProcessingInstruction.h"
#include "RegisteredEventListener.h"
+#include "StyleSheet.h"
+#include "StyledElement.h"
#include "Text.h"
#include <wtf/PassRefPtr.h>
#include <wtf/RefPtr.h>
@@ -68,6 +78,114 @@ using namespace JSC;
namespace WebCore {
+using namespace HTMLNames;
+
+static bool isObservable(JSNode* jsNode, Node* node, DOMWrapperWorld* world)
+{
+ // Certain conditions implicitly make existence of a JS DOM node wrapper observable
+ // through the DOM, even if no explicit reference to it remains.
+
+ // The DOM doesn't know how to keep a tree of nodes alive without the root
+ // being explicitly referenced. So, we artificially treat the root of
+ // every tree as observable.
+ // FIXME: Resolve this lifetime issue in the DOM, and remove this inefficiency.
+ if (!node->parentNode())
+ return true;
+
+ // If a node is in the document, and its wrapper has custom properties,
+ // the wrapper is observable because future access to the node through the
+ // DOM must reflect those properties.
+ if (jsNode->hasCustomProperties())
+ return true;
+
+ // If a node is in the document, and has event listeners, its wrapper is
+ // observable because its wrapper is responsible for marking those event listeners.
+ if (node->hasEventListeners())
+ return true;
+
+ // If a node owns another object with a wrapper with custom properties,
+ // the wrapper must be treated as observable, because future access to
+ // those objects through the DOM must reflect those properties.
+ // FIXME: It would be better if this logic could be in the node next to
+ // the custom markChildren functions rather than here.
+ // Note that for some compound objects like stylesheets and CSSStyleDeclarations,
+ // we don't descend to check children for custom properties, and just conservatively
+ // keep the node wrappers protecting them alive.
+ if (node->isElementNode()) {
+ if (node->isStyledElement()) {
+ if (CSSMutableStyleDeclaration* style = static_cast<StyledElement*>(node)->inlineStyleDecl()) {
+ if (world->m_wrappers.get(style))
+ return true;
+ }
+ }
+ if (static_cast<Element*>(node)->hasTagName(canvasTag)) {
+ if (CanvasRenderingContext* context = static_cast<HTMLCanvasElement*>(node)->renderingContext()) {
+ if (JSDOMWrapper* wrapper = world->m_wrappers.get(context).get()) {
+ if (wrapper->hasCustomProperties())
+ return true;
+ }
+ }
+ } else if (static_cast<Element*>(node)->hasTagName(linkTag)) {
+ if (StyleSheet* sheet = static_cast<HTMLLinkElement*>(node)->sheet()) {
+ if (world->m_wrappers.get(sheet))
+ return true;
+ }
+ } else if (static_cast<Element*>(node)->hasTagName(styleTag)) {
+ if (StyleSheet* sheet = static_cast<HTMLStyleElement*>(node)->sheet()) {
+ if (world->m_wrappers.get(sheet))
+ return true;
+ }
+ }
+ } else if (node->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) {
+ if (StyleSheet* sheet = static_cast<ProcessingInstruction*>(node)->sheet()) {
+ if (world->m_wrappers.get(sheet))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static inline bool isReachableFromDOM(JSNode* jsNode, Node* node, DOMWrapperWorld* world, MarkStack& markStack)
+{
+ if (!node->inDocument()) {
+ // If a wrapper is the last reference to an image or script element
+ // that is loading but not in the document, the wrapper is observable
+ // because it is the only thing keeping the image element alive, and if
+ // the image element is destroyed, its load event will not fire.
+ // FIXME: The DOM should manage this issue without the help of JavaScript wrappers.
+ if (node->hasTagName(imgTag) && !static_cast<HTMLImageElement*>(node)->haveFiredLoadEvent())
+ return true;
+ if (node->hasTagName(scriptTag) && !static_cast<HTMLScriptElement*>(node)->haveFiredLoadEvent())
+ return true;
+ #if ENABLE(VIDEO)
+ if (node->hasTagName(audioTag) && !static_cast<HTMLAudioElement*>(node)->paused())
+ return true;
+ #endif
+
+ // If a node is firing event listeners, its wrapper is observable because
+ // its wrapper is responsible for marking those event listeners.
+ if (node->isFiringEventListeners())
+ return true;
+ }
+
+ return isObservable(jsNode, node, world) && markStack.containsOpaqueRoot(root(node));
+}
+
+bool JSNodeOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void* context, MarkStack& markStack)
+{
+ JSNode* jsNode = static_cast<JSNode*>(handle.get().asCell());
+ DOMWrapperWorld* world = static_cast<DOMWrapperWorld*>(context);
+ return isReachableFromDOM(jsNode, jsNode->impl(), world, markStack);
+}
+
+void JSNodeOwner::finalize(JSC::Handle<JSC::Unknown> handle, void* context)
+{
+ JSNode* jsNode = static_cast<JSNode*>(handle.get().asCell());
+ DOMWrapperWorld* world = static_cast<DOMWrapperWorld*>(context);
+ uncacheWrapper(world, jsNode->impl(), jsNode);
+}
+
JSValue JSNode::insertBefore(ExecState* exec)
{
Node* imp = static_cast<Node*>(impl());
@@ -124,47 +242,13 @@ void JSNode::markChildren(MarkStack& markStack)
Node* node = m_impl.get();
node->markJSEventListeners(markStack);
- // Nodes in the document are kept alive by JSDocument::mark, so, if we're in
- // the document, we need to mark the document, but we don't need to explicitly
- // mark any other nodes.
- if (node->inDocument()) {
- // FIXME: Do we really want to call a virtual function, ownerDocument here,
- // when the non-virtual inline function, document, is so much faster?!
- if (Document* doc = node->ownerDocument())
- markDOMNodeWrapper(markStack, doc, doc);
- return;
- }
-
- // This is a node outside the document.
- // Find the the root, and the highest ancestor with a wrapper.
- Node* root = node;
- Node* outermostNodeWithWrapper = node;
- for (Node* current = m_impl.get(); current; current = current->parentNode()) {
- root = current;
- if (hasCachedDOMNodeWrapperUnchecked(current->document(), current))
- outermostNodeWithWrapper = current;
- }
-
- // Only nodes that have no ancestors with wrappers mark the subtree. In the common
- // case, the root of the detached subtree has a wrapper, so the tree will only
- // get marked once. Nodes that aren't outermost need to mark the outermost
- // in case it is otherwise unreachable.
- // FIXME: In the non-common case of root not having a wrapper, this is still an O(n^2) algorithm,
- // as we will traverse the whole tree as many times as there are nodes with wrappers in it.
- if (node != outermostNodeWithWrapper) {
- markDOMNodeWrapper(markStack, m_impl->document(), outermostNodeWithWrapper);
- return;
- }
-
- // Mark the whole tree subtree.
- for (Node* nodeToMark = root; nodeToMark; nodeToMark = nodeToMark->traverseNextNode())
- markDOMNodeWrapper(markStack, m_impl->document(), nodeToMark);
+ markStack.addOpaqueRoot(root(node));
}
static ALWAYS_INLINE JSValue createWrapperInline(ExecState* exec, JSDOMGlobalObject* globalObject, Node* node)
{
ASSERT(node);
- ASSERT(!getCachedDOMNodeWrapper(exec, node->document(), node));
+ ASSERT(!getCachedWrapper(currentWorld(exec), node));
JSNode* wrapper;
switch (node->nodeType()) {