void CharacterData::dispatchModifiedEvent(StringImpl* oldData) { if (parentNode()) parentNode()->childrenChanged(); if (document()->hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER)) dispatchEvent(MutationEvent::create(eventNames().DOMCharacterDataModifiedEvent, true, 0, oldData, m_data)); dispatchSubtreeModifiedEvent(); }
bool ContainerNode::removeChild(Node* oldChild, ExceptionCode& ec) { // Check that this node is not "floating". // If it is, it can be deleted as a side effect of sending mutation events. ASSERT(refCount() || parentOrShadowHostNode()); RefPtr<Node> protect(this); ec = 0; // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. if (isReadOnlyNode()) { ec = NO_MODIFICATION_ALLOWED_ERR; return false; } // NOT_FOUND_ERR: Raised if oldChild is not a child of this node. if (!oldChild || oldChild->parentNode() != this) { ec = NOT_FOUND_ERR; return false; } RefPtr<Node> child = oldChild; document().removeFocusedNodeOfSubtree(child.get()); #if ENABLE(FULLSCREEN_API) document().removeFullScreenElementOfSubtree(child.get()); #endif // Events fired when blurring currently focused node might have moved this // child into a different parent. if (child->parentNode() != this) { ec = NOT_FOUND_ERR; return false; } willRemoveChild(child.get()); // Mutation events might have moved this child into a different parent. if (child->parentNode() != this) { ec = NOT_FOUND_ERR; return false; } { WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates; Node* prev = child->previousSibling(); Node* next = child->nextSibling(); removeBetween(prev, next, child.get()); childrenChanged(false, prev, next, -1); ChildNodeRemovalNotifier(this).notify(child.get()); } dispatchSubtreeModifiedEvent(); return child; }
bool ContainerNode::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec, AttachBehavior attachBehavior) { RefPtr<ContainerNode> protect(this); // Check that this node is not "floating". // If it is, it can be deleted as a side effect of sending mutation events. ASSERT(refCount() || parentOrShadowHostNode()); ec = 0; // Make sure adding the new child is ok if (!checkAddChild(this, newChild.get(), ec)) return false; if (newChild == m_lastChild) // nothing to do return newChild; NodeVector targets; collectChildrenAndRemoveFromOldParent(newChild.get(), targets, ec); if (ec) return false; if (targets.isEmpty()) return true; // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events. if (!checkAcceptChildGuaranteedNodeTypes(this, newChild.get(), ec)) return false; InspectorInstrumentation::willInsertDOMNode(document(), this); // Now actually add the child(ren) ChildListMutationScope mutation(this); for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) { Node* child = it->get(); // If the child has a parent again, just stop what we're doing, because // that means someone is doing something with DOM mutation -- can't re-parent // a child that already has a parent. if (child->parentNode()) break; treeScope()->adoptIfNeeded(child); // Append child to the end of the list { NoEventDispatchAssertion assertNoEventDispatch; appendChildToContainer(child, this); } updateTreeAfterInsertion(this, child, attachBehavior); } dispatchSubtreeModifiedEvent(); return true; }
void CharacterData::dispatchModifiedEvent(StringImpl* prevValue) { if (parentNode()) parentNode()->childrenChanged(); if (document()->hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER)) { ExceptionCode ec; dispatchEvent(MutationEvent::create(eventNames().DOMCharacterDataModifiedEvent, true, false, 0, prevValue, m_data, String(), 0), ec); } dispatchSubtreeModifiedEvent(); }
void CharacterData::dispatchModifiedEvent(const String& oldData) { if (OwnPtr<MutationObserverInterestGroup> mutationRecipients = MutationObserverInterestGroup::createForCharacterDataMutation(this)) mutationRecipients->enqueueMutationRecord(MutationRecord::createCharacterData(this, oldData)); if (!isInShadowTree()) { if (parentNode()) parentNode()->childrenChanged(); if (document()->hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER)) dispatchScopedEvent(MutationEvent::create(eventNames().DOMCharacterDataModifiedEvent, true, 0, oldData, m_data)); dispatchSubtreeModifiedEvent(); } InspectorInstrumentation::characterDataModified(document(), this); }
// this differs from other remove functions because it forcibly removes all the children, // regardless of read-only status or event exceptions, e.g. void ContainerNode::removeChildren() { if (!m_firstChild) return; // The container node can be removed from event handlers. RefPtr<ContainerNode> protect(this); // Exclude this node when looking for the focused or full screen Node since // only children will be removed. // FIXME: We should call these inside the loop below. Right now you can focus // a node with mutation events and it'll never get blured. document()->removeFocusedNodeOfSubtree(this, true); document()->removeFullScreenElementOfSubtree(this, true); ChildListMutationScope mutation(this); NodeVector removedChildren; { WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates; while (RefPtr<Node> child = m_firstChild) { // Dispatch synchronous events like mutation and unload events. dispatchChildRemovalEvents(child.get()); ChildFrameDisconnector(child.get()).disconnect(); // FIXME: In theory this can fire focus events when the selection // changes, but there's no obvious way to test it. document()->nodeWillBeRemoved(child.get()); // If an event moved the child start over. if (child != m_firstChild) continue; mutation.willRemoveChild(child.get()); child->notifyMutationObserversNodeWillDetach(); removeBetween(0, child->nextSibling(), child.get()); removedChildren.append(child.release()); } // FIXME: We could avoid walking all the children twice by calling // notify inside the loop and childrenChanged after but that would mean // calling childrenChanged in a different order than all other methods. // Figure out if this is safe. childrenChanged(false, 0, 0, -static_cast<int>(removedChildren.size())); for (size_t i = 0; i < removedChildren.size(); ++i) ChildNodeRemovalNotifier(this).notify(removedChildren[i].get()); } dispatchSubtreeModifiedEvent(); }
// this differs from other remove functions because it forcibly removes all the children, // regardless of read-only status or event exceptions, e.g. void ContainerNode::removeChildren() { if (!m_firstChild) return; // The container node can be removed from event handlers. Ref<ContainerNode> protect(*this); // exclude this node when looking for removed focusedNode since only children will be removed document().removeFocusedNodeOfSubtree(this, true); #if ENABLE(FULLSCREEN_API) document().removeFullScreenElementOfSubtree(this, true); #endif // Do any prep work needed before actually starting to detach // and remove... e.g. stop loading frames, fire unload events. willRemoveChildren(*this); NodeVector removedChildren; { WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates; { NoEventDispatchAssertion assertNoEventDispatch; removedChildren.reserveInitialCapacity(countChildNodes()); while (RefPtr<Node> n = m_firstChild) { removedChildren.append(*m_firstChild); removeBetween(0, m_firstChild->nextSibling(), *m_firstChild); } } ChildChange change = { AllChildrenRemoved, nullptr, nullptr, ChildChangeSourceAPI }; childrenChanged(change); for (size_t i = 0; i < removedChildren.size(); ++i) ChildNodeRemovalNotifier(*this).notify(removedChildren[i].get()); } if (document().svgExtensions()) { Element* shadowHost = this->shadowHost(); if (!shadowHost || !shadowHost->hasTagName(SVGNames::useTag)) document().accessSVGExtensions()->rebuildElements(); } dispatchSubtreeModifiedEvent(); }
void CharacterData::dispatchModifiedEvent(StringImpl* oldData) { #if ENABLE(MUTATION_OBSERVERS) Vector<WebKitMutationObserver*> observers; registeredMutationObserversOfType(observers, WebKitMutationObserver::CharacterData); if (!observers.isEmpty()) { RefPtr<MutationRecord> mutation = MutationRecord::createCharacterData(this); for (size_t i = 0; i < observers.size(); ++i) observers[i]->enqueueMutationRecord(mutation); } #endif if (parentNode()) parentNode()->childrenChanged(); if (document()->hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER)) dispatchEvent(MutationEvent::create(eventNames().DOMCharacterDataModifiedEvent, true, 0, oldData, m_data)); dispatchSubtreeModifiedEvent(); #if ENABLE(INSPECTOR) InspectorInstrumentation::characterDataModified(document(), this); #endif }
void CharacterData::didModifyData(const String& oldData, UpdateSource source) { if (OwnPtrWillBeRawPtr<MutationObserverInterestGroup> mutationRecipients = MutationObserverInterestGroup::createForCharacterDataMutation(*this)) mutationRecipients->enqueueMutationRecord(MutationRecord::createCharacterData(this, oldData)); if (parentNode()) { ContainerNode::ChildrenChange change = {ContainerNode::TextChanged, previousSibling(), nextSibling(), ContainerNode::ChildrenChangeSourceAPI}; parentNode()->childrenChanged(change); } // Skip DOM mutation events if the modification is from parser. // Note that mutation observer events will still fire. // Spec: https://html.spec.whatwg.org/multipage/syntax.html#insert-a-character if (source != UpdateFromParser && !isInShadowTree()) { if (document().hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER)) dispatchScopedEvent(MutationEvent::create(EventTypeNames::DOMCharacterDataModified, true, nullptr, oldData, m_data)); dispatchSubtreeModifiedEvent(); } InspectorInstrumentation::characterDataModified(this); }
void CharacterData::dispatchModifiedEvent(const String& oldData) { if (std::unique_ptr<MutationObserverInterestGroup> mutationRecipients = MutationObserverInterestGroup::createForCharacterDataMutation(*this)) mutationRecipients->enqueueMutationRecord(MutationRecord::createCharacterData(*this, oldData)); if (!isInShadowTree()) { if (parentNode()) { ContainerNode::ChildChange change = { ContainerNode::TextChanged, ElementTraversal::previousSibling(*this), ElementTraversal::nextSibling(*this), ContainerNode::ChildChangeSourceAPI }; parentNode()->childrenChanged(change); } if (document().hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER)) dispatchScopedEvent(MutationEvent::create(eventNames().DOMCharacterDataModifiedEvent, true, nullptr, oldData, m_data)); dispatchSubtreeModifiedEvent(); } InspectorInstrumentation::characterDataModified(document(), *this); }
// this differs from other remove functions because it forcibly removes all the children, // regardless of read-only status or event exceptions, e.g. void ContainerNode::removeChildren() { if (!m_firstChild) return; // The container node can be removed from event handlers. RefPtr<ContainerNode> protect(this); // exclude this node when looking for removed focusedNode since only children will be removed document().removeFocusedNodeOfSubtree(this, true); #if ENABLE(FULLSCREEN_API) document().removeFullScreenElementOfSubtree(this, true); #endif // Do any prep work needed before actually starting to detach // and remove... e.g. stop loading frames, fire unload events. willRemoveChildren(protect.get()); NodeVector removedChildren; { WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates; { NoEventDispatchAssertion assertNoEventDispatch; removedChildren.reserveInitialCapacity(childNodeCount()); while (RefPtr<Node> n = m_firstChild) { removedChildren.append(m_firstChild); removeBetween(0, m_firstChild->nextSibling(), m_firstChild); } } childrenChanged(false, 0, 0, -static_cast<int>(removedChildren.size())); for (size_t i = 0; i < removedChildren.size(); ++i) ChildNodeRemovalNotifier(this).notify(removedChildren[i].get()); } dispatchSubtreeModifiedEvent(); }
bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode& ec, AttachBehavior attachBehavior) { // Check that this node is not "floating". // If it is, it can be deleted as a side effect of sending mutation events. ASSERT(refCount() || parentOrShadowHostNode()); RefPtr<Node> protect(this); ec = 0; if (oldChild == newChild) // nothing to do return true; if (!oldChild) { ec = NOT_FOUND_ERR; return false; } // Make sure replacing the old child with the new is ok if (!checkReplaceChild(this, newChild.get(), oldChild, ec)) return false; // NOT_FOUND_ERR: Raised if oldChild is not a child of this node. if (oldChild->parentNode() != this) { ec = NOT_FOUND_ERR; return false; } ChildListMutationScope mutation(this); RefPtr<Node> next = oldChild->nextSibling(); // Remove the node we're replacing RefPtr<Node> removedChild = oldChild; removeChild(oldChild, ec); if (ec) return false; if (next && (next->previousSibling() == newChild || next == newChild)) // nothing to do return true; // Does this one more time because removeChild() fires a MutationEvent. if (!checkReplaceChild(this, newChild.get(), oldChild, ec)) return false; NodeVector targets; collectChildrenAndRemoveFromOldParent(newChild.get(), targets, ec); if (ec) return false; // Does this yet another check because collectChildrenAndRemoveFromOldParent() fires a MutationEvent. if (!checkReplaceChild(this, newChild.get(), oldChild, ec)) return false; InspectorInstrumentation::willInsertDOMNode(document(), this); // Add the new child(ren) for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) { Node* child = it->get(); // Due to arbitrary code running in response to a DOM mutation event it's // possible that "next" is no longer a child of "this". // It's also possible that "child" has been inserted elsewhere. // In either of those cases, we'll just stop. if (next && next->parentNode() != this) break; if (child->parentNode()) break; treeScope()->adoptIfNeeded(child); // Add child before "next". { NoEventDispatchAssertion assertNoEventDispatch; if (next) insertBeforeCommon(next.get(), child); else appendChildToContainer(child, this); } updateTreeAfterInsertion(this, child, attachBehavior); } dispatchSubtreeModifiedEvent(); return true; }
bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode& ec, AttachBehavior attachBehavior) { // Check that this node is not "floating". // If it is, it can be deleted as a side effect of sending mutation events. ASSERT(refCount() || parentOrShadowHostNode()); RefPtr<Node> protect(this); ec = 0; // insertBefore(node, 0) is equivalent to appendChild(node) if (!refChild) return appendChild(newChild, ec, attachBehavior); // Make sure adding the new child is OK. if (!checkAddChild(this, newChild.get(), ec)) return false; // NOT_FOUND_ERR: Raised if refChild is not a child of this node if (refChild->parentNode() != this) { ec = NOT_FOUND_ERR; return false; } if (refChild->previousSibling() == newChild || refChild == newChild) // nothing to do return true; RefPtr<Node> next = refChild; NodeVector targets; collectChildrenAndRemoveFromOldParent(newChild.get(), targets, ec); if (ec) return false; if (targets.isEmpty()) return true; // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events. if (!checkAcceptChildGuaranteedNodeTypes(this, newChild.get(), ec)) return false; InspectorInstrumentation::willInsertDOMNode(document(), this); ChildListMutationScope mutation(this); for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) { Node* child = it->get(); // Due to arbitrary code running in response to a DOM mutation event it's // possible that "next" is no longer a child of "this". // It's also possible that "child" has been inserted elsewhere. // In either of those cases, we'll just stop. if (next->parentNode() != this) break; if (child->parentNode()) break; treeScope()->adoptIfNeeded(child); insertBeforeCommon(next.get(), child); updateTreeAfterInsertion(this, child, attachBehavior); } dispatchSubtreeModifiedEvent(); return true; }
// this differs from other remove functions because it forcibly removes all the children, // regardless of read-only status or event exceptions, e.g. void ContainerNode::removeChildren() { if (!m_firstChild) return; // The container node can be removed from event handlers. RefPtr<ContainerNode> protect(this); // exclude this node when looking for removed focusedNode since only children will be removed document()->removeFocusedNodeOfSubtree(this, true); #if ENABLE(FULLSCREEN_API) document()->removeFullScreenElementOfSubtree(this, true); #endif // Do any prep work needed before actually starting to detach // and remove... e.g. stop loading frames, fire unload events. willRemoveChildren(protect.get()); Vector<RefPtr<Node>, 10> removedChildren; { WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates; NoEventDispatchAssertion assertNoEventDispatch; removedChildren.reserveInitialCapacity(childNodeCount()); while (RefPtr<Node> n = m_firstChild) { Node* next = n->nextSibling(); // Remove the node from the tree before calling detach or removedFromDocument (4427024, 4129744). // removeChild() does this after calling detach(). There is no explanation for // this discrepancy between removeChild() and its optimized version removeChildren(). n->setPreviousSibling(0); n->setNextSibling(0); n->setParentOrHostNode(0); document()->adoptIfNeeded(n.get()); m_firstChild = next; if (n == m_lastChild) m_lastChild = 0; removedChildren.append(n.release()); } size_t removedChildrenCount = removedChildren.size(); size_t i; // Detach the nodes only after properly removed from the tree because // a. detaching requires a proper DOM tree (for counters and quotes for // example) and during the previous loop the next sibling still points to // the node being removed while the node being removed does not point back // and does not point to the same parent as its next sibling. // b. destroying Renderers of standalone nodes is sometimes faster. for (i = 0; i < removedChildrenCount; ++i) { Node* removedChild = removedChildren[i].get(); if (removedChild->attached()) removedChild->detach(); } childrenChanged(false, 0, 0, -static_cast<int>(removedChildrenCount)); for (i = 0; i < removedChildrenCount; ++i) ChildNodeRemovalNotifier(this).notify(removedChildren[i].get()); } dispatchSubtreeModifiedEvent(); }
bool ContainerNode::removeChild(Node* oldChild, ExceptionCode& ec) { // Check that this node is not "floating". // If it is, it can be deleted as a side effect of sending mutation events. ASSERT(refCount() || parentOrShadowHostNode()); Ref<ContainerNode> protect(*this); ec = 0; // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. if (isReadOnlyNode()) { ec = NO_MODIFICATION_ALLOWED_ERR; return false; } // NOT_FOUND_ERR: Raised if oldChild is not a child of this node. if (!oldChild || oldChild->parentNode() != this) { ec = NOT_FOUND_ERR; return false; } Ref<Node> child(*oldChild); document().removeFocusedNodeOfSubtree(&child.get()); #if ENABLE(FULLSCREEN_API) document().removeFullScreenElementOfSubtree(&child.get()); #endif // Events fired when blurring currently focused node might have moved this // child into a different parent. if (child->parentNode() != this) { ec = NOT_FOUND_ERR; return false; } willRemoveChild(child.get()); // Mutation events might have moved this child into a different parent. if (child->parentNode() != this) { ec = NOT_FOUND_ERR; return false; } { WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates; Node* prev = child->previousSibling(); Node* next = child->nextSibling(); removeBetween(prev, next, child.get()); notifyChildRemoved(child.get(), prev, next, ChildChangeSourceAPI); ChildNodeRemovalNotifier(*this).notify(child.get()); } if (document().svgExtensions()) { Element* shadowHost = this->shadowHost(); if (!shadowHost || !shadowHost->hasTagName(SVGNames::useTag)) document().accessSVGExtensions()->rebuildElements(); } dispatchSubtreeModifiedEvent(); return true; }