/* * Copyright (C) 2009 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "bindings/v8/V8GCController.h" #include #include "V8MutationObserver.h" #include "V8Node.h" #include "V8ScriptRunner.h" #include "bindings/v8/RetainedDOMInfo.h" #include "bindings/v8/V8AbstractEventListener.h" #include "bindings/v8/V8Binding.h" #include "bindings/v8/WrapperTypeInfo.h" #include "core/dom/Attr.h" #include "core/dom/NodeTraversal.h" #include "core/dom/TemplateContentDocumentFragment.h" #include "core/dom/shadow/ElementShadow.h" #include "core/dom/shadow/ShadowRoot.h" #include "core/html/HTMLImageElement.h" #include "core/html/HTMLTemplateElement.h" #include "core/svg/SVGElement.h" #include "platform/TraceEvent.h" namespace WebCore { // FIXME: This should use opaque GC roots. static void addReferencesForNodeWithEventListeners(v8::Isolate* isolate, Node* node, const v8::Persistent& wrapper) { ASSERT(node->hasEventListeners()); EventListenerIterator iterator(node); while (EventListener* listener = iterator.nextListener()) { if (listener->type() != EventListener::JSEventListenerType) continue; V8AbstractEventListener* v8listener = static_cast(listener); if (!v8listener->hasExistingListenerObject()) continue; // FIXME: update this to use the upcasting function which v8 will provide. v8::Persistent* value = reinterpret_cast*>(&(v8listener->existingListenerObjectPersistentHandle())); isolate->SetReference(wrapper, *value); } } Node* V8GCController::opaqueRootForGC(Node* node, v8::Isolate*) { // FIXME: Remove the special handling for image elements. // The same special handling is in V8GCController::gcTree(). // Maybe should image elements be active DOM nodes? // See https://code.google.com/p/chromium/issues/detail?id=164882 if (node->inDocument() || (node->hasTagName(HTMLNames::imgTag) && toHTMLImageElement(node)->hasPendingActivity())) return &node->document(); if (node->isAttributeNode()) { Node* ownerElement = toAttr(node)->ownerElement(); if (!ownerElement) return node; node = ownerElement; } while (Node* parent = node->parentOrShadowHostOrTemplateHostNode()) node = parent; return node; } // Regarding a minor GC algorithm for DOM nodes, see this document: // https://docs.google.com/a/google.com/presentation/d/1uifwVYGNYTZDoGLyCb7sXa7g49mWNMW2gaWvMN5NLk8/edit#slide=id.p class MinorGCWrapperVisitor : public v8::PersistentHandleVisitor { public: explicit MinorGCWrapperVisitor(v8::Isolate* isolate) : m_isolate(isolate) { } virtual void VisitPersistentHandle(v8::Persistent* value, uint16_t classId) OVERRIDE { // A minor DOM GC can collect only Nodes. if (classId != v8DOMNodeClassId) return; // To make minor GC cycle time bounded, we limit the number of wrappers handled // by each minor GC cycle to 10000. This value was selected so that the minor // GC cycle time is bounded to 20 ms in a case where the new space size // is 16 MB and it is full of wrappers (which is almost the worst case). // Practically speaking, as far as I crawled real web applications, // the number of wrappers handled by each minor GC cycle is at most 3000. // So this limit is mainly for pathological micro benchmarks. const unsigned wrappersHandledByEachMinorGC = 10000; if (m_nodesInNewSpace.size() >= wrappersHandledByEachMinorGC) return; // Casting to a Handle is safe here, since the Persistent cannot get GCd // during the GC prologue. ASSERT((*reinterpret_cast*>(value))->IsObject()); v8::Handle* wrapper = reinterpret_cast*>(value); ASSERT(V8DOMWrapper::maybeDOMWrapper(*wrapper)); ASSERT(V8Node::hasInstanceInAnyWorld(*wrapper, m_isolate)); Node* node = V8Node::toNative(*wrapper); // A minor DOM GC can handle only node wrappers in the main world. // Note that node->wrapper().IsEmpty() returns true for nodes that // do not have wrappers in the main world. if (node->containsWrapper()) { const WrapperTypeInfo* type = toWrapperTypeInfo(*wrapper); ActiveDOMObject* activeDOMObject = type->toActiveDOMObject(*wrapper); if (activeDOMObject && activeDOMObject->hasPendingActivity()) return; m_nodesInNewSpace.append(node); node->setV8CollectableDuringMinorGC(true); } } void notifyFinished() { Node** nodeIterator = m_nodesInNewSpace.begin(); Node** nodeIteratorEnd = m_nodesInNewSpace.end(); for (; nodeIterator < nodeIteratorEnd; ++nodeIterator) { Node* node = *nodeIterator; ASSERT(node->containsWrapper()); if (node->isV8CollectableDuringMinorGC()) // This branch is just for performance. gcTree(m_isolate, node); } } private: bool traverseTree(Node* rootNode, Vector* newSpaceNodes) { // To make each minor GC time bounded, we might need to give up // traversing at some point for a large DOM tree. That being said, // I could not observe the need even in pathological test cases. for (Node* node = rootNode; node; node = NodeTraversal::next(*node)) { if (node->containsWrapper()) { // FIXME: Remove the special handling for image elements. // FIXME: Remove the special handling for SVG context elements. // The same special handling is in V8GCController::opaqueRootForGC(). // Maybe should image elements be active DOM nodes? // See https://code.google.com/p/chromium/issues/detail?id=164882 if (!node->isV8CollectableDuringMinorGC() || (node->hasTagName(HTMLNames::imgTag) && toHTMLImageElement(node)->hasPendingActivity()) || (node->isSVGElement() && toSVGElement(node)->isContextElement())) { // This node is not in the new space of V8. This indicates that // the minor GC cannot anyway judge reachability of this DOM tree. // Thus we give up traversing the DOM tree. return false; } node->setV8CollectableDuringMinorGC(false); newSpaceNodes->append(node); } if (ShadowRoot* shadowRoot = node->youngestShadowRoot()) { if (!traverseTree(shadowRoot, newSpaceNodes)) return false; } else if (node->isShadowRoot()) { if (ShadowRoot* shadowRoot = toShadowRoot(node)->olderShadowRoot()) { if (!traverseTree(shadowRoot, newSpaceNodes)) return false; } } //