void ScriptExecutionContext::suspendActiveDOMObjectIfNeeded(ActiveDOMObject& activeDOMObject) { ASSERT(m_activeDOMObjects.contains(&activeDOMObject)); if (m_activeDOMObjectsAreSuspended) activeDOMObject.suspend(m_reasonForSuspendingActiveDOMObjects); if (m_activeDOMObjectsAreStopped) activeDOMObject.stop(); }
void visitDOMWrapper(DOMDataStore* store, T* object, v8::Persistent<v8::Object> wrapper) { WrapperTypeInfo* typeInfo = V8DOMWrapper::domWrapperType(wrapper); if (!S::process(object, wrapper, typeInfo)) { ActiveDOMObject* activeDOMObject = typeInfo->toActiveDOMObject(wrapper); if (activeDOMObject && activeDOMObject->hasPendingActivity()) wrapper.ClearWeak(); } }
virtual void VisitPersistentHandle(v8::Persistent<v8::Value>* value, uint16_t classId) override { // A minor DOM GC can collect only Nodes. if (classId != WrapperTypeInfo::NodeClassId) 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 doesn't get GCd // during the GC prologue. ASSERT((*reinterpret_cast<v8::Handle<v8::Value>*>(value))->IsObject()); v8::Handle<v8::Object>* wrapper = reinterpret_cast<v8::Handle<v8::Object>*>(value); ASSERT(V8DOMWrapper::isDOMWrapper(*wrapper)); ASSERT(V8Node::hasInstance(*wrapper, m_isolate)); Node* node = V8Node::toImpl(*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; // FIXME: Remove the special handling for image 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 (isHTMLImageElement(*node) && toHTMLImageElement(*node).hasPendingActivity()) return; // FIXME: Remove the special handling for SVG elements. // We currently can't collect SVG Elements from minor gc, as we have // strong references from SVG property tear-offs keeping context SVG element alive. if (node->isSVGElement()) return; m_nodesInNewSpace.append(node); node->markV8CollectableDuringMinorGC(); } }
void visitDOMWrapper(DOMDataStore* store, T* object, v8::Persistent<v8::Object> wrapper) { WrapperTypeInfo* typeInfo = V8DOMWrapper::domWrapperType(wrapper); if (!S::process(object, wrapper, typeInfo)) { ActiveDOMObject* activeDOMObject = typeInfo->toActiveDOMObject(wrapper); if (activeDOMObject && activeDOMObject->hasPendingActivity()) { ASSERT(!wrapper.IsWeak()); // NOTE: To re-enable weak status of the active object we use // |object| from the map and not |activeDOMObject|. The latter // may be a different pointer (in case ActiveDOMObject is not // the main base class of the object's class) and pointer // identity is required by DOM map functions. wrapper.MakeWeak(object, callback); } } }
ScriptExecutionContext::~ScriptExecutionContext() { m_inDestructor = true; for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != m_activeDOMObjects.end(); iter = m_activeDOMObjects.begin()) { ActiveDOMObject* object = iter->first; m_activeDOMObjects.remove(iter); ASSERT(object->scriptExecutionContext() == this); object->contextDestroyed(); } HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end(); for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) { ASSERT((*iter)->scriptExecutionContext() == this); (*iter)->contextDestroyed(); } #if ENABLE(SQL_DATABASE) if (m_databaseThread) { ASSERT(m_databaseThread->terminationRequested()); m_databaseThread = 0; } #endif #if ENABLE(BLOB) || ENABLE(FILE_SYSTEM) if (m_fileThread) { m_fileThread->stop(); m_fileThread = 0; } #endif #if ENABLE(BLOB) HashSet<String>::iterator publicBlobURLsEnd = m_publicBlobURLs.end(); for (HashSet<String>::iterator iter = m_publicBlobURLs.begin(); iter != publicBlobURLsEnd; ++iter) ThreadableBlobRegistry::unregisterBlobURL(KURL(ParsedURLString, *iter)); HashSet<DOMURL*>::iterator domUrlsEnd = m_domUrls.end(); for (HashSet<DOMURL*>::iterator iter = m_domUrls.begin(); iter != domUrlsEnd; ++iter) { ASSERT((*iter)->scriptExecutionContext() == this); (*iter)->contextDestroyed(); } #endif #if ENABLE(MEDIA_STREAM) HashSet<String>::iterator publicStreamURLsEnd = m_publicStreamURLs.end(); for (HashSet<String>::iterator iter = m_publicStreamURLs.begin(); iter != publicStreamURLsEnd; ++iter) MediaStreamRegistry::registry().unregisterMediaStreamURL(KURL(ParsedURLString, *iter)); #endif }
void ContextLifecycleNotifier::notifyStoppingActiveDOMObjects() { TemporaryChange<IterationType> scope(m_iterating, IteratingOverAll); Vector<UntracedMember<ContextLifecycleObserver>> snapshotOfObservers; copyToVector(m_observers, snapshotOfObservers); for (ContextLifecycleObserver* observer : snapshotOfObservers) { // It's possible that the ActiveDOMObject is already destructed. // See a FIXME above. if (m_observers.contains(observer)) { if (observer->observerType() != ContextLifecycleObserver::ActiveDOMObjectType) continue; ActiveDOMObject* activeDOMObject = static_cast<ActiveDOMObject*>(observer); #if DCHECK_IS_ON() DCHECK_EQ(activeDOMObject->getExecutionContext(), context()); DCHECK(activeDOMObject->suspendIfNeededCalled()); #endif activeDOMObject->stop(); } } }
void visitDOMWrapper(DOMDataStore* store, void* object, v8::Persistent<v8::Object> wrapper) { WrapperTypeInfo* typeInfo = V8DOMWrapper::domWrapperType(wrapper); // Additional handling of message port ensuring that entangled ports also // have their wrappers entangled. This should ideally be handled when the // ports are actually entangled in MessagePort::entangle, but to avoid // forking MessagePort.* this is postponed to GC time. Having this postponed // has the drawback that the wrappers are "entangled/unentangled" for each // GC even though their entaglement most likely is still the same. if (V8MessagePort::info.equals(typeInfo)) { // Mark each port as in-use if it's entangled. For simplicity's sake, we assume all ports are remotely entangled, // since the Chromium port implementation can't tell the difference. MessagePort* port1 = static_cast<MessagePort*>(object); if (port1->isEntangled() || port1->hasPendingActivity()) wrapper.ClearWeak(); } else { ActiveDOMObject* activeDOMObject = typeInfo->toActiveDOMObject(wrapper); if (activeDOMObject && activeDOMObject->hasPendingActivity()) wrapper.ClearWeak(); } }
void visitDOMWrapper(DOMDataStore* store, void* object, v8::Persistent<v8::Object> wrapper) { WrapperTypeInfo* typeInfo = V8DOMWrapper::domWrapperType(wrapper); if (V8MessagePort::info.equals(typeInfo)) { MessagePort* port1 = static_cast<MessagePort*>(object); // We marked this port as reachable in GCPrologueVisitor. Undo this now since the // port could be not reachable in the future if it gets disentangled (and also // GCPrologueVisitor expects to see all handles marked as weak). if ((!wrapper.IsWeak() && !wrapper.IsNearDeath()) || port1->hasPendingActivity()) wrapper.MakeWeak(port1, &DOMDataStore::weakActiveDOMObjectCallback); } else { ActiveDOMObject* activeDOMObject = typeInfo->toActiveDOMObject(wrapper); if (activeDOMObject && activeDOMObject->hasPendingActivity()) { ASSERT(!wrapper.IsWeak()); // NOTE: To re-enable weak status of the active object we use // |object| from the map and not |activeDOMObject|. The latter // may be a different pointer (in case ActiveDOMObject is not // the main base class of the object's class) and pointer // identity is required by DOM map functions. wrapper.MakeWeak(object, &DOMDataStore::weakActiveDOMObjectCallback); } } }
virtual void VisitPersistentHandle(v8::Persistent<v8::Value>* value, uint16_t classId) override { if (classId != WrapperTypeInfo::NodeClassId && classId != WrapperTypeInfo::ObjectClassId) return; // Casting to a Handle is safe here, since the Persistent doesn't get GCd // during the GC prologue. ASSERT((*reinterpret_cast<v8::Handle<v8::Value>*>(value))->IsObject()); v8::Handle<v8::Object>* wrapper = reinterpret_cast<v8::Handle<v8::Object>*>(value); ASSERT(V8DOMWrapper::isDOMWrapper(*wrapper)); if (value->IsIndependent()) return; const WrapperTypeInfo* type = toWrapperTypeInfo(*wrapper); ActiveDOMObject* activeDOMObject = type->toActiveDOMObject(*wrapper); if (activeDOMObject && activeDOMObject->hasPendingActivity()) { m_isolate->SetObjectGroupId(*value, liveRootId()); ++m_domObjectsWithPendingActivity; } if (classId == WrapperTypeInfo::NodeClassId) { ASSERT(V8Node::hasInstance(*wrapper, m_isolate)); Node* node = V8Node::toImpl(*wrapper); if (node->hasEventListeners()) addReferencesForNodeWithEventListeners(m_isolate, node, v8::Persistent<v8::Object>::Cast(*value)); Node* root = V8GCController::opaqueRootForGC(m_isolate, node); m_isolate->SetObjectGroupId(*value, v8::UniqueId(reinterpret_cast<intptr_t>(root))); if (m_constructRetainedObjectInfos) m_groupsWhichNeedRetainerInfo.append(root); } else if (classId == WrapperTypeInfo::ObjectClassId) { type->visitDOMWrapper(m_isolate, toScriptWrappableBase(*wrapper), v8::Persistent<v8::Object>::Cast(*value)); } else { ASSERT_NOT_REACHED(); } }
void ContextLifecycleNotifier::notifyResumingActiveDOMObjects() { TemporaryChange<IterationType> scope(m_iterating, IteratingOverAll); Vector<UntracedMember<ContextLifecycleObserver>> snapshotOfObservers; copyToVector(m_observers, snapshotOfObservers); for (ContextLifecycleObserver* observer : snapshotOfObservers) { // FIXME: Oilpan: At the moment, it's possible that a ActiveDOMObject // observer is destructed while iterating. Once we enable Oilpan by default // for all LifecycleObserver<T>s, we can remove the hack by making m_observers // a HeapHashSet<WeakMember<LifecycleObserver<T>>>. // (i.e., we can just iterate m_observers without taking a snapshot). // For more details, see https://codereview.chromium.org/247253002/. if (m_observers.contains(observer)) { if (observer->observerType() != ContextLifecycleObserver::ActiveDOMObjectType) continue; ActiveDOMObject* activeDOMObject = static_cast<ActiveDOMObject*>(observer); #if DCHECK_IS_ON() DCHECK_EQ(activeDOMObject->getExecutionContext(), context()); DCHECK(activeDOMObject->suspendIfNeededCalled()); #endif activeDOMObject->resume(); } } }